[
  {
    "path": ".doctrine-project.json",
    "content": "{\n    \"active\": true,\n    \"name\": \"Object Relational Mapper\",\n    \"shortName\": \"ORM\",\n    \"slug\": \"orm\",\n    \"docsSlug\": \"doctrine-orm\",\n    \"versions\": [\n        {\n            \"name\": \"4.0\",\n            \"branchName\": \"4.0.x\",\n            \"slug\": \"latest\",\n            \"upcoming\": true\n        },\n        {\n            \"name\": \"3.7\",\n            \"branchName\": \"3.7.x\",\n            \"slug\": \"3.7\",\n            \"upcoming\": true\n        },\n        {\n            \"name\": \"3.6\",\n            \"branchName\": \"3.6.x\",\n            \"slug\": \"3.6\",\n            \"current\": true\n        },\n        {\n            \"name\": \"2.21\",\n            \"branchName\": \"2.21.x\",\n            \"slug\": \"2.21\",\n            \"upcoming\": true\n        },\n        {\n            \"name\": \"2.20\",\n            \"branchName\": \"2.20.x\",\n            \"slug\": \"2.20\",\n            \"maintained\": true\n        },\n        {\n            \"name\": \"2.19\",\n            \"slug\": \"2.19\",\n            \"maintained\": false\n        },\n        {\n            \"name\": \"2.18\",\n            \"slug\": \"2.18\",\n            \"maintained\": false\n        },\n        {\n            \"name\": \"2.17\",\n            \"slug\": \"2.17\",\n            \"maintained\": false\n        },\n        {\n            \"name\": \"2.16\",\n            \"slug\": \"2.16\",\n            \"maintained\": false\n        },\n        {\n            \"name\": \"2.15\",\n            \"slug\": \"2.15\",\n            \"maintained\": false\n        },\n        {\n            \"name\": \"2.14\",\n            \"slug\": \"2.14\",\n            \"maintained\": false\n        }\n    ]\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "/.github export-ignore\n/ci export-ignore\n/docs export-ignore\n/tests export-ignore\n/tools export-ignore\n.doctrine-project.json export-ignore\n.gitattributes export-ignore\n.gitignore export-ignore\nbuild.properties export-ignore\nbuild.properties.dev export-ignore\nbuild.xml export-ignore\nCONTRIBUTING.md export-ignore\nphpunit.xml.dist export-ignore\nphpcs.xml.dist export-ignore\nphpbench.json export-ignore\nphpstan.neon export-ignore\nphpstan-baseline.neon export-ignore\nphpstan-dbal3.neon export-ignore\nphpstan-params.neon export-ignore\nphpstan-persistence2.neon export-ignore\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/Failing_Test.md",
    "content": "---\nname: 🐞 Failing Test\nabout: You found a bug and have a failing Unit or Functional test? 🔨\n---\n\n### Failing Test\n\n<!-- Fill in the relevant information below to help triage your issue. -->\n\n|    Q        |   A\n|------------ | ------\n| BC Break    | yes/no\n| Version     | x.y.z\n\n\n#### Summary\n\n<!-- Provide a summary of the failing scenario. -->\n\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/Improvement.md",
    "content": "---\nname: ⚙ Improvement\nabout: You have some improvement to make Doctrine better? 🎁\n---\n\n### Improvement\n\n<!-- Fill in the relevant information below to help triage your issue. -->\n\n|    Q        |   A\n|------------ | ------\n| New Feature | yes\n| RFC         | yes/no\n| BC Break    | yes/no\n\n#### Summary\n\n<!-- Provide a summary of the improvement you are submitting. -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/New_Feature.md",
    "content": "---\nname: 🎉 New Feature\nabout: You have implemented some neat idea that you want to make part of Doctrine? 🎩\n---\n\n<!--\nThank you for submitting new feature!\nPick the target branch based according to these criteria:\n  * submitting a bugfix: target the lowest active stable branch: 2.9.x\n  * submitting a new feature: target the next minor branch: 2.10.x\n  * submitting a BC-breaking change: target the next major branch: 3.0.x\n-->\n\n### New Feature\n\n<!-- Fill in the relevant information below to help triage your issue. -->\n\n|    Q        |   A\n|------------ | ------\n| New Feature | yes\n| RFC         | yes/no\n| BC Break    | yes/no\n\n#### Summary\n\n<!-- Provide a summary of the feature you have implemented. -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    labels:\n      - \"CI\"\n    target-branch: \"2.20.x\"\n"
  },
  {
    "path": ".github/workflows/coding-standards.yml",
    "content": "name: \"Coding Standards\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/coding-standards.yml\n      - bin/**\n      - composer.*\n      - src/**\n      - phpcs.xml.dist\n      - tests/**\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/coding-standards.yml\n      - bin/**\n      - composer.*\n      - src/**\n      - phpcs.xml.dist\n      - tests/**\n\njobs:\n  coding-standards:\n    uses: \"doctrine/.github/.github/workflows/coding-standards.yml@13.1.0\"\n"
  },
  {
    "path": ".github/workflows/composer-lint.yml",
    "content": "name: \"Composer Lint\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".github/workflows/composer-lint.yml\"\n      - \"composer.json\"\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".github/workflows/composer-lint.yml\"\n      - \"composer.json\"\n\njobs:\n  composer-lint:\n    name: \"Composer Lint\"\n    uses: \"doctrine/.github/.github/workflows/composer-lint.yml@13.1.0\"\n"
  },
  {
    "path": ".github/workflows/continuous-integration.yml",
    "content": "name: \"CI: PHPUnit\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/continuous-integration.yml\n      - ci/**\n      - composer.*\n      - src/**\n      - tests/**\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/continuous-integration.yml\n      - ci/**\n      - composer.*\n      - src/**\n      - tests/**\n\nenv:\n  fail-fast: true\n\njobs:\n  phpunit-smoke-check:\n    name: >\n      SQLite -\n      ${{ format('PHP {0} - DBAL {1} - ext. {2} - proxy {3}',\n        matrix.php-version || 'Ø',\n        matrix.dbal-version || 'Ø',\n        matrix.extension || 'Ø',\n        matrix.proxy || 'Ø'\n      ) }}\n    runs-on: \"ubuntu-22.04\"\n\n    strategy:\n      matrix:\n        php-version:\n          - \"8.1\"\n          - \"8.2\"\n          - \"8.3\"\n          - \"8.4\"\n          - \"8.5\"\n        dbal-version:\n          - \"default\"\n          - \"3.7\"\n        extension:\n          - \"sqlite3\"\n          - \"pdo_sqlite\"\n        deps:\n          - \"highest\"\n        stability:\n          - \"stable\"\n        native_lazy:\n          - \"0\"\n        include:\n          - php-version: \"8.2\"\n            dbal-version: \"4@dev\"\n            extension: \"pdo_sqlite\"\n            stability: \"stable\"\n            native_lazy: \"0\"\n          - php-version: \"8.2\"\n            dbal-version: \"4@dev\"\n            extension: \"sqlite3\"\n            stability: \"stable\"\n            native_lazy: \"0\"\n          - php-version: \"8.1\"\n            dbal-version: \"default\"\n            deps: \"lowest\"\n            extension: \"pdo_sqlite\"\n            stability: \"stable\"\n            native_lazy: \"0\"\n          - php-version: \"8.4\"\n            dbal-version: \"default\"\n            deps: \"highest\"\n            extension: \"pdo_sqlite\"\n            stability: \"stable\"\n            native_lazy: \"1\"\n          - php-version: \"8.4\"\n            dbal-version: \"default\"\n            deps: \"highest\"\n            extension: \"sqlite3\"\n            stability: \"dev\"\n            native_lazy: \"1\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Install PHP\"\n        uses: \"shivammathur/setup-php@v2\"\n        with:\n          php-version: \"${{ matrix.php-version }}\"\n          extensions: \"apcu, pdo, ${{ matrix.extension }}\"\n          coverage: \"pcov\"\n          ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n\n      - name: \"Allow dev dependencies\"\n        run: |\n          composer config minimum-stability dev\n          composer remove --no-update --dev phpbench/phpbench phpdocumentor/guides-cli\n          composer require --no-update symfony/console:^8 symfony/var-exporter:^8 doctrine/dbal:^4.4\n          composer require --dev --no-update symfony/cache:^8\n        if: \"${{ matrix.stability == 'dev' }}\"\n\n      - name: \"Require specific DBAL version\"\n        run: \"composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update\"\n        if: \"${{ matrix.dbal-version != 'default' }}\"\n\n      - name: \"Downgrade VarExporter\"\n        run: 'composer require --no-update \"symfony/var-exporter:^6.4 || ^7.4\"'\n        if: \"${{ matrix.native_lazy == '0' }}\"\n\n      - name: \"Install dependencies with Composer\"\n        uses: \"ramsey/composer-install@v3\"\n        with:\n          composer-options: \"--ignore-platform-req=php+\"\n          dependency-versions: \"${{ matrix.deps }}\"\n\n      - name: \"Run PHPUnit\"\n        run: \"vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 0\n            ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}\n\n      - name: \"Run PHPUnit with Second Level Cache and PHPUnit 10\"\n        run: |\n          vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \\\n            --exclude-group=performance,non-cacheable,locking_functional \\\n            --coverage-clover=coverage-cache.xml\n        if: \"${{ matrix.php-version == '8.1' }}\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 1\n            ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}\n\n      - name: \"Run PHPUnit with Second Level Cache and PHPUnit 11+\"\n        run: |\n          vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \\\n            --exclude-group=performance \\\n            --exclude-group=non-cacheable \\\n            --exclude-group=locking_functional \\\n            --coverage-clover=coverage-cache.xml\n        if: \"${{ matrix.php-version != '8.1' }}\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 1\n            ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}\n\n      - name: \"Upload coverage file\"\n        uses: \"actions/upload-artifact@v7\"\n        with:\n          name: \"phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.stability }}-${{ matrix.native_lazy }}-coverage\"\n          path: \"coverage*.xml\"\n\n\n  phpunit-deprecations:\n    name: \"PHPUnit (fail on deprecations)\"\n    runs-on: \"ubuntu-24.04\"\n\n    steps:\n      -   name: \"Checkout\"\n          uses: \"actions/checkout@v5\"\n          with:\n            fetch-depth: 2\n\n      -   name: \"Install PHP\"\n          uses: \"shivammathur/setup-php@v2\"\n          with:\n            php-version: \"8.5\"\n            extensions: \"apcu, pdo, sqlite3\"\n            coverage: \"pcov\"\n            ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n\n      -   name: \"Allow dev dependencies\"\n          run: composer config minimum-stability dev\n\n      -   name: \"Install dependencies with Composer\"\n          uses: \"ramsey/composer-install@v3\"\n          with:\n            composer-options: \"--ignore-platform-req=php+\"\n            dependency-versions: \"highest\"\n\n      -   name: \"Run PHPUnit\"\n          run: \"vendor/bin/phpunit -c ci/github/phpunit/sqlite3.xml --fail-on-deprecation\"\n          env:\n            ENABLE_SECOND_LEVEL_CACHE: 0\n            ENABLE_NATIVE_LAZY_OBJECTS: 1\n\n\n  phpunit-postgres:\n    name: >\n      ${{ format('PostgreSQL {0} - PHP {1} - DBAL {2} - ext. {3}',\n        matrix.postgres-version || 'Ø',\n        matrix.php-version || 'Ø',\n        matrix.dbal-version || 'Ø',\n        matrix.extension || 'Ø'\n      ) }}\n    runs-on: \"ubuntu-22.04\"\n    needs: \"phpunit-smoke-check\"\n\n    strategy:\n      matrix:\n        php-version:\n          - \"8.2\"\n          - \"8.3\"\n          - \"8.4\"\n          - \"8.5\"\n        dbal-version:\n          - \"default\"\n          - \"3.7\"\n        postgres-version:\n          - \"17\"\n        extension:\n          - pdo_pgsql\n          - pgsql\n        include:\n          - php-version: \"8.2\"\n            dbal-version: \"4@dev\"\n            postgres-version: \"14\"\n            extension: pdo_pgsql\n          - php-version: \"8.2\"\n            dbal-version: \"3.7\"\n            postgres-version: \"9.6\"\n            extension: pdo_pgsql\n\n    services:\n      postgres:\n        image: \"postgres:${{ matrix.postgres-version }}\"\n        env:\n          POSTGRES_PASSWORD: \"postgres\"\n\n        options: >-\n          --health-cmd \"pg_isready\"\n\n        ports:\n          - \"5432:5432\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Install PHP\"\n        uses: \"shivammathur/setup-php@v2\"\n        with:\n          php-version: \"${{ matrix.php-version }}\"\n          extensions: \"pgsql pdo_pgsql\"\n          coverage: \"pcov\"\n          ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n\n      - name: \"Require specific DBAL version\"\n        run: \"composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update\"\n        if: \"${{ matrix.dbal-version != 'default' }}\"\n\n      - name: \"Install dependencies with Composer\"\n        uses: \"ramsey/composer-install@v3\"\n        with:\n          composer-options: \"--ignore-platform-req=php+\"\n\n      - name: \"Run PHPUnit\"\n        run: \"vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml\"\n\n      - name: \"Upload coverage file\"\n        uses: \"actions/upload-artifact@v7\"\n        with:\n          name: \"${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage\"\n          path: \"coverage.xml\"\n\n\n  phpunit-mariadb:\n    name: >\n      ${{ format('MariaDB {0} - PHP {1} - DBAL {2} - ext. {3}',\n        matrix.mariadb-version || 'Ø',\n        matrix.php-version || 'Ø',\n        matrix.dbal-version || 'Ø',\n        matrix.extension || 'Ø'\n      ) }}\n    runs-on: \"ubuntu-22.04\"\n    needs: \"phpunit-smoke-check\"\n\n    strategy:\n      matrix:\n        php-version:\n          - \"8.2\"\n          - \"8.3\"\n          - \"8.4\"\n          - \"8.5\"\n        dbal-version:\n          - \"default\"\n          - \"3.7\"\n          - \"4@dev\"\n        mariadb-version:\n          - \"11.4\"\n        extension:\n          - \"mysqli\"\n          - \"pdo_mysql\"\n\n    services:\n      mariadb:\n        image: \"mariadb:${{ matrix.mariadb-version }}\"\n        env:\n          MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes\n          MARIADB_DATABASE: \"doctrine_tests\"\n\n        options: >-\n          --health-cmd \"healthcheck.sh --connect --innodb_initialized\"\n\n        ports:\n          - \"3306:3306\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Require specific DBAL version\"\n        run: \"composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update\"\n        if: \"${{ matrix.dbal-version != 'default' }}\"\n\n      - name: \"Install PHP\"\n        uses: \"shivammathur/setup-php@v2\"\n        with:\n          php-version: \"${{ matrix.php-version }}\"\n          coverage: \"pcov\"\n          ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n          extensions: \"${{ matrix.extension }}\"\n\n      - name: \"Install dependencies with Composer\"\n        uses: \"ramsey/composer-install@v3\"\n        with:\n          composer-options: \"--ignore-platform-req=php+\"\n\n      - name: \"Run PHPUnit\"\n        run: \"vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml\"\n\n      - name: \"Upload coverage file\"\n        uses: \"actions/upload-artifact@v7\"\n        with:\n          name: \"${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage\"\n          path: \"coverage.xml\"\n\n\n  phpunit-mysql:\n    name: >\n      ${{ format('MySQL {0} - PHP {1} - DBAL {2} - ext. {3}',\n        matrix.mysql-version || 'Ø',\n        matrix.php-version || 'Ø',\n        matrix.dbal-version || 'Ø',\n        matrix.extension || 'Ø'\n      ) }}\n    runs-on: \"ubuntu-22.04\"\n    needs: \"phpunit-smoke-check\"\n\n    strategy:\n      matrix:\n        php-version:\n          - \"8.2\"\n          - \"8.3\"\n          - \"8.4\"\n          - \"8.5\"\n        dbal-version:\n          - \"default\"\n          - \"3.7\"\n        mysql-version:\n          - \"5.7\"\n          - \"8.0\"\n        extension:\n          - \"mysqli\"\n          - \"pdo_mysql\"\n        include:\n          - php-version: \"8.2\"\n            dbal-version: \"4@dev\"\n            mysql-version: \"8.0\"\n            extension: \"mysqli\"\n          - php-version: \"8.2\"\n            dbal-version: \"4@dev\"\n            mysql-version: \"8.0\"\n            extension: \"pdo_mysql\"\n\n    services:\n      mysql:\n        image: \"mysql:${{ matrix.mysql-version }}\"\n\n        options: >-\n          --health-cmd \"mysqladmin ping --silent\"\n          -e MYSQL_ALLOW_EMPTY_PASSWORD=yes\n          -e MYSQL_DATABASE=doctrine_tests\n\n        ports:\n          - \"3306:3306\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Install PHP\"\n        uses: \"shivammathur/setup-php@v2\"\n        with:\n          php-version: \"${{ matrix.php-version }}\"\n          coverage: \"pcov\"\n          ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n          extensions: \"${{ matrix.extension }}\"\n\n      - name: \"Require specific DBAL version\"\n        run: \"composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update\"\n        if: \"${{ matrix.dbal-version != 'default' }}\"\n\n      - name: \"Install dependencies with Composer\"\n        uses: \"ramsey/composer-install@v3\"\n        with:\n          composer-options: \"--ignore-platform-req=php+\"\n\n      - name: \"Run PHPUnit\"\n        run: \"vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 0\n\n      - name: \"Run PHPUnit with Second Level Cache and PHPUnit 10\"\n        run: |\n          vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \\\n            --exclude-group=performance,non-cacheable,locking_functional \\\n            --coverage-clover=coverage-no-cache.xml\"\n        if: \"${{ matrix.php-version == '8.1' }}\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 1\n      - name: \"Run PHPUnit with Second Level Cache and PHPUnit 11+\"\n        run: |\n          vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \\\n            --exclude-group=performance \\\n            --exclude-group=non-cacheable \\\n            --exclude-group=locking_functional \\\n            --coverage-clover=coverage-no-cache.xml\n        if: \"${{ matrix.php-version != '8.1' }}\"\n        env:\n            ENABLE_SECOND_LEVEL_CACHE: 1\n\n      - name: \"Upload coverage files\"\n        uses: \"actions/upload-artifact@v7\"\n        with:\n          name: \"${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage\"\n          path: \"coverage*.xml\"\n\n  upload_coverage:\n    name: \"Upload coverage to Codecov\"\n    runs-on: \"ubuntu-22.04\"\n    # Only run on PRs from forks\n    if: \"github.event.pull_request.head.repo.full_name != github.repository\"\n    needs:\n      - \"phpunit-smoke-check\"\n      - \"phpunit-postgres\"\n      - \"phpunit-mariadb\"\n      - \"phpunit-mysql\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Download coverage files\"\n        uses: \"actions/download-artifact@v8\"\n        with:\n          path: \"reports\"\n\n      - name: \"Upload to Codecov\"\n        uses: \"codecov/codecov-action@v5\"\n        with:\n          directory: reports\n        env:\n          CODECOV_TOKEN: \"${{ secrets.CODECOV_TOKEN }}\"\n"
  },
  {
    "path": ".github/workflows/documentation.yml",
    "content": "name: \"Documentation\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".github/workflows/documentation.yml\"\n      - \"docs/**\"\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".github/workflows/documentation.yml\"\n      - \"docs/**\"\n\njobs:\n  documentation:\n    name: \"Documentation\"\n    uses: \"doctrine/.github/.github/workflows/documentation.yml@13.1.0\"\n"
  },
  {
    "path": ".github/workflows/phpbench.yml",
    "content": "\nname: \"Performance benchmark\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/phpbench.yml\n      - composer.*\n      - src/**\n      - phpbench.json\n      - tests/**\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/phpbench.yml\n      - composer.*\n      - src/**\n      - phpbench.json\n      - tests/**\n\nenv:\n  fail-fast: true\n\njobs:\n  phpbench:\n    name: \"PHPBench\"\n    runs-on: \"ubuntu-22.04\"\n\n    strategy:\n      matrix:\n        php-version:\n          - \"8.1\"\n\n    steps:\n      - name: \"Checkout\"\n        uses: \"actions/checkout@v6\"\n        with:\n          fetch-depth: 2\n\n      - name: \"Install PHP\"\n        uses: \"shivammathur/setup-php@v2\"\n        with:\n          php-version: \"${{ matrix.php-version }}\"\n          coverage: \"pcov\"\n          ini-values: \"zend.assertions=1, apc.enable_cli=1\"\n\n      - name: \"Install dependencies with Composer\"\n        uses: \"ramsey/composer-install@v3\"\n\n      - name: \"Run PHPBench\"\n        run: \"vendor/bin/phpbench run --report=default\"\n"
  },
  {
    "path": ".github/workflows/release-on-milestone-closed.yml",
    "content": "name: \"Automatic Releases\"\n\non:\n  milestone:\n    types:\n      - \"closed\"\n\njobs:\n  release:\n    uses: \"doctrine/.github/.github/workflows/release-on-milestone-closed.yml@13.1.0\"\n    secrets:\n      GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}\n      GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}\n      ORGANIZATION_ADMIN_TOKEN: ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}\n      SIGNING_SECRET_KEY: ${{ secrets.SIGNING_SECRET_KEY }}\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale pull requests'\non:\n  schedule:\n    - cron: '0 3 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v9\n        with:\n          stale-pr-message: >\n            There hasn't been any activity on this pull request in the past 90 days, so\n            it has been marked as stale and it will be closed automatically if no\n            further activity occurs in the next 7 days.\n\n            If you want to continue working on it, please leave a comment.\n\n          close-pr-message: >\n            This pull request was closed due to inactivity.\n\n          days-before-stale:    -1\n          days-before-pr-stale: 90\n          days-before-pr-close: 7\n"
  },
  {
    "path": ".github/workflows/static-analysis.yml",
    "content": "name: \"Static Analysis\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/static-analysis.yml\n      - composer.*\n      - src/**\n      - phpstan*\n      - tests/StaticAnalysis/**\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - .github/workflows/static-analysis.yml\n      - composer.*\n      - src/**\n      - phpstan*\n      - tests/StaticAnalysis/**\n\njobs:\n  static-analysis-phpstan:\n    name: Static Analysis with PHPStan\n    runs-on: ubuntu-22.04\n\n    strategy:\n      matrix:\n        include:\n        - dbal-version: default\n          config: phpstan.neon\n        - dbal-version: 3.8.2\n          config: phpstan-dbal3.neon\n\n    steps:\n      - name: \"Checkout code\"\n        uses: \"actions/checkout@v6\"\n\n      - name: Install PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          coverage: none\n          php-version: \"8.4\"\n          tools: cs2pr\n\n      - name: Require specific DBAL version\n        run: \"composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update\"\n        if: \"${{ matrix.dbal-version != 'default' }}\"\n\n\n      - name: Install dependencies with Composer\n        uses: ramsey/composer-install@v2\n\n      - name: Run static analysis with phpstan/phpstan\n        run: \"vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr\"\n"
  },
  {
    "path": ".github/workflows/website-schema.yml",
    "content": "\nname: \"Website config validation\"\n\non:\n  pull_request:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".doctrine-project.json\"\n      - \".github/workflows/website-schema.yml\"\n  push:\n    branches:\n      - \"*.x\"\n    paths:\n      - \".doctrine-project.json\"\n      - \".github/workflows/website-schema.yml\"\n\njobs:\n  json-validate:\n    name: \"Validate JSON schema\"\n    uses: \"doctrine/.github/.github/workflows/website-schema.yml@7.1.0\"\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nlogs/\nreports/\ndist/\ndownload/\n/.settings/\n.buildpath\n.project\n.idea\n*.iml\nvendor/\n/tests/Doctrine/Performance/history.db\n/.phpcs-cache\ncomposer.lock\n.phpunit.cache\n.phpunit.result.cache\n/*.phpunit.xml\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribute to Doctrine\n\nThank you for contributing to Doctrine!\n\nBefore we can merge your Pull-Request here are some guidelines that you need to follow.\nThese guidelines exist not to annoy you, but to keep the code base clean,\nunified and future proof.\n\nDoctrine has [general contributing guidelines][contributor workflow], make\nsure you follow them.\n\n[contributor workflow]: https://www.doctrine-project.org/contribute/index.html\n\n## Coding Standard\n\nThis project follows [`doctrine/coding-standard`][coding standard homepage].\nYou may fix many some of the issues with `vendor/bin/phpcbf`.\n\n[coding standard homepage]: https://github.com/doctrine/coding-standard\n\n## Unit-Tests\n\nPlease try to add a test for your pull-request.\n\n* If you want to fix a bug or provide a reproduce case, create a test file in\n  ``tests/Tests/ORM/Functional/Ticket`` with the name of the ticket,\n  ``DDC1234Test.php`` for example.\n* If you want to contribute new functionality add unit- or functional tests\n  depending on the scope of the feature.\n\nYou can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project.\nIt will run all the tests with an in memory SQLite database.\n\nIn order to do that, you will need a fresh copy of the ORM, and you\nwill have to run a composer installation in the project:\n\n```sh\ngit clone git@github.com:doctrine/orm.git\ncd orm\ncomposer install\n```\n\nYou will also need to enable the PHP extension that provides the SQLite driver\nfor PDO: `pdo_sqlite`. How to do so depends on your system, but checking that it\nis enabled can universally be done with `php -m`: that command should list the\nextension.\n\nTo run the testsuite against another database, copy the ``phpunit.xml.dist``\nto for example ``mysql.phpunit.xml`` and edit the parameters. You can\ntake a look at the ``ci/github/phpunit`` directory for some examples. Then run:\n\n    vendor/bin/phpunit -c mysql.phpunit.xml\n\nIf you do not provide these parameters, the test suite will use an in-memory\nsqlite database.\n\nTips for creating unit tests:\n\n1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.\n   See `https://github.com/doctrine/orm/tree/3.0.x/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an\n   example.\n\n## Getting merged\n\nPlease allow us time to review your pull requests. We will give our best to review\neverything as fast as possible, but cannot always live up to our own expectations.\n\nThank you very much again for your contribution!\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) Doctrine Project\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "|                      [4.0.x][4.0]                      |                      [3.7.x][3.7]                      |                      [3.6.x][3.6]                      |                      [2.21.x][2.21]                      |                      [2.20.x][2.20]                      |\n|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|\n|       [![Build status][4.0 image]][4.0 workflow]       |       [![Build status][3.7 image]][3.7 workflow]       |       [![Build status][3.6 image]][3.6 workflow]       |       [![Build status][2.21 image]][2.21 workflow]       |       [![Build status][2.20 image]][2.20 workflow]       |\n| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.7 coverage image]][3.7 coverage] | [![Coverage Status][3.6 coverage image]][3.6 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |\n\nDoctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence\nfor PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features\nis the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),\ninspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility\nwithout requiring unnecessary code duplication.\n\n\n## More resources:\n\n* [Website](http://www.doctrine-project.org)\n* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/index.html)\n\n\n  [4.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0.x\n  [4.0]: https://github.com/doctrine/orm/tree/4.0.x\n  [4.0 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A4.0.x\n  [4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg\n  [4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x\n  [3.7 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.7.x\n  [3.7]: https://github.com/doctrine/orm/tree/3.7.x\n  [3.7 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.7.x\n  [3.7 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.7.x/graph/badge.svg\n  [3.7 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.7.x\n  [3.6 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.6.x\n  [3.6]: https://github.com/doctrine/orm/tree/3.6.x\n  [3.6 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.6.x\n  [3.6 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.6.x/graph/badge.svg\n  [3.6 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.6.x\n  [2.21 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.21.x\n  [2.21]: https://github.com/doctrine/orm/tree/2.21.x\n  [2.21 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.21.x\n  [2.21 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.21.x/graph/badge.svg\n  [2.21 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.21.x\n  [2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x\n  [2.20]: https://github.com/doctrine/orm/tree/2.20.x\n  [2.20 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.20.x\n  [2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg\n  [2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x\n"
  },
  {
    "path": "SECURITY.md",
    "content": "Security\n========\n\nThe Doctrine library is operating very close to your database and as such needs\nto handle and make assumptions about SQL injection vulnerabilities.\n\nIt is vital that you understand how Doctrine approaches security, because\nwe cannot protect you from SQL injection.\n\nPlease read the documentation chapter on Security in Doctrine DBAL and ORM to\nunderstand the assumptions we make.\n\n- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)\n- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/security.html)\n\nIf you find a Security bug in Doctrine, please follow our\n[Security reporting guidelines](https://www.doctrine-project.org/policies/security.html#reporting).\n"
  },
  {
    "path": "UPGRADE.md",
    "content": "Note about upgrading: Doctrine uses static and runtime mechanisms to raise\nawareness about deprecated code.\n\n- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or\n  Static Analysis tools (like Psalm, phpstan)\n- Use of our low-overhead runtime deprecation API, details:\n  https://github.com/doctrine/deprecations/\n\n# Upgrade to 3.x General Notes\n\nWe recommend you upgrade to DBAL 3 first before upgrading to ORM 3. See\nthe DBAL upgrade docs: https://github.com/doctrine/dbal/blob/3.10.x/UPGRADE.md\n\nRather than doing several major upgrades at once, we recommend you do the following:\n\n- upgrade to DBAL 3\n- deploy and monitor\n- upgrade to ORM 3\n- deploy and monitor\n- upgrade to DBAL 4\n- deploy and monitor\n\nIf you are using Symfony, the recommended minimal Doctrine Bundle version is 2.15\nto run with ORM 3.\n\nAt this point, we recommend upgrading to PHP 8.4 first and then directly from\nORM 2.19 to 3.5 and up so that you can skip the lazy ghost proxy generation\nand directly start using native lazy objects.\n\n# Upgrade to 3.6\n\n## Deprecate using string expression for default values in mappings\n\nUsing a string expression for default values in field mappings is deprecated.\nUse `Doctrine\\DBAL\\Schema\\DefaultExpression` instances instead.\n\nHere is how to address this deprecation when mapping entities using PHP attributes:\n\n```diff\n use DateTime;\n+use Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate;\n+use Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime;\n+use Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp;\n use Doctrine\\ORM\\Mapping as ORM;\n\n #[ORM\\Entity]\n final class TimeEntity\n {\n     #[ORM\\Id]\n     #[ORM\\Column]\n     public int $id;\n\n-    #[ORM\\Column(options: ['default' => 'CURRENT_TIMESTAMP'], insertable: false, updatable: false)]\n+    #[ORM\\Column(options: ['default' => new CurrentTimestamp()], insertable: false, updatable: false)]\n     public DateTime $createdAt;\n\n-    #[ORM\\Column(options: ['default' => 'CURRENT_TIME'], insertable: false, updatable: false)]\n+    #[ORM\\Column(options: ['default' => new CurrentTime()], insertable: false, updatable: false)]\n     public DateTime $createdTime;\n\n-    #[ORM\\Column(options: ['default' => 'CURRENT_DATE'], insertable: false, updatable: false)]\n+    #[ORM\\Column(options: ['default' => new CurrentDate()], insertable: false, updatable: false)]\n     public DateTime $createdDate;\n }\n```\n\nHere is how to do the same when mapping entities using XML:\n\n```diff\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                   xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                           https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n     <entity name=\"Doctrine\\Tests\\ORM\\Functional\\XmlTimeEntity\">\n         <id name=\"id\" type=\"integer\" column=\"id\">\n             <generator strategy=\"AUTO\"/>\n         </id>\n\n         <field name=\"createdAt\" type=\"datetime\" insertable=\"false\" updatable=\"false\">\n             <options>\n-                <option name=\"default\">CURRENT_TIMESTAMP</option>\n+                <option name=\"default\">\n+                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n+                </option>\n             </options>\n         </field>\n\n         <field name=\"createdAtImmutable\" type=\"datetime_immutable\" insertable=\"false\" updatable=\"false\">\n             <options>\n-                <option name=\"default\">CURRENT_TIMESTAMP</option>\n+                <option name=\"default\">\n+                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n+                </option>\n             </options>\n         </field>\n\n         <field name=\"createdTime\" type=\"time\" insertable=\"false\" updatable=\"false\">\n             <options>\n-                <option name=\"default\">CURRENT_TIME</option>\n+                <option name=\"default\">\n+                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime\"/>\n+                </option>\n             </options>\n         </field>\n         <field name=\"createdDate\" type=\"date\" insertable=\"false\" updatable=\"false\">\n             <options>\n-                <option name=\"default\">CURRENT_DATE</option>\n+                <option name=\"default\">\n+                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate\"/>\n+                </option>\n             </options>\n         </field>\n     </entity>\n </doctrine-mapping>\n```\n\n\n## Deprecate `FieldMapping::$default`\n\nThe `default` property of `Doctrine\\ORM\\Mapping\\FieldMapping` is deprecated and\nwill be removed in 4.0. Instead, use `FieldMapping::$options['default']`.\n\n## Deprecate specifying `nullable` on columns that end up being used in a primary key\n\nSpecifying `nullable` on join columns that are part of a primary key is\ndeprecated and will be an error in 4.0.\n\nThis can happen when using a join column mapping together with an id mapping,\nor when using a join column mapping or an inverse join column mapping on a\nmany-to-many relationship.\n\n```diff\n class User\n {\n     #[ORM\\Id]\n     #[ORM\\Column(type: 'integer')]\n     private int $id;\n\n     #[ORM\\Id]\n     #[ORM\\ManyToOne(targetEntity: Family::class, inversedBy: 'users')]\n-    #[ORM\\JoinColumn(name: 'family_id', referencedColumnName: 'id', nullable: true)]\n+    #[ORM\\JoinColumn(name: 'family_id', referencedColumnName: 'id')]\n     private ?Family $family;\n\n     #[ORM\\ManyToMany(targetEntity: Group::class)]\n     #[ORM\\JoinTable(name: 'user_group')]\n-    #[ORM\\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]\n-    #[ORM\\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', nullable: true)]\n+    #[ORM\\JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n+    #[ORM\\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n     private Collection $groups;\n }\n```\n\n## Deprecate `Doctrine\\ORM\\QueryBuilder::add('join', ...)` with a list of join parts\n\nUsing `Doctrine\\ORM\\QueryBuilder::add('join', ...)` with a list of join parts\nis deprecated in favor of using an associative array of join parts with the\nroot alias as key.\n\n## Deprecate using the `WITH` keyword for arbitrary DQL joins\n\nUsing the `WITH` keyword to specify the condition for an arbitrary DQL join is\ndeprecated in favor of using the `ON` keyword (similar to the SQL syntax for\njoins).\nThe `WITH` keyword is now meant to be used only for filtering conditions in\nassociation joins.\n\n# Upgrade to 3.5\n\nSee the General notes to upgrading to 3.x versions above.\n\n## Deprecate not using native lazy objects on PHP 8.4+\n\nHaving native lazy objects disabled on PHP 8.4+ is deprecated and will not be\npossible in 4.0.\n\nYou can enable them through configuration:\n\n```php\n$config->enableNativeLazyObjects(true);\n```\n\nAs a consequence, methods, parameters and commands related to userland lazy\nobjects have been deprecated on PHP 8.4+:\n\n- `Doctrine\\ORM\\Tools\\Console\\Command\\GenerateProxiesCommand`\n- `Doctrine\\ORM\\Configuration::getAutoGenerateProxyClasses()`\n- `Doctrine\\ORM\\Configuration::getProxyDir()`\n- `Doctrine\\ORM\\Configuration::getProxyNamespace()`\n- `Doctrine\\ORM\\Configuration::setAutoGenerateProxyClasses()`\n- `Doctrine\\ORM\\Configuration::setProxyDir()`\n- `Doctrine\\ORM\\Configuration::setProxyNamespace()`\n- Passing more than one argument to `Doctrine\\ORM\\Proxy\\ProxyFactory::__construct()`\n\nAdditionally, some methods of ORMSetup have been deprecated in favor of a new\ncounterpart.\n\n- `Doctrine\\ORM\\ORMSetup::createAttributeMetadataConfiguration()` is deprecated in favor of\n  `Doctrine\\ORM\\ORMSetup::createAttributeMetadataConfig()`\n- `Doctrine\\ORM\\ORMSetup::createXMLMetadataConfiguration()` is deprecated in favor of\n  `Doctrine\\ORM\\ORMSetup::createXMLMetadataConfig()`\n- `Doctrine\\ORM\\ORMSetup::createConfiguration()` is deprecated in favor of\n  `Doctrine\\ORM\\ORMSetup::createConfig()`\n\n## Deprecate methods for configuring no longer configurable features\n\nSince 3.0, lazy ghosts are enabled unconditionally, and so is rejecting ID\ncollisions in the identity map.\n\nAs a consequence, the following methods are deprecated and will be removed in 4.0:\n* `Doctrine\\ORM\\Configuration::setLazyGhostObjectEnabled()`\n* `Doctrine\\ORM\\Configuration::isLazyGhostObjectEnabled()`\n* `Doctrine\\ORM\\Configuration::setRejectIdCollisionInIdentityMap()`\n* `Doctrine\\ORM\\Configuration::isRejectIdCollisionInIdentityMapEnabled()`\n\n# Upgrade to 3.4.1\n\n## BC BREAK: You can no longer use the `.*` notation to get all fields of an entity in a DTO\n\nThis feature was introduced in 3.4.0, and introduces several issues, so we\ndecide to remove it before it is used too widely.\n\n# Upgrade to 3.4\n\nSee the General notes to upgrading to 3.x versions above.\n\n## Discriminator Map class duplicates\n\nUsing the same class several times in a discriminator map is deprecated.\nIn 4.0, this will be an error.\n\n## `Doctrine\\ORM\\Mapping\\ClassMetadata::$reflFields` deprecated\n\nTo better support property hooks and lazy proxies in the future, `$reflFields` had to\nbe deprecated because we cannot use the PHP internal reflection API directly anymore.\n\nThe property was changed from an array to an object of type `LegacyReflectionFields`\nthat implements `ArrayAccess`.\n\nUse the new `Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessor` API and access\nthrough `Doctrine\\ORM\\Mapping\\ClassMetadata::$propertyAccessors` instead.\n\nCompanion accessor methods are deprecated as well.\n\n# Upgrade to 3.3\n\nSee the General notes to upgrading to 3.x versions above.\n\n## Deprecate `DatabaseDriver`\n\nThe class `Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver` is deprecated without replacement.\n\n## Add `Doctrine\\ORM\\Query\\OutputWalker` interface, deprecate `Doctrine\\ORM\\Query\\SqlWalker::getExecutor()`\n\nOutput walkers should implement the new `\\Doctrine\\ORM\\Query\\OutputWalker` interface and create\n`Doctrine\\ORM\\Query\\Exec\\SqlFinalizer` instances instead of `Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor`s.\nThe output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the\n`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.\nAny operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`\nmethod. Details can be found at https://github.com/doctrine/orm/pull/11188.\n\n\n# Upgrade to 3.2\n\nSee the General notes to upgrading to 3.x versions above.\n\n## Deprecate the `NotSupported` exception\n\nThe class `Doctrine\\ORM\\Exception\\NotSupported` is deprecated without replacement.\n\n## Deprecate remaining `Serializable` implementation\n\nRelying on `SequenceGenerator` implementing the `Serializable` is deprecated\nbecause that interface won't be implemented in ORM 4 anymore.\n\nThe following methods are deprecated:\n\n* `SequenceGenerator::serialize()`\n* `SequenceGenerator::unserialize()`\n\n## `orm:schema-tool:update` option `--complete` is deprecated\n\nThat option behaves as a no-op, and is deprecated. It will be removed in 4.0.\n\n## Deprecate properties `$indexes` and `$uniqueConstraints` of `Doctrine\\ORM\\Mapping\\Table`\n\nThe properties `$indexes` and `$uniqueConstraints` have been deprecated since they had no effect at all.\nThe preferred way of defining indices and unique constraints is by\nusing the `\\Doctrine\\ORM\\Mapping\\UniqueConstraint` and `\\Doctrine\\ORM\\Mapping\\Index` attributes.\n\n# Upgrade to 3.1\n\nSee the General notes to upgrading to 3.x versions above.\n\n## Deprecate `Doctrine\\ORM\\Mapping\\ReflectionEnumProperty`\n\nThis class is deprecated and will be removed in 4.0.\nInstead, use `Doctrine\\Persistence\\Reflection\\EnumReflectionProperty` from\n`doctrine/persistence`.\n\n## Deprecate passing null to `ClassMetadata::fullyQualifiedClassName()`\n\nPassing `null` to `Doctrine\\ORM\\ClassMetadata::fullyQualifiedClassName()` is\ndeprecated and will no longer be possible in 4.0.\n\n## Deprecate array access\n\nUsing array access on instances of the following classes is deprecated:\n\n- `Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping`\n- `Doctrine\\ORM\\Mapping\\EmbedClassMapping`\n- `Doctrine\\ORM\\Mapping\\FieldMapping`\n- `Doctrine\\ORM\\Mapping\\JoinColumnMapping`\n- `Doctrine\\ORM\\Mapping\\JoinTableMapping`\n\n# Upgrade to 3.0\n\nSee the General notes to upgrading to 3.x versions above.\n\n## BC BREAK: Calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association now throws an exception\n\nPreviously, calling\n`Doctrine\\ORM\\Mapping\\ClassMetadata::getAssociationMappedByTargetField()` with\nthe owning side of an association returned `null`, which was undocumented, and\nwrong according to the phpdoc of the parent method.\n\nIf you do not know whether you are on the owning or inverse side of an association,\nyou can use  `Doctrine\\ORM\\Mapping\\ClassMetadata::isAssociationInverseSide()`\nto find out.\n\n## BC BREAK: `Doctrine\\ORM\\Proxy\\Autoloader` no longer extends `Doctrine\\Common\\Proxy\\Autoloader`\n\nMake sure to use the former when writing a type declaration or an `instanceof` check.\n\n## Minor BC BREAK: Changed order of arguments passed to `OneToOne`, `ManyToOne` and `Index` mapping PHP attributes\n\nTo keep PHP mapping attributes consistent, order of arguments passed to above attributes has been changed\nso `$targetEntity` is a first argument now. This change affects only non-named arguments usage.\n\n## BC BREAK: AUTO keyword for identity generation defaults to IDENTITY for PostgreSQL when using `doctrine/dbal` 4\n\nWhen using the `AUTO` strategy to let Doctrine determine the identity generation mechanism for\nan entity, and when using `doctrine/dbal` 4, PostgreSQL now uses `IDENTITY`\ninstead of `SEQUENCE` or `SERIAL`.\n\nThere are three ways to handle this change.\n\n* If you want to upgrade your existing tables to identity columns, you will need to follow [migration to identity columns on PostgreSQL](https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/how-to/postgresql-identity-migration.html)\n* If you want to keep using SQL sequences, you need to configure the ORM this way:\n```php\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\nassert($configuration instanceof Configuration);\n$configuration->setIdentityGenerationPreferences([\n    PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE,\n]);\n```\n* You can change individual entities to use the `SEQUENCE` strategy instead of `AUTO`:\n```php\n\ndiff --git a/src/Entity/Example.php b/src/Entity/Example.php\nindex 28be8df378..3b7d61bda6 100644\n--- a/src/Entity/Example.php\n+++ b/src/Entity/Example.php\n@@ -38,7 +38,7 @@ class Example\n\n     #[ORM\\Id]\n     #[ORM\\Column(type: 'integer')]\n-    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n+    #[ORM\\GeneratedValue(strategy: 'SEQUENCE')]\n     private int $id;\n\n     #[Assert\\Length(max: 255)]\n```\nThe later two options require a small database migration that will remove the default\nexpression fetching the next value from the sequence. It's not strictly necessary to\ndo this migration because the code will work anyway. A benefit of this approach is\nthat you can just make and roll out the code changes first and then migrate the database later.\n\n## BC BREAK: Throw exceptions when using illegal attributes on Embeddable\n\nThere are only a few attributes allowed on an embeddable such as `#[Column]` or\n`#[Embedded]`. Previously all others that target entity classes where ignored,\nnow they throw an exception.\n\n## BC BREAK: Partial objects are removed\n\nWARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydration.\n\n- The `PARTIAL` keyword in DQL no longer exists (reintroduced in ORM 3.2)\n- `Doctrine\\ORM\\Query\\AST\\PartialObjectExpression` is removed. (reintroduced in ORM 3.2)\n- `Doctrine\\ORM\\Query\\SqlWalker::HINT_PARTIAL` (reintroduced in ORM 3.2) and\n  `Doctrine\\ORM\\Query::HINT_FORCE_PARTIAL_LOAD` are removed.\n- `Doctrine\\ORM\\EntityManager*::getPartialReference()` is removed.\n\n## BC BREAK: Enforce ArrayCollection Type on `\\Doctrine\\ORM\\QueryBuilder::setParameters(ArrayCollection $parameters)`\n\nThe argument $parameters can no longer be a key=>value array. Only ArrayCollection types are allowed.\n\n### Before\n\n```php\n$qb = $em->createQueryBuilder()\n    ->select('u')\n    ->from('User', 'u')\n    ->where('u.id = :user_id1 OR u.id = :user_id2')\n    ->setParameters(array(\n        'user_id1' => 1,\n        'user_id2' => 2\n    ));\n```\n\n### After\n\n```php\n$qb = $em->createQueryBuilder()\n    ->select('u')\n    ->from('User', 'u')\n    ->where('u.id = :user_id1 OR u.id = :user_id2')\n    ->setParameters(new ArrayCollection(array(\n        new Parameter('user_id1', 1),\n        new Parameter('user_id2', 2)\n    )));\n```\n\n## BC BREAK: `Doctrine\\ORM\\Persister\\Entity\\EntityPersister::executeInserts()` return type changed to `void`\n\nImplementors should adapt to the new signature, and should call\n`UnitOfWork::assignPostInsertId()` for each entry in the previously returned\narray.\n\n## BC BREAK: `Doctrine\\ORM\\Proxy\\ProxyFactory` no longer extends abstract factory from `doctrine/common`\n\nIt is no longer possible to call methods, constants or properties inherited\nfrom that class on a `ProxyFactory` instance.\n\n`Doctrine\\ORM\\Proxy\\ProxyFactory::createProxyDefinition()` and\n`Doctrine\\ORM\\Proxy\\ProxyFactory::resetUninitializedProxy()` are removed as well.\n\n## BC BREAK: lazy ghosts are enabled unconditionally\n\n`Doctrine\\ORM\\Configuration::setLazyGhostObjectEnabled()` and\n`Doctrine\\ORM\\Configuration::isLazyGhostObjectEnabled()` are now no-ops and\nwill be deprecated in 3.1.0\n\n## BC BREAK: collisions in identity map are unconditionally rejected\n\n`Doctrine\\ORM\\Configuration::setRejectIdCollisionInIdentityMap()` and\n`Doctrine\\ORM\\Configuration::isRejectIdCollisionInIdentityMapEnabled()` are now\nno-ops and will be deprecated in 3.1.0.\n\n## BC BREAK: Lifecycle callback mapping on embedded classes is now explicitly forbidden\n\nLifecycle callback mapping on embedded classes produced no effect, and is now\nexplicitly forbidden to point out mistakes.\n\n## BC BREAK: The `NOTIFY` change tracking policy is removed\n\nYou should use `DEFERRED_EXPLICIT` instead.\n\n## BC BREAK: `Mapping\\Driver\\XmlDriver::__construct()` third argument is now enabled by default\n\nThe third argument to\n`Doctrine\\ORM\\Mapping\\Driver\\XmlDriver::__construct()` was introduced to\nlet users opt-in to XML validation, that is now always enabled by default.\n\nAs a consequence, the same goes for\n`Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver`, and for\n`Doctrine\\ORM\\ORMSetup::createXMLMetadataConfiguration()`.\n\n## BC BREAK: `Mapping\\Driver\\AttributeDriver::__construct()` second argument is now a no-op\n\nThe second argument to\n`Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver::__construct()` was introduced to\nlet users opt-in to a new behavior, that is now always enforced, regardless of\nthe value of that argument.\n\n## BC BREAK: `Query::setDQL()` and `Query::setFirstResult()` no longer accept `null`\n\nThe `$dqlQuery` argument of `Doctrine\\ORM\\Query::setDQL()` must always be a\nstring.\n\nThe `$firstResult` argument of `Doctrine\\ORM\\Query::setFirstResult()` must\nalways be an integer.\n\n## BC BREAK: `orm:schema-tool:update` option `--complete` is now a no-op\n\n`orm:schema-tool:update` now behaves as if `--complete` was provided,\nregardless of whether it is provided or not.\n\n## BC BREAK: Removed `Doctrine\\ORM\\Proxy\\Proxy` interface.\n\nUse `Doctrine\\Persistence\\Proxy` instead to check whether proxies are initialized.\n\n## BC BREAK: Overriding fields or associations declared in other than mapped superclasses\n\nAs stated in the documentation, fields and associations may only be overridden when being inherited\nfrom mapped superclasses. Overriding them for parent entity classes now throws a `MappingException`.\n\n## BC BREAK: Undeclared entity inheritance now throws a `MappingException`\n\nAs soon as an entity class inherits from another entity class, inheritance has to\nbe declared by adding the appropriate configuration for the root entity.\n\n## Removed `getEntityManager()` in `Doctrine\\ORM\\Event\\OnClearEventArgs` and `Doctrine\\ORM\\Event\\*FlushEventArgs`\n\nUse `getObjectManager()` instead.\n\n## BC BREAK: Removed `Doctrine\\ORM\\Mapping\\ClassMetadataInfo` class\n\nUse `Doctrine\\ORM\\Mapping\\ClassMetadata` instead.\n\n## BC BREAK: Removed `Doctrine\\ORM\\Event\\LifecycleEventArgs` class.\n\nUse one of the dedicated event classes instead:\n\n* `Doctrine\\ORM\\Event\\PrePersistEventArgs`\n* `Doctrine\\ORM\\Event\\PreUpdateEventArgs`\n* `Doctrine\\ORM\\Event\\PreRemoveEventArgs`\n* `Doctrine\\ORM\\Event\\PostPersistEventArgs`\n* `Doctrine\\ORM\\Event\\PostUpdateEventArgs`\n* `Doctrine\\ORM\\Event\\PostRemoveEventArgs`\n* `Doctrine\\ORM\\Event\\PostLoadEventArgs`\n\n## BC BREAK: Removed `AttributeDriver::$entityAnnotationClasses` and `AttributeDriver::getReader()`\n\n* If you need to change the behavior of `AttributeDriver::isTransient()`,\n  override that method instead.\n* The attribute reader is internal to the driver and should not be accessed from outside.\n\n## BC BREAK: Removed `Doctrine\\ORM\\Query\\AST\\InExpression`\n\nThe AST parser will create a `InListExpression` or a `InSubselectExpression` when\nencountering an `IN ()` DQL expression instead of a generic `InExpression`.\n\nAs a consequence, `SqlWalker::walkInExpression()` has been replaced by\n`SqlWalker::walkInListExpression()` and `SqlWalker::walkInSubselectExpression()`.\n\n## BC BREAK: Changed `EntityManagerInterface#refresh($entity)`, `EntityManagerDecorator#refresh($entity)` and `UnitOfWork#refresh($entity)` signatures\n\nThe new signatures of these methods add an optional `LockMode|int|null $lockMode`\nparam with default `null` value (no lock).\n\n## BC Break: Removed AnnotationDriver\n\nThe annotation driver and anything related to annotation has been removed.\nPlease migrate to another mapping driver.\n\nThe `Doctrine\\ORM\\Mapping\\Annotation` maker interface has been removed in favor of the new\n`Doctrine\\ORM\\Mapping\\MappingAttribute` interface.\n\n## BC BREAK: Removed `EntityManager::create()`\n\nThe constructor of `EntityManager` is now public and must be used instead of the `create()` method.\nHowever, the constructor expects a `Connection` while `create()` accepted an array with connection parameters.\nYou can pass that array to DBAL's `Doctrine\\DBAL\\DriverManager::getConnection()` method to bootstrap the\nconnection.\n\n## BC BREAK: Removed `QueryBuilder` methods and constants.\n\nThe following `QueryBuilder` constants and methods have been removed:\n\n1. `SELECT`,\n2. `DELETE`,\n3. `UPDATE`,\n4. `STATE_DIRTY`,\n5. `STATE_CLEAN`,\n6. `getState()`,\n7. `getType()`.\n\n## BC BREAK: Omitting only the alias argument for `QueryBuilder::update` and `QueryBuilder::delete` is not supported anymore\n\nWhen building an UPDATE or DELETE query and when passing a class/type to the function, the alias argument must not be omitted.\n\n### Before\n\n```php\n$qb = $em->createQueryBuilder()\n    ->delete('User u')\n    ->where('u.id = :user_id')\n    ->setParameter('user_id', 1);\n```\n\n### After\n\n```php\n$qb = $em->createQueryBuilder()\n    ->delete('User', 'u')\n    ->where('u.id = :user_id')\n    ->setParameter('user_id', 1);\n```\n\n## BC BREAK: Split output walkers and tree walkers\n\n`SqlWalker` and its child classes don't implement the `TreeWalker` interface\nanymore.\n\nThe following methods have been removed from the `TreeWalker` interface and\nfrom the `TreeWalkerAdapter` and `TreeWalkerChain` classes:\n\n* `setQueryComponent()`\n* `walkSelectClause()`\n* `walkFromClause()`\n* `walkFunction()`\n* `walkOrderByClause()`\n* `walkOrderByItem()`\n* `walkHavingClause()`\n* `walkJoin()`\n* `walkSelectExpression()`\n* `walkQuantifiedExpression()`\n* `walkSubselect()`\n* `walkSubselectFromClause()`\n* `walkSimpleSelectClause()`\n* `walkSimpleSelectExpression()`\n* `walkAggregateExpression()`\n* `walkGroupByClause()`\n* `walkGroupByItem()`\n* `walkDeleteClause()`\n* `walkUpdateClause()`\n* `walkUpdateItem()`\n* `walkWhereClause()`\n* `walkConditionalExpression()`\n* `walkConditionalTerm()`\n* `walkConditionalFactor()`\n* `walkConditionalPrimary()`\n* `walkExistsExpression()`\n* `walkCollectionMemberExpression()`\n* `walkEmptyCollectionComparisonExpression()`\n* `walkNullComparisonExpression()`\n* `walkInExpression()`\n* `walkInstanceOfExpression()`\n* `walkLiteral()`\n* `walkBetweenExpression()`\n* `walkLikeExpression()`\n* `walkStateFieldPathExpression()`\n* `walkComparisonExpression()`\n* `walkInputParameter()`\n* `walkArithmeticExpression()`\n* `walkArithmeticTerm()`\n* `walkStringPrimary()`\n* `walkArithmeticFactor()`\n* `walkSimpleArithmeticExpression()`\n* `walkPathExpression()`\n* `walkResultVariable()`\n* `getExecutor()`\n\nThe following changes have been made to the abstract `TreeWalkerAdapter` class:\n\n* The method `setQueryComponent()` is now protected.\n* The method `_getQueryComponents()` has been removed in favor of\n  `getQueryComponents()`.\n\n## BC BREAK: Removed identity columns emulation through sequences\n\nIf the platform you are using does not support identity columns, you should\nswitch to the `SEQUENCE` strategy.\n\n## BC BREAK: Made setters parameters mandatory\n\nThe following methods require an argument when being called. Pass `null`\ninstead of omitting the argument.\n\n* `Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs::setFoundMetadata()`\n* `Doctrine\\ORM\\AbstractQuery::setHydrationCacheProfile()`\n* `Doctrine\\ORM\\AbstractQuery::setResultCache()`\n* `Doctrine\\ORM\\AbstractQuery::setResultCacheProfile()`\n\n## BC BREAK: New argument to `NamingStrategy::joinColumnName()`\n\n### Before\n\n```php\n<?php\nclass MyStrategy implements NamingStrategy\n{\n    /**\n     * @param string $propertyName A property name.\n     */\n    public function joinColumnName($propertyName): string\n    {\n        // …\n    }\n}\n```\n\n### After\n\nThe `class-string` type for `$className` can be inherited from the signature of\nthe interface.\n\n```php\n<?php\nclass MyStrategy implements NamingStrategy\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function joinColumnName(string $propertyName, string $className): string\n    {\n        // …\n    }\n}\n```\n\n## BC BREAK: Remove `StaticPHPDriver` and `DriverChain`\n\nUse `Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver` and\n`Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain` from\n`doctrine/persistence` instead.\n\n## BC BREAK: `UnderscoreNamingStrategy` is number aware only\n\nThe second argument to `UnderscoreNamingStrategy::__construct()` was dropped,\nthe strategy can no longer be unaware of numbers.\n\n## BC BREAK: Remove `Doctrine\\ORM\\Tools\\DisconnectedClassMetadataFactory`\n\nNo replacement is provided.\n\n## BC BREAK: Remove support for `Type::canRequireSQLConversion()`\n\nThis feature was deprecated in DBAL 3.3.0 and will be removed in DBAL 4.0.\nThe value conversion methods are now called regardless of the type.\n\nThe `MappingException::sqlConversionNotAllowedForIdentifiers()` method has been removed\nas no longer relevant.\n\n## BC Break: Removed the `doctrine` binary.\n\nThe documentation explains how the console tools can be bootstrapped for\nstandalone usage:\n\nhttps://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/tools.html\n\nThe method `ConsoleRunner::printCliConfigTemplate()` has been removed as well\nbecause it was only useful in the context of the `doctrine` binary.\n\n## BC Break: Removed `EntityManagerHelper` and related logic\n\nAll console commands require a `$entityManagerProvider` to be passed via the\nconstructor. Commands won't try to get the entity manager from a previously\nregistered `em` console helper.\n\nThe following classes have been removed:\n\n* `Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\HelperSetManagerProvider`\n* `Doctrine\\ORM\\Tools\\Console\\Helper\\EntityManagerHelper`\n\nThe following breaking changes have been applied to `Doctrine\\ORM\\Tools\\Console\\ConsoleRunner`:\n\n* The method `createHelperSet()` has been removed.\n* The methods `run()` and `createApplication()` don't accept an instance of\n  `HelperSet` as first argument anymore.\n* The method `addCommands()` requires an instance of `EntityManagerProvider`\n  as second argument now.\n\n## BC Break: `Exception\\ORMException` is no longer a class, but an interface\n\nAll methods in `Doctrine\\ORM\\ORMException` have been extracted to dedicated exceptions.\n\n * `missingMappingDriverImpl()` => `Exception\\MissingMappingDriverImplementation::create()`\n * `unrecognizedField()` => `Persisters\\Exception\\UnrecognizedField::byName()`\n * `unexpectedAssociationValue()` => `Exception\\UnexpectedAssociationValue::create()`\n * `invalidOrientation()` => `Persisters\\Exception\\InvalidOrientation::fromClassNameAndField()`\n * `entityManagerClosed()` => `Exception\\EntityManagerClosed::create()`\n * `invalidHydrationMode()` => `Exception\\InvalidHydrationMode::fromMode()`\n * `mismatchedEventManager()` => `Exception\\MismatchedEventManager::create()`\n * `findByRequiresParameter()` => `Repository\\Exception\\InvalidMagicMethodCall::onMissingParameter()`\n * `invalidMagicCall()` => `Repository\\Exception\\InvalidMagicMethodCall::becauseFieldNotFoundIn()`\n * `invalidFindByInverseAssociation()` => `Repository\\Exception\\InvalidFindByCall::fromInverseSideUsage()`\n * `invalidResultCacheDriver()` => `Cache\\Exception\\InvalidResultCacheDriver::create()`\n * `notSupported()` => `Exception\\NotSupported::create()`\n * `queryCacheNotConfigured()` => `QueryCacheNotConfigured::create()`\n * `metadataCacheNotConfigured()` => `Cache\\Exception\\MetadataCacheNotConfigured::create()`\n * `queryCacheUsesNonPersistentCache()` => `Cache\\Exception\\QueryCacheUsesNonPersistentCache::fromDriver()`\n * `metadataCacheUsesNonPersistentCache()` => `Cache\\Exception\\MetadataCacheUsesNonPersistentCache::fromDriver()`\n * `proxyClassesAlwaysRegenerating()` => `Exception\\ProxyClassesAlwaysRegenerating::create()`\n * `invalidEntityRepository()` => `Exception\\InvalidEntityRepository::fromClassName()`\n * `missingIdentifierField()` => `Exception\\MissingIdentifierField::fromFieldAndClass()`\n * `unrecognizedIdentifierFields()` => `Exception\\UnrecognizedIdentifierFields::fromClassAndFieldNames()`\n * `cantUseInOperatorOnCompositeKeys()` => `Persisters\\Exception\\CantUseInOperatorOnCompositeKeys::create()`\n\n## BC Break: `CacheException` is no longer a class, but an interface\n\nAll methods in `Doctrine\\ORM\\Cache\\CacheException` have been extracted to dedicated exceptions.\n\n * `updateReadOnlyCollection()` => `Cache\\Exception\\CannotUpdateReadOnlyCollection::fromEntityAndField()`\n * `updateReadOnlyEntity()` => `Cache\\Exception\\CannotUpdateReadOnlyEntity::fromEntity()`\n * `nonCacheableEntity()` => `Cache\\Exception\\NonCacheableEntity::fromEntity()`\n * `nonCacheableEntityAssociation()` => `Cache\\Exception\\NonCacheableEntityAssociation::fromEntityAndField()`\n\n\n## BC Break: Missing type declaration added for identifier generators\n\nAlthough undocumented, it was possible to configure a custom repository\nclass that implements `ObjectRepository` but does not extend the\n`EntityRepository` base class. Repository classes have to extend\n`EntityRepository` now.\n\n## BC BREAK: Removed support for entity namespace alias\n\n- `EntityManager::getRepository()` no longer accepts the entity namespace alias\n  notation.\n- `Configuration::addEntityNamespace()` and\n  `Configuration::getEntityNamespace()` have been removed.\n\n## BC BREAK: Remove helper methods from `AbstractCollectionPersister`\n\nThe following protected methods of\n`Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister`\nhave been removed.\n\n* `evictCollectionCache()`\n* `evictElementCache()`\n\n## BC BREAK: `Doctrine\\ORM\\Query\\TreeWalkerChainIterator`\n\nThis class has been removed without replacement.\n\n## BC BREAK: Remove quoting methods from `ClassMetadata`\n\nThe following methods have been removed from the class metadata because\nquoting is handled by implementations of `Doctrine\\ORM\\Mapping\\QuoteStrategy`:\n\n* `getQuotedIdentifierColumnNames()`\n* `getQuotedColumnName()`\n* `getQuotedTableName()`\n* `getQuotedJoinTableName()`\n\n## BC BREAK: Remove ability to merge detached entities\n\nMerge semantics was a poor fit for the PHP \"share-nothing\" architecture.\nIn addition to that, merging caused multiple issues with data integrity\nin the managed entity graph, which was constantly spawning more edge-case\nbugs/scenarios.\n\nThe method `UnitOfWork::merge()` has been removed. The method\n`EntityManager::merge()` will throw an exception on each call.\n\n## BC BREAK: Removed ability to partially flush/commit entity manager and unit of work\n\nThe following methods don't accept a single entity or an array of entities anymore:\n\n* `Doctrine\\ORM\\EntityManager::flush()`\n* `Doctrine\\ORM\\Decorator\\EntityManagerDecorator::flush()`\n* `Doctrine\\ORM\\UnitOfWork::commit()`\n\nThe semantics of `flush()` and `commit()` will remain the same, but the change\ntracking will be performed on all entities managed by the unit of work, and not\njust on the provided entities, as the parameter is now completely ignored.\n\n## BC BREAK: Removed ability to partially clear entity manager and unit of work\n\n* Passing an argument other than `null` to `EntityManager::clear()` will raise\n  an exception.\n* The unit of work cannot be cleared partially anymore. Passing an argument to\n  `UnitOfWork::clear()` does not have any effect anymore; the unit of work is\n  cleared completely.\n* The method `EntityRepository::clear()` has been removed.\n* The methods `getEntityClass()` and `clearsAllEntities()` have been removed\n  from `OnClearEventArgs`.\n\n## BC BREAK: Remove support for Doctrine Cache\n\nThe Doctrine Cache library is not supported anymore. The following methods\nhave been removed from `Doctrine\\ORM\\Configuration`:\n\n* `getQueryCacheImpl()`\n* `setQueryCacheImpl()`\n* `getHydrationCacheImpl()`\n* `setHydrationCacheImpl()`\n* `getMetadataCacheImpl()`\n* `setMetadataCacheImpl()`\n\nThe methods have been replaced by PSR-6 compatible counterparts\n(just strip the `Impl` suffix from the old name to get the new one).\n\n## BC BREAK: Remove `Doctrine\\ORM\\Configuration::newDefaultAnnotationDriver`\n\nThis functionality has been moved to the new `ORMSetup` class. Call\n`Doctrine\\ORM\\ORMSetup::createDefaultAnnotationDriver()` to create\na new annotation driver.\n\n## BC BREAK: Remove `Doctrine\\ORM\\Tools\\Setup`\n\nIn our effort to migrate from Doctrine Cache to PSR-6, the `Setup` class which\naccepted a Doctrine Cache instance in each method has been removed.\n\nThe replacement is `Doctrine\\ORM\\ORMSetup` which accepts a PSR-6\ncache instead.\n\n## BC BREAK: Removed named queries\n\nAll APIs related to named queries have been removed.\n\n## BC BREAK: Remove old cache accessors and mutators from query classes\n\nThe following methods have been removed from `AbstractQuery`:\n\n* `setResultCacheDriver()`\n* `getResultCacheDriver()`\n* `useResultCache()`\n* `getResultCacheLifetime()`\n* `getResultCacheId()`\n\nThe following methods have been removed from `Query`:\n\n* `setQueryCacheDriver()`\n* `getQueryCacheDriver()`\n\n## BC BREAK: Remove `Doctrine\\ORM\\Cache\\MultiGetRegion`\n\nThe interface has been merged into `Doctrine\\ORM\\Cache\\Region`.\n\n## BC BREAK: Rename `AbstractIdGenerator::generate()` to `generateId()`\n\n* Implementations of `AbstractIdGenerator` have to implement the method\n  `generateId()`.\n* The method `generate()` has been removed from `AbstractIdGenerator`.\n\n## BC BREAK: Remove cache settings inspection\n\nDoctrine does not provide its own cache implementation anymore and relies on\nthe PSR-6 standard instead. As a consequence, we cannot determine anymore\nwhether a given cache adapter is suitable for a production environment.\nBecause of that, functionality that aims to do so has been removed:\n\n* `Configuration::ensureProductionSettings()`\n* the `orm:ensure-production-settings` console command\n\n## BC BREAK: PSR-6-based second level cache\n\nThe second level cache has been reworked to consume a PSR-6 cache. Using a\nDoctrine Cache instance is not supported anymore.\n\n* `DefaultCacheFactory`: The constructor expects a PSR-6 cache item pool as\n  second argument now.\n* `DefaultMultiGetRegion`: This class has been removed.\n* `DefaultRegion`:\n    * The constructor expects a PSR-6 cache item pool as second argument now.\n    * The protected `$cache` property is removed.\n    * The properties `$name` and `$lifetime` as well as the constant\n      `REGION_KEY_SEPARATOR` and the method `getCacheEntryKey()` are\n      `private` now.\n    * The method `getCache()` has been removed.\n\n\n## BC Break: Remove `Doctrine\\ORM\\Mapping\\Driver\\PHPDriver`\n\nUse `StaticPHPDriver` instead when you want to programmatically configure\nentity metadata.\n\n## BC BREAK: Remove `Doctrine\\ORM\\EntityManagerInterface#transactional()`\n\nThis method has been replaced by `Doctrine\\ORM\\EntityManagerInterface#wrapInTransaction()`.\n\n## BC BREAK: Removed support for schema emulation.\n\nThe ORM no longer attempts to emulate schemas on SQLite.\n\n## BC BREAK: Remove `Setup::registerAutoloadDirectory()`\n\nUse Composer's autoloader instead.\n\n## BC BREAK: Remove YAML mapping drivers.\n\nIf your code relies on `YamlDriver` or `SimpleYamlDriver`, you **MUST** migrate to\nattribute, annotation or XML drivers instead.\n\nYou can use the `orm:convert-mapping` command to convert your metadata mapping to XML\n_before_ upgrading to 3.0:\n\n```sh\nphp doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml\n```\n\n## BC BREAK: Remove code generators and related console commands\n\nThese console commands have been removed:\n\n* `orm:convert-d1-schema`\n* `orm:convert-mapping`\n* `orm:generate:entities`\n* `orm:generate-repositories`\n\nThese classes have been deprecated:\n\n* `Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema`\n* `Doctrine\\ORM\\Tools\\EntityGenerator`\n* `Doctrine\\ORM\\Tools\\EntityRepositoryGenerator`\n\nThe entire `Doctrine\\ORM\\Tools\\Export` namespace has been removed as well.\n\n## BC BREAK: Removed `Doctrine\\ORM\\Version`\n\nUse Composer's runtime API if you _really_ need to check the version of the ORM package at runtime.\n\n## BC BREAK: EntityRepository::count() signature change\n\nThe argument `$criteria` of `Doctrine\\ORM\\EntityRepository::count()` is now\noptional. Overrides in child classes should be made compatible.\n\n## BC BREAK: changes in exception hierarchy\n\n- `Doctrine\\ORM\\ORMException` has been removed\n- `Doctrine\\ORM\\Exception\\ORMException` is now an interface\n\n## Variadic methods now use native variadics\nThe following methods were using `func_get_args()` to simulate a variadic argument:\n- `Doctrine\\ORM\\Query\\Expr#andX()`\n- `Doctrine\\ORM\\Query\\Expr#orX()`\n- `Doctrine\\ORM\\QueryBuilder#select()`\n- `Doctrine\\ORM\\QueryBuilder#addSelect()`\n- `Doctrine\\ORM\\QueryBuilder#where()`\n- `Doctrine\\ORM\\QueryBuilder#andWhere()`\n- `Doctrine\\ORM\\QueryBuilder#orWhere()`\n- `Doctrine\\ORM\\QueryBuilder#groupBy()`\n- `Doctrine\\ORM\\QueryBuilder#andGroupBy()`\n- `Doctrine\\ORM\\QueryBuilder#having()`\n- `Doctrine\\ORM\\QueryBuilder#andHaving()`\n- `Doctrine\\ORM\\QueryBuilder#orHaving()`\nA variadic argument is now actually used in their signatures signature (`...$x`).\nSignatures of overridden methods should be changed accordingly\n\n## Minor BC BREAK: removed `Doctrine\\ORM\\EntityManagerInterface#copy()`\n\nMethod `Doctrine\\ORM\\EntityManagerInterface#copy()` never got its implementation and is removed in 3.0.\n\n## BC BREAK: Removed classes related to UUID and TABLE generator strategies\n\nThe following classes have been removed:\n- `Doctrine\\ORM\\Id\\TableGenerator`\n- `Doctrine\\ORM\\Id\\UuidGenerator`\n\nUsing the `UUID` strategy for generating identifiers is not supported anymore.\n\n## BC BREAK: Removed `Query::iterate()`\n\nThe deprecated method `Query::iterate()` has been removed along with the\nfollowing classes and methods:\n\n- `AbstractHydrator::iterate()`\n- `AbstractHydrator::hydrateRow()`\n- `IterableResult`\n\nUse `toIterable()` instead.\n\n# Upgrade to 2.20\n\n## Add `Doctrine\\ORM\\Query\\OutputWalker` interface, deprecate `Doctrine\\ORM\\Query\\SqlWalker::getExecutor()`\n\nOutput walkers should implement the new `\\Doctrine\\ORM\\Query\\OutputWalker` interface and create\n`Doctrine\\ORM\\Query\\Exec\\SqlFinalizer` instances instead of `Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor`s.\nThe output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the\n`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.\nAny operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`\nmethod. Details can be found at https://github.com/doctrine/orm/pull/11188.\n\n## Explictly forbid property hooks\n\nProperty hooks are not supported yet by Doctrine ORM. Until support is added,\nthey are explicitly forbidden because the support would result in a breaking\nchange in behavior.\n\nProgress on this is tracked at https://github.com/doctrine/orm/issues/11624 .\n\n## PARTIAL DQL syntax is undeprecated\n\nUse of the PARTIAL keyword is not deprecated anymore in DQL, because we will be\nable to support PARTIAL objects with PHP 8.4 Lazy Objects and\nSymfony/VarExporter in a better way. When we decided to remove this feature\nthese two abstractions did not exist yet.\n\nWARNING: If you want to upgrade to 3.x and still use PARTIAL keyword in DQL\nwith array or object hydrators, then you have to directly migrate to ORM 3.3.x or higher.\nPARTIAL keyword in DQL is not available in 3.0, 3.1 and 3.2 of ORM.\n\n## Deprecate `\\Doctrine\\ORM\\Query\\Parser::setCustomOutputTreeWalker()`\n\nUse the `\\Doctrine\\ORM\\Query::HINT_CUSTOM_OUTPUT_WALKER` query hint to set the output walker\nclass instead of setting it through the `\\Doctrine\\ORM\\Query\\Parser::setCustomOutputTreeWalker()` method\non the parser instance.\n\n# Upgrade to 2.19\n\n## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association\n\nCalling\n`Doctrine\\ORM\\Mapping\\ClassMetadata::getAssociationMappedByTargetField()` with\nthe owning side of an association returns `null`, which is undocumented, and\nwrong according to the phpdoc of the parent method.\n\nIf you do not know whether you are on the owning or inverse side of an association,\nyou can use  `Doctrine\\ORM\\Mapping\\ClassMetadata::isAssociationInverseSide()`\nto find out.\n\n## Deprecate `Doctrine\\ORM\\Query\\Lexer::T_*` constants\n\nUse `Doctrine\\ORM\\Query\\TokenType::T_*` instead.\n\n# Upgrade to 2.17\n\n## Deprecate annotations classes for named queries\n\nThe following classes have been deprecated:\n\n* `Doctrine\\ORM\\Mapping\\NamedNativeQueries`\n* `Doctrine\\ORM\\Mapping\\NamedNativeQuery`\n* `Doctrine\\ORM\\Mapping\\NamedQueries`\n* `Doctrine\\ORM\\Mapping\\NamedQuery`\n\n## Deprecate `Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor::_sqlStatements`\n\nUse `Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor::sqlStatements` instead.\n\n## Undeprecate `Doctrine\\ORM\\Proxy\\Autoloader`\n\nIt will be a full-fledged class, no longer extending\n`Doctrine\\Common\\Proxy\\Autoloader` in 3.0.x.\n\n## Deprecated: reliance on the non-optimal defaults that come with the `AUTO` identifier generation strategy\n\nWhen the `AUTO` identifier generation strategy was introduced, the best\nstrategy at the time was selected for each database platform.\nA lot of time has passed since then, and with ORM 3.0.0 and DBAL 4.0.0, support\nfor better strategies will be added.\n\nBecause of that, it is now deprecated to rely on the historical defaults when\nthey differ from what we will be recommended in the future.\n\nInstead, you should pick a strategy for each database platform you use, and it\nwill be used when using `AUTO`. As of now, only PostgreSQL is affected by this.\n\nIt is recommended that PostgreSQL users configure their existing and new\napplications to use `SEQUENCE` until `doctrine/dbal` 4.0.0 is released:\n\n```php\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Configuration;\n\nassert($configuration instanceof Configuration);\n$configuration->setIdentityGenerationPreferences([\n    PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE,\n]);\n```\n\nWhen DBAL 4 is released, `AUTO` will result in `IDENTITY`, and the above\nconfiguration should be removed to migrate to it.\n\n## Deprecate `EntityManagerInterface::getPartialReference()`\n\nThis method does not have a replacement and will be removed in 3.0.\n\n## Deprecate not-enabling lazy-ghosts\n\nNot enabling lazy ghost objects is deprecated. In ORM 3.0, they will be always enabled.\nEnsure `Doctrine\\ORM\\Configuration::setLazyGhostObjectEnabled(true)` is called to enable them.\n\n# Upgrade to 2.16\n\n## Deprecated accepting duplicate IDs in the identity map\n\nFor any given entity class and ID value, there should be only one object instance\nrepresenting the entity.\n\nIn https://github.com/doctrine/orm/pull/10785, a check was added that will guard this\nin the identity map. The most probable cause for violations of this rule are collisions\nof application-provided IDs.\n\nIn ORM 2.16.0, the check was added by throwing an exception. In ORM 2.16.1, this will be\nchanged to a deprecation notice. ORM 3.0 will make it an exception again. Use\n`\\Doctrine\\ORM\\Configuration::setRejectIdCollisionInIdentityMap()` if you want to opt-in\nto the new mode.\n\n## Potential changes to the order in which `INSERT`s are executed\n\nIn https://github.com/doctrine/orm/pull/10547, the commit order computation was improved\nto fix a series of bugs where a correct (working) commit order was previously not found.\nAlso, the new computation may get away with fewer queries being executed: By inserting\nreferred-to entities first and using their ID values for foreign key fields in subsequent\n`INSERT` statements, additional `UPDATE` statements that were previously necessary can be\navoided.\n\nWhen using database-provided, auto-incrementing IDs, this may lead to IDs being assigned\nto entities in a different order than it was previously the case.\n\n## Deprecated returning post insert IDs from `EntityPersister::executeInserts()`\n\nPersisters implementing `\\Doctrine\\ORM\\Persisters\\Entity\\EntityPersister` should no longer\nreturn an array of post insert IDs from their `::executeInserts()` method. Make the\npersister call `Doctrine\\ORM\\UnitOfWork::assignPostInsertId()` instead.\n\n## Changing the way how reflection-based mapping drivers report fields, deprecated the \"old\" mode\n\nIn ORM 3.0, a change will be made regarding how the `AttributeDriver` reports field mappings.\nThis change is necessary to be able to detect (and reject) some invalid mapping configurations.\n\nTo avoid surprises during 2.x upgrades, the new mode is opt-in. It can be activated on the\n`AttributeDriver` and `AnnotationDriver` by setting the `$reportFieldsWhereDeclared`\nconstructor parameter to `true`. It will cause `MappingException`s to be thrown when invalid\nconfigurations are detected.\n\nNot enabling the new mode will cause a deprecation notice to be raised. In ORM 3.0,\nonly the new mode will be available.\n\n# Upgrade to 2.15\n\n## Deprecated configuring `JoinColumn` on the inverse side of one-to-one associations\n\nFor one-to-one associations, the side using the `mappedBy` attribute is the inverse side.\nThe owning side is the entity with the table containing the foreign key. Using `JoinColumn`\nconfiguration on the _inverse_ side now triggers a deprecation notice and will be an error\nin 3.0.\n\n## Deprecated overriding fields or associations not declared in mapped superclasses\n\nAs stated in the documentation, fields and associations may only be overridden when being inherited\nfrom mapped superclasses. Overriding them for parent entity classes now triggers a deprecation notice\nand will be an error in 3.0.\n\n## Deprecated undeclared entity inheritance\n\nAs soon as an entity class inherits from another entity class, inheritance has to\nbe declared by adding the appropriate configuration for the root entity.\n\n## Deprecated stubs for \"concrete table inheritance\"\n\nThis third way of mapping class inheritance was never implemented. Code stubs are\nnow deprecated and will be removed in 3.0.\n\n* `\\Doctrine\\ORM\\Mapping\\ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS` constant\n* `\\Doctrine\\ORM\\Mapping\\ClassMetadataInfo::isInheritanceTypeTablePerClass()` method\n* Using `TABLE_PER_CLASS` as the value for the `InheritanceType` attribute or annotation\n  or in XML configuration files.\n\n# Upgrade to 2.14\n\n## Deprecated `Doctrine\\ORM\\Persisters\\Exception\\UnrecognizedField::byName($field)` method.\n\nUse `Doctrine\\ORM\\Persisters\\Exception\\UnrecognizedField::byFullyQualifiedName($className, $field)` instead.\n\n## Deprecated constants of `Doctrine\\ORM\\Internal\\CommitOrderCalculator`\n\nThe following public constants have been deprecated:\n\n* `CommitOrderCalculator::NOT_VISITED`\n* `CommitOrderCalculator::IN_PROGRESS`\n* `CommitOrderCalculator::VISITED`\n\nThese constants were used for internal purposes. Relying on them is discouraged.\n\n## Deprecated `Doctrine\\ORM\\Query\\AST\\InExpression`\n\nThe AST parser will create a `InListExpression` or a `InSubselectExpression` when\nencountering an `IN ()` DQL expression instead of a generic `InExpression`.\n\nAs a consequence, `SqlWalker::walkInExpression()` has been deprecated in favor of\n`SqlWalker::walkInListExpression()` and `SqlWalker::walkInSubselectExpression()`.\n\n## Deprecated constructing a `CacheKey` without `$hash`\n\nThe `Doctrine\\ORM\\Cache\\CacheKey` class has an explicit constructor now with\nan optional parameter `$hash`. That parameter will become mandatory in 3.0.\n\n## Deprecated `AttributeDriver::$entityAnnotationClasses`\n\nIf you need to change the behavior of `AttributeDriver::isTransient()`,\noverride that method instead.\n\n## Deprecated incomplete schema updates\n\nUsing `orm:schema-tool:update` without passing the `--complete` flag is\ndeprecated. Use schema asset filtering if you need to preserve assets not\nmanaged by DBAL.\n\nLikewise, calling `SchemaTool::updateSchema()` or\n`SchemaTool::getUpdateSchemaSql()` with a second argument is deprecated.\n\n## Deprecated annotation mapping driver.\n\nPlease switch to one of the other mapping drivers. Native attributes which PHP\nsupports since version 8.0 are probably your best option.\n\nAs a consequence, the following methods are deprecated:\n- `ORMSetup::createAnnotationMetadataConfiguration`\n- `ORMSetup::createDefaultAnnotationDriver`\n\nThe marker interface `Doctrine\\ORM\\Mapping\\Annotation` is deprecated as well.\nAll annotation/attribute classes implement\n`Doctrine\\ORM\\Mapping\\MappingAttribute` now.\n\n## Deprecated `Doctrine\\ORM\\Proxy\\Proxy` interface.\n\nUse `Doctrine\\Persistence\\Proxy` instead to check whether proxies are initialized.\n\n## Deprecated `Doctrine\\ORM\\Event\\LifecycleEventArgs` class.\n\nIt will be removed in 3.0. Use one of the dedicated event classes instead:\n\n* `Doctrine\\ORM\\Event\\PrePersistEventArgs`\n* `Doctrine\\ORM\\Event\\PreUpdateEventArgs`\n* `Doctrine\\ORM\\Event\\PreRemoveEventArgs`\n* `Doctrine\\ORM\\Event\\PostPersistEventArgs`\n* `Doctrine\\ORM\\Event\\PostUpdateEventArgs`\n* `Doctrine\\ORM\\Event\\PostRemoveEventArgs`\n* `Doctrine\\ORM\\Event\\PostLoadEventArgs`\n\n# Upgrade to 2.13\n\n## Deprecated `EntityManager::create()`\n\nThe constructor of `EntityManager` is now public and should be used instead of the `create()` method.\nHowever, the constructor expects a `Connection` while `create()` accepted an array with connection parameters.\nYou can pass that array to DBAL's `Doctrine\\DBAL\\DriverManager::getConnection()` method to bootstrap the\nconnection.\n\n## Deprecated `QueryBuilder` methods and constants.\n\n1. The `QueryBuilder::getState()` method has been deprecated as the builder state is an internal concern.\n2. Relying on the type of the query being built by using `QueryBuilder::getType()` has been deprecated.\n   If necessary, track the type of the query being built outside of the builder.\n\nThe following `QueryBuilder` constants related to the above methods have been deprecated:\n\n1. `SELECT`,\n2. `DELETE`,\n3. `UPDATE`,\n4. `STATE_DIRTY`,\n5. `STATE_CLEAN`.\n\n## Deprecated omitting only the alias argument for `QueryBuilder::update` and `QueryBuilder::delete`\n\nWhen building an UPDATE or DELETE query and when passing a class/type to the function, the alias argument must not be omitted.\n\n### Before\n\n```php\n$qb = $em->createQueryBuilder()\n    ->delete('User u')\n    ->where('u.id = :user_id')\n    ->setParameter('user_id', 1);\n```\n\n### After\n\n```php\n$qb = $em->createQueryBuilder()\n    ->delete('User', 'u')\n    ->where('u.id = :user_id')\n    ->setParameter('user_id', 1);\n```\n\n## Deprecated using the `IDENTITY` identifier strategy on platform that do not support identity columns\n\nIf identity columns are emulated with sequences on the platform you are using,\nyou should switch to the `SEQUENCE` strategy.\n\n## Deprecated passing `null` to `Doctrine\\ORM\\Query::setFirstResult()`\n\n`$query->setFirstResult(null);` is equivalent to `$query->setFirstResult(0)`.\n\n## Deprecated calling setters without arguments\n\nThe following methods will require an argument in 3.0. Pass `null` instead of\nomitting the argument.\n\n* `Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs::setFoundMetadata()`\n* `Doctrine\\ORM\\AbstractQuery::setHydrationCacheProfile()`\n* `Doctrine\\ORM\\AbstractQuery::setResultCache()`\n* `Doctrine\\ORM\\AbstractQuery::setResultCacheProfile()`\n\n## Deprecated passing invalid fetch modes to `AbstractQuery::setFetchMode()`\n\nCalling `AbstractQuery::setFetchMode()` with anything else than\n`Doctrine\\ORM\\Mapping::FETCH_EAGER` results in\n`Doctrine\\ORM\\Mapping::FETCH_LAZY` being used. Relying on that behavior is\ndeprecated and will result in an exception in 3.0.\n\n## Deprecated `getEntityManager()` in `Doctrine\\ORM\\Event\\OnClearEventArgs` and `Doctrine\\ORM\\Event\\*FlushEventArgs`\n\nThis method has been deprecated in:\n\n* `Doctrine\\ORM\\Event\\OnClearEventArgs`\n* `Doctrine\\ORM\\Event\\OnFlushEventArgs`\n* `Doctrine\\ORM\\Event\\PostFlushEventArgs`\n* `Doctrine\\ORM\\Event\\PreFlushEventArgs`\n\nIt will be removed in 3.0. Use `getObjectManager()` instead.\n\n## Prepare split of output walkers and tree walkers\n\nIn 3.0, `SqlWalker` and its child classes won't implement the `TreeWalker`\ninterface anymore. Relying on that inheritance is deprecated.\n\nThe following methods of the `TreeWalker` interface have been deprecated:\n\n* `setQueryComponent()`\n* `walkSelectClause()`\n* `walkFromClause()`\n* `walkFunction()`\n* `walkOrderByClause()`\n* `walkOrderByItem()`\n* `walkHavingClause()`\n* `walkJoin()`\n* `walkSelectExpression()`\n* `walkQuantifiedExpression()`\n* `walkSubselect()`\n* `walkSubselectFromClause()`\n* `walkSimpleSelectClause()`\n* `walkSimpleSelectExpression()`\n* `walkAggregateExpression()`\n* `walkGroupByClause()`\n* `walkGroupByItem()`\n* `walkDeleteClause()`\n* `walkUpdateClause()`\n* `walkUpdateItem()`\n* `walkWhereClause()`\n* `walkConditionalExpression()`\n* `walkConditionalTerm()`\n* `walkConditionalFactor()`\n* `walkConditionalPrimary()`\n* `walkExistsExpression()`\n* `walkCollectionMemberExpression()`\n* `walkEmptyCollectionComparisonExpression()`\n* `walkNullComparisonExpression()`\n* `walkInExpression()`\n* `walkInstanceOfExpression()`\n* `walkLiteral()`\n* `walkBetweenExpression()`\n* `walkLikeExpression()`\n* `walkStateFieldPathExpression()`\n* `walkComparisonExpression()`\n* `walkInputParameter()`\n* `walkArithmeticExpression()`\n* `walkArithmeticTerm()`\n* `walkStringPrimary()`\n* `walkArithmeticFactor()`\n* `walkSimpleArithmeticExpression()`\n* `walkPathExpression()`\n* `walkResultVariable()`\n* `getExecutor()`\n\nThe following changes have been made to the abstract `TreeWalkerAdapter` class:\n\n* All implementations of now-deprecated `TreeWalker` methods have been\n  deprecated as well.\n* The method `setQueryComponent()` will become protected in 3.0. Calling it\n  publicly is deprecated.\n* The method `_getQueryComponents()` is deprecated, call `getQueryComponents()`\n  instead.\n\nOn the `TreeWalkerChain` class, all implementations of now-deprecated\n`TreeWalker` methods have been deprecated as well.  However, `SqlWalker` is\nunaffected by those deprecations and will continue to implement all of those\nmethods.\n\n## Deprecated passing `null` to `Doctrine\\ORM\\Query::setDQL()`\n\nDoing `$query->setDQL(null);` achieves nothing.\n\n## Deprecated omitting second argument to `NamingStrategy::joinColumnName`\n\nWhen implementing `NamingStrategy`, it is deprecated to implement\n`joinColumnName()` with only one argument.\n\n### Before\n\n```php\n<?php\nclass MyStrategy implements NamingStrategy\n{\n    /**\n     * @param string $propertyName A property name.\n     */\n    public function joinColumnName($propertyName): string\n    {\n        // …\n    }\n}\n```\n\n### After\n\nFor backward-compatibility reasons, the parameter has to be optional, but can\nbe documented as guaranteed to be a `class-string`.\n\n```php\n<?php\nclass MyStrategy implements NamingStrategy\n{\n    /**\n     * @param string       $propertyName A property name.\n     * @param class-string $className\n     */\n    public function joinColumnName($propertyName, $className = null): string\n    {\n        // …\n    }\n}\n```\n\n## Deprecated methods related to named queries\n\nThe following methods have been deprecated:\n\n- `Doctrine\\ORM\\Query\\ResultSetMappingBuilder::addNamedNativeQueryMapping()`\n- `Doctrine\\ORM\\Query\\ResultSetMappingBuilder::addNamedNativeQueryResultClassMapping()`\n- `Doctrine\\ORM\\Query\\ResultSetMappingBuilder::addNamedNativeQueryResultSetMapping()`\n- `Doctrine\\ORM\\Query\\ResultSetMappingBuilder::addNamedNativeQueryEntityResultMapping()`\n\n## Deprecated classes related to Doctrine 1 and reverse engineering\n\nThe following classes have been deprecated:\n\n- `Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema`\n- `Doctrine\\ORM\\Tools\\DisconnectedClassMetadataFactory`\n\n## Deprecate `ClassMetadataInfo` usage\n\nIt is deprecated to pass `Doctrine\\ORM\\Mapping\\ClassMetadataInfo` instances\nthat are not also instances of `Doctrine\\ORM\\ClassMetadata` to the following\nmethods:\n\n- `Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder::__construct()`\n- `Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver::loadMetadataForClass()`\n- `Doctrine\\ORM\\Tools\\SchemaValidator::validateClass()`\n\n# Upgrade to 2.12\n\n## Deprecated the `doctrine` binary.\n\nThe documentation explains how the console tools can be bootstrapped for\nstandalone usage.\n\nThe method `ConsoleRunner::printCliConfigTemplate()` is deprecated because it\nwas only useful in the context of the `doctrine` binary.\n\n## Deprecate omitting `$class` argument to `ORMInvalidArgumentException::invalidIdentifierBindingEntity()`\n\nTo make it easier to identify understand the cause for that exception, it is\ndeprecated to omit the class name when calling\n`ORMInvalidArgumentException::invalidIdentifierBindingEntity()`.\n\n## Deprecate `Doctrine\\ORM\\Tools\\Console\\Helper\\EntityManagerHelper`\n\nUsing a console helper to provide the ORM's console commands with one or\nmultiple entity managers had been deprecated with 2.9 already. This leaves\nThe `EntityManagerHelper` class with no purpose which is why it is now\ndeprecated too. Applications that still rely on the `em` console helper, can\neasily recreate that class in their own codebase.\n\n## Deprecate custom repository classes that don't extend `EntityRepository`\n\nAlthough undocumented, it is currently possible to configure a custom repository\nclass that implements `ObjectRepository` but does not extend the\n`EntityRepository` base class.\n\nThis is now deprecated. Please extend `EntityRepository` instead.\n\n## Deprecated more APIs related to entity namespace aliases\n\n```diff\n-$config = $entityManager->getConfiguration();\n-$config->addEntityNamespace('CMS', 'My\\App\\Cms');\n+use My\\App\\Cms\\CmsUser;\n\n-$entityManager->getRepository('CMS:CmsUser');\n+$entityManager->getRepository(CmsUser::class);\n```\n\n## Deprecate `AttributeDriver::getReader()` and `AnnotationDriver::getReader()`\n\nThat method was inherited from the abstract `AnnotationDriver` class of\n`doctrine/persistence`, and does not seem to serve any purpose.\n\n## Un-deprecate `Doctrine\\ORM\\Proxy\\Proxy`\n\nBecause no forward-compatible new proxy solution had been implemented yet, the\ncurrent proxy mechanism is not considered deprecated anymore for the time\nbeing. This applies to the following interfaces/classes:\n\n* `Doctrine\\ORM\\Proxy\\Proxy`\n* `Doctrine\\ORM\\Proxy\\ProxyFactory`\n\nThese methods have been un-deprecated:\n\n* `Doctrine\\ORM\\Configuration::getAutoGenerateProxyClasses()`\n* `Doctrine\\ORM\\Configuration::getProxyDir()`\n* `Doctrine\\ORM\\Configuration::getProxyNamespace()`\n\nNote that the `Doctrine\\ORM\\Proxy\\Autoloader` remains deprecated and will be removed in 3.0.\n\n## Deprecate helper methods from `AbstractCollectionPersister`\n\nThe following protected methods of\n`Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister`\nare not in use anymore and will be removed.\n\n* `evictCollectionCache()`\n* `evictElementCache()`\n\n## Deprecate `Doctrine\\ORM\\Query\\TreeWalkerChainIterator`\n\nThis class won't have a replacement.\n\n## Deprecate `OnClearEventArgs::getEntityClass()` and `OnClearEventArgs::clearsAllEntities()`\n\nThese methods will be removed in 3.0 along with the ability to partially clear\nthe entity manager.\n\n## Deprecate `Doctrine\\ORM\\Configuration::newDefaultAnnotationDriver`\n\nThis functionality has been moved to the new `ORMSetup` class. Call\n`Doctrine\\ORM\\ORMSetup::createDefaultAnnotationDriver()` to create\na new annotation driver.\n\n## Deprecate `Doctrine\\ORM\\Tools\\Setup`\n\nIn our effort to migrate from Doctrine Cache to PSR-6, the `Setup` class which\naccepted a Doctrine Cache instance in each method has been deprecated.\n\nThe replacement is `Doctrine\\ORM\\ORMSetup` which accepts a PSR-6\ncache instead.\n\n## Deprecate `Doctrine\\ORM\\Cache\\MultiGetRegion`\n\nThe interface will be merged with `Doctrine\\ORM\\Cache\\Region` in 3.0.\n\n# Upgrade to 2.11\n\n## Rename `AbstractIdGenerator::generate()` to `generateId()`\n\nImplementations of `AbstractIdGenerator` have to override the method\n`generateId()` without calling the parent implementation. Not doing so is\ndeprecated. Calling `generate()` on any `AbstractIdGenerator` implementation\nis deprecated.\n\n## PSR-6-based second level cache\n\nThe second level cache has been reworked to consume a PSR-6 cache. Using a\nDoctrine Cache instance is deprecated.\n\n* `DefaultCacheFactory`: The constructor expects a PSR-6 cache item pool as\n  second argument now.\n* `DefaultMultiGetRegion`: This class is deprecated in favor of `DefaultRegion`.\n* `DefaultRegion`:\n  * The constructor expects a PSR-6 cache item pool as second argument now.\n  * The protected `$cache` property is deprecated.\n  * The properties `$name` and `$lifetime` as well as the constant\n   `REGION_KEY_SEPARATOR` and the method `getCacheEntryKey()` are flagged as\n   `@internal` now. They all will become `private` in 3.0.\n  * The method `getCache()` is deprecated without replacement.\n\n## Deprecated: `Doctrine\\ORM\\Mapping\\Driver\\PHPDriver`\n\nUse `StaticPHPDriver` instead when you want to programmatically configure\nentity metadata.\n\nYou can convert mappings with the `orm:convert-mapping` command or more simply\nin this case, `include` the metadata file from the `loadMetadata` static method\nused by the `StaticPHPDriver`.\n\n## Deprecated: `Setup::registerAutoloadDirectory()`\n\nUse Composer's autoloader instead.\n\n## Deprecated: `AbstractHydrator::hydrateRow()`\n\nFollowing the deprecation of the method `AbstractHydrator::iterate()`, the\nmethod `hydrateRow()` has been deprecated as well.\n\n## Deprecate cache settings inspection\n\nDoctrine does not provide its own cache implementation anymore and relies on\nthe PSR-6 standard instead. As a consequence, we cannot determine anymore\nwhether a given cache adapter is suitable for a production environment.\nBecause of that, functionality that aims to do so has been deprecated:\n\n* `Configuration::ensureProductionSettings()`\n* the `orm:ensure-production-settings` console command\n\n# Upgrade to 2.10\n\n## BC Break: `UnitOfWork` now relies on SPL object IDs, not hashes\n\nWhen calling the following methods, you are now supposed to use the result of\n`spl_object_id()`, and not `spl_object_hash()`:\n- `UnitOfWork::clearEntityChangeSet()`\n- `UnitOfWork::setOriginalEntityProperty()`\n\n## BC Break: Removed `TABLE` id generator strategy\n\nThe implementation was unfinished for 14 years.\nIt is now deprecated to rely on:\n- `Doctrine\\ORM\\Id\\TableGenerator`;\n- `Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_TABLE`;\n- `Doctrine\\ORM\\Mapping\\ClassMetadata::$tableGeneratorDefinition`;\n- or `Doctrine\\ORM\\Mapping\\ClassMetadata::isIdGeneratorTable()`.\n\n## New method `Doctrine\\ORM\\EntityManagerInterface#wrapInTransaction($func)`\n\nWorks the same as `Doctrine\\ORM\\EntityManagerInterface#transactional()` but returns any value returned from `$func` closure rather than just _non-empty value returned from the closure or true_.\n\nBecause of BC policy, the method does not exist on the interface yet. This is the example of safe usage:\n\n```php\nfunction foo(EntityManagerInterface $entityManager, callable $func) {\n    if (method_exists($entityManager, 'wrapInTransaction')) {\n        return $entityManager->wrapInTransaction($func);\n    }\n\n    return $entityManager->transactional($func);\n}\n```\n\n`Doctrine\\ORM\\EntityManagerInterface#transactional()` has been deprecated.\n\n## Minor BC BREAK: some exception methods have been removed\n\nThe following methods were not in use and are very unlikely to be used by\ndownstream packages or applications, and were consequently removed:\n\n- `ORMException::entityMissingForeignAssignedId`\n- `ORMException::entityMissingAssignedIdForField`\n- `ORMException::invalidFlushMode`\n\n## Deprecated: database-side UUID generation\n\n[DB-generated UUIDs are deprecated as of `doctrine/dbal` 2.8][DBAL deprecation].\nAs a consequence, using the `UUID` strategy for generating identifiers is deprecated as well.\nFurthermore, relying on the following classes and methods is deprecated:\n\n- `Doctrine\\ORM\\Id\\UuidGenerator`\n- `Doctrine\\ORM\\Mapping\\ClassMetadataInfo::isIdentifierUuid()`\n\n[DBAL deprecation]: https://github.com/doctrine/dbal/pull/3212\n\n## Minor BC BREAK: Custom hydrators and `toIterable()`\n\nThe type declaration of the `$stmt` parameter of `AbstractHydrator::toIterable()` has been removed. This change might\nbreak custom hydrator implementations that override this very method.\n\nOverriding this method is not recommended, which is why the method is documented as `@final` now.\n\n```diff\n- public function toIterable(ResultStatement $stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable\n+ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable\n```\n\n## Deprecated: Entity Namespace Aliases\n\nEntity namespace aliases are deprecated, use the magic ::class constant to abbreviate full class names\nin EntityManager, EntityRepository and DQL.\n\n```diff\n-  $entityManager->find('MyBundle:User', $id);\n+  $entityManager->find(User::class, $id);\n```\n\n# Upgrade to 2.9\n\n## Minor BC BREAK: Setup tool needs cache implementation\n\nWith the deprecation of doctrine/cache, the setup tool might no longer work as expected without a different cache\nimplementation. To work around this:\n* Install symfony/cache: `composer require symfony/cache`. This will keep previous behaviour without any changes\n* Instantiate caches yourself: to use a different cache implementation, pass a cache instance when calling any\n  configuration factory in the setup tool:\n  ```diff\n  - $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir);\n  + $cache = \\Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider::wrap($anyPsr6Implementation);\n  + $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);\n  ```\n* As a quick workaround, you can lock the doctrine/cache dependency to work around this: `composer require doctrine/cache ^1.11`.\n  Note that this is only recommended as a bandaid fix, as future versions of ORM will no longer work with doctrine/cache\n  1.11.\n\n## Deprecated: doctrine/cache for metadata caching\n\nThe `Doctrine\\ORM\\Configuration#setMetadataCacheImpl()` method is deprecated and should no longer be used. Please use\n`Doctrine\\ORM\\Configuration#setMetadataCache()` with any PSR-6 cache adapter instead.\n\n## Removed: flushing metadata cache\n\nTo support PSR-6 caches, the `--flush` option for the `orm:clear-cache:metadata` command is ignored. Metadata cache is\nnow always cleared regardless of the cache adapter being used.\n\n# Upgrade to 2.8\n\n## Minor BC BREAK: Failed commit now throw OptimisticLockException\n\nMethod `Doctrine\\ORM\\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false\nsince `Doctrine\\DBAL\\Connection#commit()` signature changed from returning void to boolean\n\n## Deprecated: `Doctrine\\ORM\\AbstractQuery#iterate()`\n\nThe method `Doctrine\\ORM\\AbstractQuery#iterate()` is deprecated in favor of `Doctrine\\ORM\\AbstractQuery#toIterable()`.\nNote that `toIterable()` yields results of the query, unlike `iterate()` which yielded each result wrapped into an array.\n\n# Upgrade to 2.7\n\n## Added `Doctrine\\ORM\\AbstractQuery#enableResultCache()` and `Doctrine\\ORM\\AbstractQuery#disableResultCache()` methods\n\nMethod `Doctrine\\ORM\\AbstractQuery#useResultCache()` which could be used for both enabling and disabling the cache\n(depending on passed flag) was split into two.\n\n## Minor BC BREAK: paginator output walkers aren't be called anymore on sub-queries for queries without max results\n\nTo optimize DB interaction, `Doctrine\\ORM\\Tools\\Pagination\\Paginator` no longer fetches identifiers to be able to\nperform the pagination with join collections when max results isn't set in the query.\n\n## Minor BC BREAK: tables filtered with `schema_filter` are no longer created\n\nWhen generating schema diffs, if a source table is filtered out by a `schema_filter` expression, then a `CREATE TABLE` was\nalways generated, even if the table already existed. This has been changed in this release and the table will no longer\nbe created.\n\n## Deprecated number unaware `Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy`\n\nIn the last patch of the `v2.6.x` series, we fixed a bug that was not converting names properly when they had numbers\n(e.g.: `base64Encoded` was wrongly converted to `base64encoded` instead of `base64_encoded`).\n\nIn order to not break BC we've introduced a way to enable the fixed behavior using a boolean constructor argument. This\nargument will be removed in 3.0 and the default behavior will be the fixed one.\n\n## Deprecated: `Doctrine\\ORM\\AbstractQuery#useResultCache()`\n\nMethod `Doctrine\\ORM\\AbstractQuery#useResultCache()` is deprecated because it is split into `enableResultCache()`\nand `disableResultCache()`. It will be removed in 3.0.\n\n## Deprecated code generators and related console commands\n\nThese console commands have been deprecated:\n\n * `orm:convert-mapping`\n * `orm:generate:entities`\n * `orm:generate-repositories`\n\nThese classes have been deprecated:\n\n * `Doctrine\\ORM\\Tools\\EntityGenerator`\n * `Doctrine\\ORM\\Tools\\EntityRepositoryGenerator`\n\nWhole Doctrine\\ORM\\Tools\\Export namespace with all its members have been deprecated as well.\n\n## Deprecated `Doctrine\\ORM\\Proxy\\Proxy` marker interface\n\nProxy objects in Doctrine ORM 3.0 will no longer implement `Doctrine\\ORM\\Proxy\\Proxy` nor\n`Doctrine\\Persistence\\Proxy`: instead, they implement\n`ProxyManager\\Proxy\\GhostObjectInterface`.\n\nThese related classes have been deprecated:\n\n * `Doctrine\\ORM\\Proxy\\ProxyFactory`\n * `Doctrine\\ORM\\Proxy\\Autoloader` - we suggest using the composer autoloader instead\n\nThese methods have been deprecated:\n\n * `Doctrine\\ORM\\Configuration#getAutoGenerateProxyClasses()`\n * `Doctrine\\ORM\\Configuration#getProxyDir()`\n * `Doctrine\\ORM\\Configuration#getProxyNamespace()`\n\n## Deprecated `Doctrine\\ORM\\Version`\n\nThe `Doctrine\\ORM\\Version` class is now deprecated and will be removed in Doctrine ORM 3.0:\nplease refrain from checking the ORM version at runtime or use Composer's [runtime API](https://getcomposer.org/doc/07-runtime.md#knowing-whether-package-x-is-installed-in-version-y).\n\n## Deprecated `EntityManager#merge()` method\n\nMerge semantics was a poor fit for the PHP \"share-nothing\" architecture.\nIn addition to that, merging caused multiple issues with data integrity\nin the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.\n\nThe following API methods were therefore deprecated:\n\n* `EntityManager#merge()`\n* `UnitOfWork#merge()`\n\nAn alternative to `EntityManager#merge()` will not be provided by ORM 3.0, since the merging\nsemantics should be part of the business domain rather than the persistence domain of an\napplication. If your application relies heavily on CRUD-alike interactions and/or `PATCH`\nrestful operations, you should look at alternatives such as [JMSSerializer](https://github.com/schmittjoh/serializer).\n\n## Extending `EntityManager` is deprecated\n\nFinal keyword will be added to the `EntityManager::class` in Doctrine ORM 3.0 in order to ensure that EntityManager\n is not used as valid extension point. Valid extension point should be EntityManagerInterface.\n\n## Deprecated `EntityManager#clear($entityName)`\n\nIf your code relies on clearing a single entity type via `EntityManager#clear($entityName)`,\nthe signature has been changed to `EntityManager#clear()`.\n\nThe main reason is that partial clears caused multiple issues with data integrity\nin the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.\n\n## Deprecated `EntityManager#flush($entity)` and `EntityManager#flush($entities)`\n\nIf your code relies on single entity flushing optimisations via\n`EntityManager#flush($entity)`, the signature has been changed to\n`EntityManager#flush()`.\n\nSaid API was affected by multiple data integrity bugs due to the fact\nthat change tracking was being restricted upon a subset of the managed\nentities. The ORM cannot support committing subsets of the managed\nentities while also guaranteeing data integrity, therefore this\nutility was removed.\n\nThe `flush()` semantics will remain the same, but the change tracking will be performed\non all entities managed by the unit of work, and not just on the provided\n`$entity` or `$entities`, as the parameter is now completely ignored.\n\nThe same applies to `UnitOfWork#commit($entity)`, which will simply be\n`UnitOfWork#commit()`.\n\nIf you would still like to perform batching operations over small `UnitOfWork`\ninstances, it is suggested to follow these paths instead:\n\n * eagerly use `EntityManager#clear()` in conjunction with a specific second level\n   cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/second-level-cache.html)\n * use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/change-tracking-policies.html)\n\n## Deprecated `YAML` mapping drivers.\n\nIf your code relies on `YamlDriver`  or `SimpleYamlDriver`, you **MUST** change to\nannotation or XML drivers instead.\n\n## Deprecated: `Doctrine\\ORM\\EntityManagerInterface#copy()`\n\nMethod `Doctrine\\ORM\\EntityManagerInterface#copy()` never got its implementation and is deprecated.\nIt will be removed in 3.0.\n\n# Upgrade to 2.6\n\n## Added `Doctrine\\ORM\\EntityRepository::count()` method\n\n`Doctrine\\ORM\\EntityRepository::count()` has been added. This new method has different\nsignature than `Countable::count()` (required parameter) and therefore are not compatible.\nIf your repository implemented the `Countable` interface, you will have to use\n`$repository->count([])` instead and not implement `Countable` interface anymore.\n\n## Minor BC BREAK: `Doctrine\\ORM\\Tools\\Console\\ConsoleRunner` is now final\n\nSince it's just an utilitarian class and should not be inherited.\n\n## Minor BC BREAK: removed `Doctrine\\ORM\\Query\\QueryException::associationPathInverseSideNotSupported()`\n\nMethod `Doctrine\\ORM\\Query\\QueryException::associationPathInverseSideNotSupported()`\nnow has a required parameter `$pathExpr`.\n\n## Minor BC BREAK: removed `Doctrine\\ORM\\Query\\Parser#isInternalFunction()`\n\nMethod `Doctrine\\ORM\\Query\\Parser#isInternalFunction()` was removed because\nthe distinction between internal function and user defined DQL was removed.\n[#6500](https://github.com/doctrine/orm/pull/6500)\n\n## Minor BC BREAK: removed `Doctrine\\ORM\\ORMException#overwriteInternalDQLFunctionNotAllowed()`\n\nMethod `Doctrine\\ORM\\Query\\Parser#overwriteInternalDQLFunctionNotAllowed()` was\nremoved because of the choice to allow users to overwrite internal functions, ie\n`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/orm/pull/6500)\n\n## PHP 7.1 is now required\n\nDoctrine 2.6 now requires PHP 7.1 or newer.\n\nAs a consequence, automatic cache setup in Doctrine\\ORM\\Tools\\Setup::create*Configuration() was changed:\n- APCu extension (ext-apcu) will now be used instead of abandoned APC (ext-apc).\n- Memcached extension (ext-memcached) will be used instead of obsolete Memcache (ext-memcache).\n- XCache support was dropped as it doesn't work with PHP 7.\n\n# Upgrade to 2.5\n\n## Minor BC BREAK: removed `Doctrine\\ORM\\Query\\SqlWalker#walkCaseExpression()`\n\nMethod `Doctrine\\ORM\\Query\\SqlWalker#walkCaseExpression()` was unused and part\nof the internal API of the ORM, so it was removed. [#5600](https://github.com/doctrine/orm/pull/5600).\n\n## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`\n\nAs `$className` parameter was not used in the method, it was safely removed.\n\n## Minor BC BREAK: query cache key time is now a float\n\nAs of 2.5.5, the `QueryCacheEntry#time` property will contain a float value\ninstead of an integer in order to have more precision and also to be consistent\nwith the `TimestampCacheEntry#time`.\n\n## Minor BC BREAK: discriminator map must now include all non-transient classes\n\nIt is now required that you declare the root of an inheritance in the\ndiscriminator map.\n\nWhen declaring an inheritance map, it was previously possible to skip the root\nof the inheritance in the discriminator map. This was actually a validation\nmistake by Doctrine2 and led to problems when trying to persist instances of\nthat class.\n\nIf you don't plan to persist instances some classes in your inheritance, then\neither:\n\n - make those classes `abstract`\n - map those classes as `MappedSuperclass`\n\n## Minor BC BREAK: ``EntityManagerInterface`` instead of ``EntityManager`` in type-hints\n\nAs of 2.5, classes requiring the ``EntityManager`` in any method signature will now require\nan ``EntityManagerInterface`` instead.\nIf you are extending any of the following classes, then you need to check following\nsignatures:\n\n- ``Doctrine\\ORM\\Tools\\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)``\n- ``Doctrine\\ORM\\Mapping\\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)``\n\n## Minor BC BREAK: Custom Hydrators API change\n\nAs of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of\nAPI, and now provides you a clean API for column information through the method\n`hydrateColumnInfo($column)`.\nCache variable being passed around by reference is no longer needed since\nHydrators are per query instantiated since Doctrine 2.4.\n\n## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach\n\nWhenever ``EntityManager#clear()`` method gets called with a given entity class\nname, until 2.4, it was only detaching the specific requested entity.\nAs of 2.5, ``EntityManager`` will follow configured cascades, providing a better\nmemory management since associations will be garbage collected, optimizing\nresources consumption on long running jobs.\n\n## BC BREAK: NamingStrategy interface changes\n\n1. A new method ``embeddedFieldToColumnName($propertyName, $embeddedColumnName)``\n\nThis method generates the column name for fields of embedded objects. If you implement your custom NamingStrategy, you\nnow also need to implement this new method.\n\n2. A change to method ``joinColumnName()`` to include the $className\n\n## Updates on entities scheduled for deletion are no longer processed\n\nIn Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would\nproduce an UPDATE statement to be executed right before the DELETE statement. The entity in question\nwas therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate``\nlisteners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo\nthe scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both\n``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset\ncalculation logic is optimized away.\n\n## Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures\n\nA misconception concerning default lock mode values in method signatures lead to unexpected behaviour\nin SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the\nmethod signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related\nqueries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)``\ntable hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED\ninstead of the default READ COMMITTED transaction isolation level.\nTherefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell\nDoctrine whether to add table lock hints to queries by intention or not. To achieve this, the following\nmethod signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``:\n\n- ``Doctrine\\ORM\\Cache\\Persister\\AbstractEntityPersister#getSelectSQL()``\n- ``Doctrine\\ORM\\Cache\\Persister\\AbstractEntityPersister#load()``\n- ``Doctrine\\ORM\\Cache\\Persister\\AbstractEntityPersister#refresh()``\n- ``Doctrine\\ORM\\Decorator\\EntityManagerDecorator#find()``\n- ``Doctrine\\ORM\\EntityManager#find()``\n- ``Doctrine\\ORM\\EntityRepository#find()``\n- ``Doctrine\\ORM\\Persisters\\BasicEntityPersister#getSelectSQL()``\n- ``Doctrine\\ORM\\Persisters\\BasicEntityPersister#load()``\n- ``Doctrine\\ORM\\Persisters\\BasicEntityPersister#refresh()``\n- ``Doctrine\\ORM\\Persisters\\EntityPersister#getSelectSQL()``\n- ``Doctrine\\ORM\\Persisters\\EntityPersister#load()``\n- ``Doctrine\\ORM\\Persisters\\EntityPersister#refresh()``\n- ``Doctrine\\ORM\\Persisters\\JoinedSubclassPersister#getSelectSQL()``\n\nYou should update signatures for these methods if you have subclassed one of the above classes.\nPlease also check the calling code of these methods in your application and update if necessary.\n\n**Note:**\nThis in fact is really a minor BC BREAK and should not have any affect on database vendors\nother than SQL Server because it is the only one that supports and therefore cares about\n``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM.\n\n## Minor BC BREAK: `__clone` method not called anymore when entities are instantiated via metadata API\n\nAs of PHP 5.6, instantiation of new entities is deferred to the\n[`doctrine/instantiator`](https://github.com/doctrine/instantiator) library, which will avoid calling `__clone`\nor any public API on instantiated objects.\n\n## BC BREAK: `Doctrine\\ORM\\Repository\\DefaultRepositoryFactory` is now `final`\n\nPlease implement the `Doctrine\\ORM\\Repository\\RepositoryFactory` interface instead of extending\nthe `Doctrine\\ORM\\Repository\\DefaultRepositoryFactory`.\n\n## BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields\n\nWhen executing DQL queries with new object expressions, instead of returning DTOs numerically indexes, it will now respect user provided aliases. Consider the following query:\n\n    SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true\n\nPreviously, your result would be similar to this:\n\n    array(\n        0=>array(\n            0=>{UserDTO object},\n            1=>{AddressDTO object},\n            2=>{u.id scalar},\n            3=>{u.name scalar},\n            4=>{a.street scalar},\n            5=>{a.postalCode scalar},\n            'addressId'=>{a.id scalar},\n        ),\n        ...\n    )\n\nFrom now on, the resultset will look like this:\n\n    array(\n        0=>array(\n            'user'=>{UserDTO object},\n            'address'=>{AddressDTO object},\n            'addressId'=>{a.id scalar}\n        ),\n        ...\n    )\n\n## Minor BC BREAK: added second parameter $indexBy in EntityRepository#createQueryBuilder method signature\n\nAdded way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()\n\n# Upgrade to 2.4\n\n## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()\n\nIn Doctrine 2.3 it was possible to use the new ``matching($criteria)``\nfunctionality by adding constraints for assocations based on ID:\n\n    Criteria::expr()->eq('association', $assocation->getId());\n\nThis functionality does not work on InMemory collections however, because\nin memory criteria compares object values based on reference.\nAs of 2.4 the above code will throw an exception. You need to change\noffending code to pass the ``$assocation`` reference directly:\n\n    Criteria::expr()->eq('association', $assocation);\n\n## Composer is now the default autoloader\n\nThe test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated.\nSupport for GIT submodules is removed.\n\n## OnFlush and PostFlush event always called\n\nBefore 2.4 the postFlush and onFlush events were only called when there were\nactually entities that changed. Now these events are called no matter if there\nare entities in the UoW or changes are found.\n\n## Parenthesis are now considered in arithmetic expression\n\nBefore 2.4 parenthesis are not considered in arithmetic primary expression.\nThat's conceptually wrong, since it might result in wrong values. For example:\n\nThe DQL:\n\n    SELECT 100 / ( 2 * 2 ) FROM MyEntity\n\nBefore 2.4 it generates the SQL:\n\n    SELECT 100 / 2 * 2 FROM my_entity\n\nNow parenthesis are considered, the previous DQL will generate:\n\n    SELECT 100 / (2 * 2) FROM my_entity\n\n# Upgrade to 2.3\n\n## Auto Discriminator Map breaks userland implementations with Listener\n\nThe new feature to detect discriminator maps automatically when none\nare provided breaks userland implementations doing this with a\nlistener in ``loadClassMetadata`` event.\n\n## EntityManager#find() not calls EntityRepository#find() anymore\n\nPrevious to 2.3, calling ``EntityManager#find()`` would be delegated to\n``EntityRepository#find()``.  This has lead to some unexpected behavior in the\ncore of Doctrine when people have overwritten the find method in their\nrepositories. That is why this behavior has been reversed in 2.3, and\n``EntityRepository#find()`` calls ``EntityManager#find()`` instead.\n\n## EntityGenerator add*() method generation\n\nWhen generating an add*() method for a collection the EntityGenerator will now not\nuse the Type-Hint to get the singular for the collection name, but use the field-name\nand strip a trailing \"s\" character if there is one.\n\n## Merge copies non persisted properties too\n\nWhen merging an entity in UoW not only mapped properties are copied, but also others.\n\n## Query, QueryBuilder and NativeQuery parameters *BC break*\n\nFrom now on, parameters in queries is an ArrayCollection instead of a simple array.\nThis affects heavily the usage of setParameters(), because it will not append anymore\nparameters to query, but will actually override the already defined ones.\nWhenever you are retrieving a parameter (ie. $query->getParameter(1)), you will\nreceive an instance of Query\\Parameter, which contains the methods \"getName\",\n\"getValue\" and \"getType\". Parameters are also only converted to when necessary, and\nnot when they are set.\n\nAlso, related functions were affected:\n\n* execute($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance\n* iterate($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance\n* setParameters($parameters) the argument $parameters can be either an key=>value array or an ArrayCollection instance\n* getParameters() now returns ArrayCollection instead of array\n* getParameter($key) now returns Parameter instance instead of parameter value\n\n## Query TreeWalker method renamed\n\nInternal changes were made to DQL and SQL generation. If you have implemented your own TreeWalker,\nyou probably need to update it. The method walkJoinVariableDeclaration is now named walkJoin.\n\n## New methods in TreeWalker interface *BC break*\n\nTwo methods getQueryComponents() and setQueryComponent() were added to the TreeWalker interface and all its implementations\nincluding TreeWalkerAdapter, TreeWalkerChain and SqlWalker. If you have your own implementation not inheriting from one of the\nabove you must implement these new methods.\n\n## Metadata Drivers\n\nMetadata drivers have been rewritten to reuse code from `Doctrine\\Persistence`. Anyone who is using the\n`Doctrine\\ORM\\Mapping\\Driver\\Driver` interface should instead refer to\n`Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver`. Same applies to\n`Doctrine\\ORM\\Mapping\\Driver\\AbstractFileDriver`: you should now refer to\n`Doctrine\\Persistence\\Mapping\\Driver\\FileDriver`.\n\nAlso, following mapping drivers have been deprecated, please use their replacements in Doctrine\\Common as listed:\n\n *  `Doctrine\\ORM\\Mapping\\Driver\\DriverChain`       => `Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain`\n *  `Doctrine\\ORM\\Mapping\\Driver\\PHPDriver`         => `Doctrine\\Persistence\\Mapping\\Driver\\PHPDriver`\n *  `Doctrine\\ORM\\Mapping\\Driver\\StaticPHPDriver`   => `Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver`\n\n# Upgrade to 2.2\n\n## ResultCache implementation rewritten\n\nThe result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery\nanymore. This means that for result cached queries the hydration will now always be performed again, regardless of\nthe hydration mode. Affected areas are:\n\n1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork\n   leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.\n2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.\n\nThe API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now\ndeprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\\DBAL\\Cache\\QueryCacheProfile`\ninstance with access to result cache driver, lifetime and cache key.\n\n\n## EntityManager#getPartialReference() creates read-only entity\n\nEntities returned from EntityManager#getPartialReference() are now marked as read-only if they\nhaven't been in the identity map before. This means objects of this kind never lead to changes\nin the UnitOfWork.\n\n\n## Fields omitted in a partial DQL query or a native query are never updated\n\nFields of an entity that are not returned from a partial DQL Query or native SQL query\nwill never be updated through an UPDATE statement.\n\n\n## Removed support for onUpdate in @JoinColumn\n\nThe onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.\n\n\n## Changes in Annotation Handling\n\nThere have been some changes to the annotation handling in Common 2.2 again, that affect how people with old configurations\nfrom 2.0 have to configure the annotation driver if they don't use `Configuration::newDefaultAnnotationDriver()`:\n\n    // Register the ORM Annotations in the AnnotationRegistry\n    AnnotationRegistry::registerFile('path/to/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');\n\n    $reader = new \\Doctrine\\Common\\Annotations\\SimpleAnnotationReader();\n    $reader->addNamespace('Doctrine\\ORM\\Mapping');\n    $reader = new \\Doctrine\\Common\\Annotations\\CachedReader($reader, new ArrayCache());\n\n    $driver = new AnnotationDriver($reader, (array)$paths);\n\n    $config->setMetadataDriverImpl($driver);\n\n\n## Scalar mappings can now be omitted from DQL result\n\nYou are now allowed to mark scalar SELECT expressions as HIDDEN and they are not hydrated anymore.\nExample:\n\nSELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10\n\nYour result will be a collection of Users, and not an array with key 0 as User object instance and \"numArticles\" as the number of articles per user\n\n\n## Map entities as scalars in DQL result\n\nWhen hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance.\nYou are now allowed to alias this, providing more flexibility for you code.\nExample:\n\nSELECT u AS user FROM User u\n\nWill now return a collection of arrays with index \"user\" pointing to the User object instance.\n\n\n## Performance optimizations\n\nThousands of lines were completely reviewed and optimized for best performance.\nRemoved redundancy and improved code readability made now internal Doctrine code easier to understand.\nAlso, Doctrine 2.2 now is around 10-15% faster than 2.1.\n\n## EntityManager#find(null)\n\nPreviously EntityManager#find(null) returned null. It now throws an exception.\n\n# Upgrade to 2.1\n\n## Interface for EntityRepository\n\nThe EntityRepository now has an interface Doctrine\\Persistence\\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.\n\n## AnnotationReader changes\n\nThe annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way:\n\n    // new call to the AnnotationRegistry\n    \\Doctrine\\Common\\Annotations\\AnnotationRegistry::registerFile('/doctrine-src/src/Mapping/Driver/DoctrineAnnotations.php');\n\n    $reader = new \\Doctrine\\Common\\Annotations\\AnnotationReader();\n    $reader->setDefaultAnnotationNamespace('Doctrine\\ORM\\Mapping\\\\');\n    // new code necessary starting here\n    $reader->setIgnoreNotImportedAnnotations(true);\n    $reader->setEnableParsePhpImports(false);\n    $reader = new \\Doctrine\\Common\\Annotations\\CachedReader(\n        new \\Doctrine\\Common\\Annotations\\IndexedReader($reader), new ArrayCache()\n    );\n\nThis is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory.\n\n# Update from 2.0-BETA3 to 2.0-BETA4\n\n## XML Driver <change-tracking-policy /> element demoted to attribute\n\nWe changed how the XML Driver allows to define the change-tracking-policy. The working case is now:\n\n    <entity change-tracking-policy=\"DEFERRED_IMPLICT\" />\n\n# Update from 2.0-BETA2 to 2.0-BETA3\n\n## Serialization of Uninitialized Proxies\n\nAs of Beta3 you can now serialize uninitialized proxies, an exception will only be thrown when\ntrying to access methods on the unserialized proxy as long as it has not been re-attached to the\nEntityManager using `EntityManager#merge()`. See this example:\n\n    $proxy = $em->getReference('User', 1);\n\n    $serializedProxy = serialize($proxy);\n    $detachedProxy = unserialized($serializedProxy);\n\n    echo $em->contains($detachedProxy); // FALSE\n\n    try {\n        $detachedProxy->getId(); // uninitialized detached proxy\n    } catch(Exception $e) {\n\n    }\n    $attachedProxy = $em->merge($detachedProxy);\n    echo $attackedProxy->getId(); // works!\n\n## Changed SQL implementation of Postgres and Oracle DateTime types\n\nThe DBAL Type \"datetime\" included the Timezone Offset in both Postgres and Oracle. As of this version they are now\ngenerated without Timezone (TIMESTAMP WITHOUT TIME ZONE instead of TIMESTAMP WITH TIME ZONE).\nSee [this comment to Ticket DBAL-22](http://www.doctrine-project.org/jira/browse/DBAL-22?focusedCommentId=13396&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_13396)\nfor more details as well as migration issues for PostgreSQL and Oracle.\n\nBoth Postgres and Oracle will throw Exceptions during hydration of Objects with \"DateTime\" fields unless migration steps are taken!\n\n## Removed multi-dot/deep-path expressions in DQL\n\nThe support for implicit joins in DQL through the multi-dot/Deep Path Expressions\nwas dropped. For example:\n\n    SELECT u FROM User u WHERE u.group.name = ?1\n\nSee the \"u.group.id\" here is using multi dots (deep expression) to walk\nthrough the graph of objects and properties. Internally the DQL parser\nwould rewrite these queries to:\n\n    SELECT u FROM User u JOIN u.group g WHERE g.name = ?1\n\nThis explicit notation will be the only supported notation as of now. The internal\nhandling of multi-dots in the DQL Parser was very complex, error prone in edge cases\nand required special treatment for several features we added. Additionally\nit had edge cases that could not be solved without making the DQL Parser\neven much more complex. For this reason we will drop the support for the\ndeep path expressions to increase maintainability and overall performance\nof the DQL parsing process. This will benefit any DQL query being parsed,\neven those not using deep path expressions.\n\nNote that the generated SQL of both notations is exactly the same! You\ndon't loose anything through this.\n\n## Default Allocation Size for Sequences\n\nThe default allocation size for sequences has been changed from 10 to 1. This step was made\nto not cause confusion with users and also because it is partly some kind of premature optimization.\n\n# Update from 2.0-BETA1 to 2.0-BETA2\n\nThere are no backwards incompatible changes in this release.\n\n# Upgrade from 2.0-ALPHA4 to 2.0-BETA1\n\n## EntityRepository deprecates access to protected variables\n\nInstead of accessing protected variables for the EntityManager in\na custom EntityRepository it is now required to use the getter methods\nfor all the three instance variables:\n\n* `$this->_em` now accessible through `$this->getEntityManager()`\n* `$this->_class` now accessible through `$this->getClassMetadata()`\n* `$this->_entityName` now accessible through `$this->getEntityName()`\n\nImportant: For Beta 2 the protected visibility of these three properties will be\nchanged to private!\n\n## Console migrated to Symfony Console\n\nThe Doctrine CLI has been replaced by Symfony Console Configuration\n\nInstead of having to specify:\n\n    [php]\n    $cliConfig = new CliConfiguration();\n    $cliConfig->setAttribute('em', $entityManager);\n\nYou now have to configure the script like:\n\n    [php]\n    $helperSet = new \\Symfony\\Components\\Console\\Helper\\HelperSet(array(\n        'db' => new \\Doctrine\\DBAL\\Tools\\Console\\Helper\\ConnectionHelper($em->getConnection()),\n        'em' => new \\Doctrine\\ORM\\Tools\\Console\\Helper\\EntityManagerHelper($em)\n    ));\n\n## Console: No need for Mapping Paths anymore\n\nIn previous versions you had to specify the --from and --from-path options\nto show where your mapping paths are from the console. However this information\nis already known from the Mapping Driver configuration, so the requirement\nfor this options were dropped.\n\nInstead for each console command all the entities are loaded and to\nrestrict the operation to one or more sub-groups you can use the --filter flag.\n\n## AnnotationDriver is not a default mapping driver anymore\n\nIn conjunction with the recent changes to Console we realized that the\nannotations driver being a default metadata driver lead to lots of glue\ncode in the console components to detect where entities lie and how to load\nthem for batch updates like SchemaTool and other commands. However the\nannotations driver being a default driver does not really help that much\nanyways.\n\nTherefore we decided to break backwards compatibility in this issue and drop\nthe support for Annotations as Default Driver and require our users to\nspecify the driver explicitly (which allows us to ask for the path to all\nentities).\n\nIf you are using the annotations metadata driver as default driver, you\nhave to add the following lines to your bootstrap code:\n\n    $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__.\"/Entities\"));\n    $config->setMetadataDriverImpl($driverImpl);\n\nYou have to specify the path to your entities as either string of a single\npath or array of multiple paths\nto your entities. This information will be used by all console commands to\naccess all entities.\n\nXml and Yaml Drivers work as before!\n\n\n## New inversedBy attribute\n\nIt is now *mandatory* that the owning side of a bidirectional association specifies the\n'inversedBy' attribute that points to the name of the field on the inverse side that completes\nthe association. Example:\n\n    [php]\n    // BEFORE (ALPHA4 AND EARLIER)\n    class User\n    {\n        //...\n        /** @OneToOne(targetEntity=\"Address\", mappedBy=\"user\") */\n        private $address;\n        //...\n    }\n    class Address\n    {\n        //...\n        /** @OneToOne(targetEntity=\"User\") */\n        private $user;\n        //...\n    }\n\n    // SINCE BETA1\n    // User class DOES NOT CHANGE\n    class Address\n    {\n        //...\n        /** @OneToOne(targetEntity=\"User\", inversedBy=\"address\") */\n        private $user;\n        //...\n    }\n\nThus, the inversedBy attribute is the counterpart to the mappedBy attribute. This change\nwas necessary to enable some simplifications and further performance improvements. We\napologize for the inconvenience.\n\n## Default Property for Field Mappings\n\nThe \"default\" option for database column defaults has been removed. If desired, database column defaults can\nbe implemented by using the columnDefinition attribute of the @Column annotation (or the appropriate XML and YAML equivalents).\nPrefer PHP default values, if possible.\n\n## Selecting Partial Objects\n\nQuerying for partial objects now has a new syntax. The old syntax to query for partial objects\nnow has a different meaning. This is best illustrated by an example. If you previously\nhad a DQL query like this:\n\n    [sql]\n    SELECT u.id, u.name FROM User u\n\nSince BETA1, simple state field path expressions in the select clause are used to select\nobject fields as plain scalar values (something that was not possible before).\nTo achieve the same result as previously (that is, a partial object with only id and name populated)\nyou need to use the following, explicit syntax:\n\n    [sql]\n    SELECT PARTIAL u.{id,name} FROM User u\n\n## XML Mapping Driver\n\nThe 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e.\nNONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED\n\n## YAML Mapping Driver\n\nThe way to specify lifecycle callbacks in YAML Mapping driver was changed to allow for multiple callbacks\nper event. The Old syntax ways:\n\n    [yaml]\n    lifecycleCallbacks:\n      doStuffOnPrePersist: prePersist\n      doStuffOnPostPersist: postPersist\n\nThe new syntax is:\n\n    [yaml]\n    lifecycleCallbacks:\n      prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]\n      postPersist: [ doStuffOnPostPersist ]\n\n## PreUpdate Event Listeners\n\nEvent Listeners listening to the 'preUpdate' event can only affect the primitive values of entity changesets\nby using the API on the `PreUpdateEventArgs` instance passed to the preUpdate listener method. Any changes\nto the state of the entitys properties won't affect the database UPDATE statement anymore. This gives drastic\nperformance benefits for the preUpdate event.\n\n## Collection API\n\nThe Collection interface in the Common package has been updated with some missing methods\nthat were present only on the default implementation, ArrayCollection. Custom collection\nimplementations need to be updated to adhere to the updated interface.\n\n# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4\n\n## CLI Controller changes\n\nCLI main object changed its name and namespace. Renamed from Doctrine\\ORM\\Tools\\Cli to Doctrine\\Common\\Cli\\CliController.\nDoctrine\\Common\\Cli\\CliController now only deals with namespaces. Ready to go, Core, Dbal and Orm are available and you can subscribe new tasks by retrieving the namespace and including new task. Example:\n\n    [php]\n    $cli->getNamespace('Core')->addTask('my-example', '\\MyProject\\Tools\\Cli\\Tasks\\MyExampleTask');\n\n\n## CLI Tasks documentation\n\nTasks have implemented a new way to build documentation. Although it is still possible to define the help manually by extending the basicHelp and extendedHelp, they are now optional.\nWith new required method AbstractTask::buildDocumentation, its implementation defines the TaskDocumentation instance (accessible through AbstractTask::getDocumentation()), basicHelp and extendedHelp are now not necessary to be implemented.\n\n## Changes in Method Signatures\n\n    * A bunch of Methods on both Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Schema\\AbstractSchemaManager\n      have changed quite significantly by adopting the new Schema instance objects.\n\n## Renamed Methods\n\n    * Doctrine\\ORM\\AbstractQuery::setExpireResultCache() -> expireResultCache()\n    * Doctrine\\ORM\\Query::setExpireQueryCache() -> expireQueryCache()\n\n## SchemaTool Changes\n\n    * \"doctrine schema-tool --drop\" now always drops the complete database instead of\n    only those tables defined by the current database model. The previous method had\n    problems when foreign keys of orphaned tables pointed to tables that were scheduled\n    for deletion.\n    * Use \"doctrine schema-tool --update\" to get a save incremental update for your\n    database schema without deleting any unused tables, sequences or foreign keys.\n    * Use \"doctrine schema-tool --complete-update\" to do a full incremental update of\n    your schema.\n# Upgrade from 2.0-ALPHA2 to 2.0-ALPHA3\n\nThis section details the changes made to Doctrine 2.0-ALPHA3 to make it easier for you\nto upgrade your projects to use this version.\n\n## CLI Changes\n\nThe $args variable used in the cli-config.php for configuring the Doctrine CLI has been renamed to $globalArguments.\n\n## Proxy class changes\n\nYou are now required to make supply some minimalist configuration with regards to proxy objects. That involves 2 new configuration options. First, the directory where generated proxy classes should be placed needs to be specified. Secondly, you need to configure the namespace used for proxy classes. The following snippet shows an example:\n\n    [php]\n    // step 1: configure directory for proxy classes\n    // $config instanceof Doctrine\\ORM\\Configuration\n    $config->setProxyDir('/path/to/myproject/lib/MyProject/Generated/Proxies');\n    $config->setProxyNamespace('MyProject\\Generated\\Proxies');\n\nNote that proxy classes behave exactly like any other classes when it comes to class loading. Therefore you need to make sure the proxy classes can be loaded by some class loader. If you place the generated proxy classes in a namespace and directory under your projects class files, like in the example above, it would be sufficient to register the MyProject namespace on a class loader. Since the proxy classes are contained in that namespace and adhere to the standards for class loading, no additional work is required.\nGenerating the proxy classes into a namespace within your class library is the recommended setup.\n\nEntities with initialized proxy objects can now be serialized and unserialized properly from within the same application.\n\nFor more details refer to the Configuration section of the manual.\n\n## Removed allowPartialObjects configuration option\n\nThe allowPartialObjects configuration option together with the `Configuration#getAllowPartialObjects` and `Configuration#setAllowPartialObjects` methods have been removed.\nThe new behavior is as if the option were set to FALSE all the time, basically disallowing partial objects globally. However, you can still use the `Query::HINT_FORCE_PARTIAL_LOAD` query hint to force a query to return partial objects for optimization purposes.\n\n## Renamed Methods\n\n* Doctrine\\ORM\\Configuration#getCacheDir() to getProxyDir()\n* Doctrine\\ORM\\Configuration#setCacheDir($dir) to setProxyDir($dir)\n"
  },
  {
    "path": "ci/github/phpunit/mysqli.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <var name=\"db_driver\" value=\"mysqli\"/>\n        <var name=\"db_host\" value=\"127.0.0.1\" />\n        <var name=\"db_port\" value=\"3306\"/>\n        <var name=\"db_user\" value=\"root\" />\n        <var name=\"db_dbname\" value=\"doctrine_tests\" />\n        <var name=\"db_default_table_option_charset\" value=\"utf8mb4\" />\n        <var name=\"db_default_table_option_collation\" value=\"utf8mb4_unicode_ci\" />\n        <var name=\"db_default_table_option_engine\" value=\"InnoDB\" />\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "ci/github/phpunit/pdo_mysql.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <var name=\"db_driver\" value=\"pdo_mysql\"/>\n        <var name=\"db_host\" value=\"127.0.0.1\" />\n        <var name=\"db_port\" value=\"3306\"/>\n        <var name=\"db_user\" value=\"root\" />\n        <var name=\"db_dbname\" value=\"doctrine_tests\" />\n        <var name=\"db_default_table_option_charset\" value=\"utf8mb4\" />\n        <var name=\"db_default_table_option_collation\" value=\"utf8mb4_unicode_ci\" />\n        <var name=\"db_default_table_option_engine\" value=\"InnoDB\" />\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "ci/github/phpunit/pdo_pgsql.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <var name=\"db_driver\" value=\"pdo_pgsql\"/>\n        <var name=\"db_host\" value=\"localhost\" />\n        <var name=\"db_user\" value=\"postgres\" />\n        <var name=\"db_password\" value=\"postgres\" />\n        <var name=\"db_dbname\" value=\"doctrine_tests\" />\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "ci/github/phpunit/pdo_sqlite.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <!-- use an in-memory sqlite database -->\n        <var name=\"db_driver\" value=\"pdo_sqlite\"/>\n        <var name=\"db_memory\" value=\"true\"/>\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "ci/github/phpunit/pgsql.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <var name=\"db_driver\" value=\"pgsql\"/>\n        <var name=\"db_host\" value=\"localhost\" />\n        <var name=\"db_user\" value=\"postgres\" />\n        <var name=\"db_password\" value=\"postgres\" />\n        <var name=\"db_dbname\" value=\"doctrine_tests\" />\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "ci/github/phpunit/sqlite3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"../../../vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n        <!-- use an in-memory sqlite database -->\n        <var name=\"db_driver\" value=\"sqlite3\"/>\n        <var name=\"db_memory\" value=\"true\"/>\n\n        <!-- necessary change for some CLI/console output test assertions -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Doctrine DBAL Test Suite\">\n            <directory>../../../tests</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory suffix=\".php\">../../../src</directory>\n        </include>\n    </source>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n</phpunit>\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"doctrine/orm\",\n    \"description\": \"Object-Relational-Mapper for PHP\",\n    \"license\": \"MIT\",\n    \"type\": \"library\",\n    \"keywords\": [\n        \"orm\",\n        \"database\"\n    ],\n    \"authors\": [\n        {\n            \"name\": \"Guilherme Blanco\",\n            \"email\": \"guilhermeblanco@gmail.com\"\n        },\n        {\n            \"name\": \"Roman Borschel\",\n            \"email\": \"roman@code-factory.org\"\n        },\n        {\n            \"name\": \"Benjamin Eberlei\",\n            \"email\": \"kontakt@beberlei.de\"\n        },\n        {\n            \"name\": \"Jonathan Wage\",\n            \"email\": \"jonwage@gmail.com\"\n        },\n        {\n            \"name\": \"Marco Pivetta\",\n            \"email\": \"ocramius@gmail.com\"\n        }\n    ],\n    \"homepage\": \"https://www.doctrine-project.org/projects/orm.html\",\n    \"require\": {\n        \"php\": \"^8.1\",\n        \"ext-ctype\": \"*\",\n        \"composer-runtime-api\": \"^2\",\n        \"doctrine/collections\": \"^2.2\",\n        \"doctrine/dbal\": \"^3.8.2 || ^4\",\n        \"doctrine/deprecations\": \"^0.5.3 || ^1\",\n        \"doctrine/event-manager\": \"^1.2 || ^2\",\n        \"doctrine/inflector\": \"^1.4 || ^2.0\",\n        \"doctrine/instantiator\": \"^1.3 || ^2\",\n        \"doctrine/lexer\": \"^3\",\n        \"doctrine/persistence\": \"^3.3.1 || ^4\",\n        \"psr/cache\": \"^1 || ^2 || ^3\",\n        \"symfony/console\": \"^5.4 || ^6.0 || ^7.0 || ^8.0\",\n        \"symfony/var-exporter\": \"^6.3.9 || ^7.0 || ^8.0\"\n    },\n    \"require-dev\": {\n        \"doctrine/coding-standard\": \"^14.0\",\n        \"phpbench/phpbench\": \"^1.0\",\n        \"phpstan/extension-installer\": \"^1.4\",\n        \"phpstan/phpstan\": \"2.1.23\",\n        \"phpstan/phpstan-deprecation-rules\": \"^2\",\n        \"phpunit/phpunit\": \"^10.5.0 || ^11.5\",\n        \"psr/log\": \"^1 || ^2 || ^3\",\n        \"symfony/cache\": \"^5.4 || ^6.2 || ^7.0 || ^8.0\"\n    },\n    \"suggest\": {\n        \"ext-dom\": \"Provides support for XSD validation for XML mapping files\",\n        \"symfony/cache\": \"Provides cache support for Setup Tool with doctrine/cache 2.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Doctrine\\\\ORM\\\\\": \"src\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Doctrine\\\\Performance\\\\\": \"tests/Performance\",\n            \"Doctrine\\\\StaticAnalysis\\\\\": \"tests/StaticAnalysis\",\n            \"Doctrine\\\\Tests\\\\\": \"tests/Tests\"\n        }\n    },\n    \"config\": {\n        \"allow-plugins\": {\n            \"composer/package-versions-deprecated\": true,\n            \"dealerdirect/phpcodesniffer-composer-installer\": true,\n            \"phpstan/extension-installer\": true\n        },\n        \"sort-packages\": true\n    },\n    \"scripts\": {\n        \"docs\": \"composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args\"\n    }\n}\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "composer.lock\nvendor/\noutput/\n"
  },
  {
    "path": "docs/LICENSE.md",
    "content": "The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)\n\nCreative Commons Legal Code\n\nAttribution-NonCommercial-ShareAlike 3.0 Unported\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR\n    DAMAGES RESULTING FROM ITS USE.\n\nLicense\n\nTHE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE\nCOMMONS PUBLIC LICENSE (\"CCPL\" OR \"LICENSE\"). THE WORK IS PROTECTED BY\nCOPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS\nAUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.\n\nBY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE\nTO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY\nBE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS\nCONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND\nCONDITIONS.\n\n1. Definitions\n\n a. \"Adaptation\" means a work based upon the Work, or upon the Work and\n    other pre-existing works, such as a translation, adaptation,\n    derivative work, arrangement of music or other alterations of a\n    literary or artistic work, or phonogram or performance and includes\n    cinematographic adaptations or any other form in which the Work may be\n    recast, transformed, or adapted including in any form recognizably\n    derived from the original, except that a work that constitutes a\n    Collection will not be considered an Adaptation for the purpose of\n    this License. For the avoidance of doubt, where the Work is a musical\n    work, performance or phonogram, the synchronization of the Work in\n    timed-relation with a moving image (\"synching\") will be considered an\n    Adaptation for the purpose of this License.\n b. \"Collection\" means a collection of literary or artistic works, such as\n    encyclopedias and anthologies, or performances, phonograms or\n    broadcasts, or other works or subject matter other than works listed\n    in Section 1(g) below, which, by reason of the selection and\n    arrangement of their contents, constitute intellectual creations, in\n    which the Work is included in its entirety in unmodified form along\n    with one or more other contributions, each constituting separate and\n    independent works in themselves, which together are assembled into a\n    collective whole. A work that constitutes a Collection will not be\n    considered an Adaptation (as defined above) for the purposes of this\n    License.\n c. \"Distribute\" means to make available to the public the original and\n    copies of the Work or Adaptation, as appropriate, through sale or\n    other transfer of ownership.\n d. \"License Elements\" means the following high-level license attributes\n    as selected by Licensor and indicated in the title of this License:\n    Attribution, Noncommercial, ShareAlike.\n e. \"Licensor\" means the individual, individuals, entity or entities that\n    offer(s) the Work under the terms of this License.\n f. \"Original Author\" means, in the case of a literary or artistic work,\n    the individual, individuals, entity or entities who created the Work\n    or if no individual or entity can be identified, the publisher; and in\n    addition (i) in the case of a performance the actors, singers,\n    musicians, dancers, and other persons who act, sing, deliver, declaim,\n    play in, interpret or otherwise perform literary or artistic works or\n    expressions of folklore; (ii) in the case of a phonogram the producer\n    being the person or legal entity who first fixes the sounds of a\n    performance or other sounds; and, (iii) in the case of broadcasts, the\n    organization that transmits the broadcast.\n g. \"Work\" means the literary and/or artistic work offered under the terms\n    of this License including without limitation any production in the\n    literary, scientific and artistic domain, whatever may be the mode or\n    form of its expression including digital form, such as a book,\n    pamphlet and other writing; a lecture, address, sermon or other work\n    of the same nature; a dramatic or dramatico-musical work; a\n    choreographic work or entertainment in dumb show; a musical\n    composition with or without words; a cinematographic work to which are\n    assimilated works expressed by a process analogous to cinematography;\n    a work of drawing, painting, architecture, sculpture, engraving or\n    lithography; a photographic work to which are assimilated works\n    expressed by a process analogous to photography; a work of applied\n    art; an illustration, map, plan, sketch or three-dimensional work\n    relative to geography, topography, architecture or science; a\n    performance; a broadcast; a phonogram; a compilation of data to the\n    extent it is protected as a copyrightable work; or a work performed by\n    a variety or circus performer to the extent it is not otherwise\n    considered a literary or artistic work.\n h. \"You\" means an individual or entity exercising rights under this\n    License who has not previously violated the terms of this License with\n    respect to the Work, or who has received express permission from the\n    Licensor to exercise rights under this License despite a previous\n    violation.\n i. \"Publicly Perform\" means to perform public recitations of the Work and\n    to communicate to the public those public recitations, by any means or\n    process, including by wire or wireless means or public digital\n    performances; to make available to the public Works in such a way that\n    members of the public may access these Works from a place and at a\n    place individually chosen by them; to perform the Work to the public\n    by any means or process and the communication to the public of the\n    performances of the Work, including by public digital performance; to\n    broadcast and rebroadcast the Work by any means including signs,\n    sounds or images.\n j. \"Reproduce\" means to make copies of the Work by any means including\n    without limitation by sound or visual recordings and the right of\n    fixation and reproducing fixations of the Work, including storage of a\n    protected performance or phonogram in digital form or other electronic\n    medium.\n\n2. Fair Dealing Rights. Nothing in this License is intended to reduce,\nlimit, or restrict any uses free from copyright or rights arising from\nlimitations or exceptions that are provided for in connection with the\ncopyright protection under copyright law or other applicable laws.\n\n3. License Grant. Subject to the terms and conditions of this License,\nLicensor hereby grants You a worldwide, royalty-free, non-exclusive,\nperpetual (for the duration of the applicable copyright) license to\nexercise the rights in the Work as stated below:\n\n a. to Reproduce the Work, to incorporate the Work into one or more\n    Collections, and to Reproduce the Work as incorporated in the\n    Collections;\n b. to create and Reproduce Adaptations provided that any such Adaptation,\n    including any translation in any medium, takes reasonable steps to\n    clearly label, demarcate or otherwise identify that changes were made\n    to the original Work. For example, a translation could be marked \"The\n    original work was translated from English to Spanish,\" or a\n    modification could indicate \"The original work has been modified.\";\n c. to Distribute and Publicly Perform the Work including as incorporated\n    in Collections; and,\n d. to Distribute and Publicly Perform Adaptations.\n\nThe above rights may be exercised in all media and formats whether now\nknown or hereafter devised. The above rights include the right to make\nsuch modifications as are technically necessary to exercise the rights in\nother media and formats. Subject to Section 8(f), all rights not expressly\ngranted by Licensor are hereby reserved, including but not limited to the\nrights described in Section 4(e).\n\n4. Restrictions. The license granted in Section 3 above is expressly made\nsubject to and limited by the following restrictions:\n\n a. You may Distribute or Publicly Perform the Work only under the terms\n    of this License. You must include a copy of, or the Uniform Resource\n    Identifier (URI) for, this License with every copy of the Work You\n    Distribute or Publicly Perform. You may not offer or impose any terms\n    on the Work that restrict the terms of this License or the ability of\n    the recipient of the Work to exercise the rights granted to that\n    recipient under the terms of the License. You may not sublicense the\n    Work. You must keep intact all notices that refer to this License and\n    to the disclaimer of warranties with every copy of the Work You\n    Distribute or Publicly Perform. When You Distribute or Publicly\n    Perform the Work, You may not impose any effective technological\n    measures on the Work that restrict the ability of a recipient of the\n    Work from You to exercise the rights granted to that recipient under\n    the terms of the License. This Section 4(a) applies to the Work as\n    incorporated in a Collection, but this does not require the Collection\n    apart from the Work itself to be made subject to the terms of this\n    License. If You create a Collection, upon notice from any Licensor You\n    must, to the extent practicable, remove from the Collection any credit\n    as required by Section 4(d), as requested. If You create an\n    Adaptation, upon notice from any Licensor You must, to the extent\n    practicable, remove from the Adaptation any credit as required by\n    Section 4(d), as requested.\n b. You may Distribute or Publicly Perform an Adaptation only under: (i)\n    the terms of this License; (ii) a later version of this License with\n    the same License Elements as this License; (iii) a Creative Commons\n    jurisdiction license (either this or a later license version) that\n    contains the same License Elements as this License (e.g.,\n    Attribution-NonCommercial-ShareAlike 3.0 US) (\"Applicable License\").\n    You must include a copy of, or the URI, for Applicable License with\n    every copy of each Adaptation You Distribute or Publicly Perform. You\n    may not offer or impose any terms on the Adaptation that restrict the\n    terms of the Applicable License or the ability of the recipient of the\n    Adaptation to exercise the rights granted to that recipient under the\n    terms of the Applicable License. You must keep intact all notices that\n    refer to the Applicable License and to the disclaimer of warranties\n    with every copy of the Work as included in the Adaptation You\n    Distribute or Publicly Perform. When You Distribute or Publicly\n    Perform the Adaptation, You may not impose any effective technological\n    measures on the Adaptation that restrict the ability of a recipient of\n    the Adaptation from You to exercise the rights granted to that\n    recipient under the terms of the Applicable License. This Section 4(b)\n    applies to the Adaptation as incorporated in a Collection, but this\n    does not require the Collection apart from the Adaptation itself to be\n    made subject to the terms of the Applicable License.\n c. You may not exercise any of the rights granted to You in Section 3\n    above in any manner that is primarily intended for or directed toward\n    commercial advantage or private monetary compensation. The exchange of\n    the Work for other copyrighted works by means of digital file-sharing\n    or otherwise shall not be considered to be intended for or directed\n    toward commercial advantage or private monetary compensation, provided\n    there is no payment of any monetary compensation in con-nection with\n    the exchange of copyrighted works.\n d. If You Distribute, or Publicly Perform the Work or any Adaptations or\n    Collections, You must, unless a request has been made pursuant to\n    Section 4(a), keep intact all copyright notices for the Work and\n    provide, reasonable to the medium or means You are utilizing: (i) the\n    name of the Original Author (or pseudonym, if applicable) if supplied,\n    and/or if the Original Author and/or Licensor designate another party\n    or parties (e.g., a sponsor institute, publishing entity, journal) for\n    attribution (\"Attribution Parties\") in Licensor's copyright notice,\n    terms of service or by other reasonable means, the name of such party\n    or parties; (ii) the title of the Work if supplied; (iii) to the\n    extent reasonably practicable, the URI, if any, that Licensor\n    specifies to be associated with the Work, unless such URI does not\n    refer to the copyright notice or licensing information for the Work;\n    and, (iv) consistent with Section 3(b), in the case of an Adaptation,\n    a credit identifying the use of the Work in the Adaptation (e.g.,\n    \"French translation of the Work by Original Author,\" or \"Screenplay\n    based on original Work by Original Author\"). The credit required by\n    this Section 4(d) may be implemented in any reasonable manner;\n    provided, however, that in the case of a Adaptation or Collection, at\n    a minimum such credit will appear, if a credit for all contributing\n    authors of the Adaptation or Collection appears, then as part of these\n    credits and in a manner at least as prominent as the credits for the\n    other contributing authors. For the avoidance of doubt, You may only\n    use the credit required by this Section for the purpose of attribution\n    in the manner set out above and, by exercising Your rights under this\n    License, You may not implicitly or explicitly assert or imply any\n    connection with, sponsorship or endorsement by the Original Author,\n    Licensor and/or Attribution Parties, as appropriate, of You or Your\n    use of the Work, without the separate, express prior written\n    permission of the Original Author, Licensor and/or Attribution\n    Parties.\n e. For the avoidance of doubt:\n\n     i. Non-waivable Compulsory License Schemes. In those jurisdictions in\n        which the right to collect royalties through any statutory or\n        compulsory licensing scheme cannot be waived, the Licensor\n        reserves the exclusive right to collect such royalties for any\n        exercise by You of the rights granted under this License;\n    ii. Waivable Compulsory License Schemes. In those jurisdictions in\n        which the right to collect royalties through any statutory or\n        compulsory licensing scheme can be waived, the Licensor reserves\n        the exclusive right to collect such royalties for any exercise by\n        You of the rights granted under this License if Your exercise of\n        such rights is for a purpose or use which is otherwise than\n        noncommercial as permitted under Section 4(c) and otherwise waives\n        the right to collect royalties through any statutory or compulsory\n        licensing scheme; and,\n   iii. Voluntary License Schemes. The Licensor reserves the right to\n        collect royalties, whether individually or, in the event that the\n        Licensor is a member of a collecting society that administers\n        voluntary licensing schemes, via that society, from any exercise\n        by You of the rights granted under this License that is for a\n        purpose or use which is otherwise than noncommercial as permitted\n        under Section 4(c).\n f. Except as otherwise agreed in writing by the Licensor or as may be\n    otherwise permitted by applicable law, if You Reproduce, Distribute or\n    Publicly Perform the Work either by itself or as part of any\n    Adaptations or Collections, You must not distort, mutilate, modify or\n    take other derogatory action in relation to the Work which would be\n    prejudicial to the Original Author's honor or reputation. Licensor\n    agrees that in those jurisdictions (e.g. Japan), in which any exercise\n    of the right granted in Section 3(b) of this License (the right to\n    make Adaptations) would be deemed to be a distortion, mutilation,\n    modification or other derogatory action prejudicial to the Original\n    Author's honor and reputation, the Licensor will waive or not assert,\n    as appropriate, this Section, to the fullest extent permitted by the\n    applicable national law, to enable You to reasonably exercise Your\n    right under Section 3(b) of this License (right to make Adaptations)\n    but not otherwise.\n\n5. Representations, Warranties and Disclaimer\n\nUNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE\nFULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS\nAND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE\nWORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT\nLIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\nPURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,\nACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT\nDISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED\nWARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.\n\n6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE\nLAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR\nANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES\nARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS\nBEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n7. Termination\n\n a. This License and the rights granted hereunder will terminate\n    automatically upon any breach by You of the terms of this License.\n    Individuals or entities who have received Adaptations or Collections\n    from You under this License, however, will not have their licenses\n    terminated provided such individuals or entities remain in full\n    compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will\n    survive any termination of this License.\n b. Subject to the above terms and conditions, the license granted here is\n    perpetual (for the duration of the applicable copyright in the Work).\n    Notwithstanding the above, Licensor reserves the right to release the\n    Work under different license terms or to stop distributing the Work at\n    any time; provided, however that any such election will not serve to\n    withdraw this License (or any other license that has been, or is\n    required to be, granted under the terms of this License), and this\n    License will continue in full force and effect unless terminated as\n    stated above.\n\n8. Miscellaneous\n\n a. Each time You Distribute or Publicly Perform the Work or a Collection,\n    the Licensor offers to the recipient a license to the Work on the same\n    terms and conditions as the license granted to You under this License.\n b. Each time You Distribute or Publicly Perform an Adaptation, Licensor\n    offers to the recipient a license to the original Work on the same\n    terms and conditions as the license granted to You under this License.\n c. If any provision of this License is invalid or unenforceable under\n    applicable law, it shall not affect the validity or enforceability of\n    the remainder of the terms of this License, and without further action\n    by the parties to this agreement, such provision shall be reformed to\n    the minimum extent necessary to make such provision valid and\n    enforceable.\n d. No term or provision of this License shall be deemed waived and no\n    breach consented to unless such waiver or consent shall be in writing\n    and signed by the party to be charged with such waiver or consent.\n e. This License constitutes the entire agreement between the parties with\n    respect to the Work licensed here. There are no understandings,\n    agreements or representations with respect to the Work not specified\n    here. Licensor shall not be bound by any additional provisions that\n    may appear in any communication from You. This License may not be\n    modified without the mutual written agreement of the Licensor and You.\n f. The rights granted under, and the subject matter referenced, in this\n    License were drafted utilizing the terminology of the Berne Convention\n    for the Protection of Literary and Artistic Works (as amended on\n    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright\n    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996\n    and the Universal Copyright Convention (as revised on July 24, 1971).\n    These rights and subject matter take effect in the relevant\n    jurisdiction in which the License terms are sought to be enforced\n    according to the corresponding provisions of the implementation of\n    those treaty provisions in the applicable national law. If the\n    standard suite of rights granted under applicable copyright law\n    includes additional rights not granted under this License, such\n    additional rights are deemed to be included in the License; this\n    License is not intended to restrict the license of any rights under\n    applicable law.\n\n\nCreative Commons Notice\n\n    Creative Commons is not a party to this License, and makes no warranty\n    whatsoever in connection with the Work. Creative Commons will not be\n    liable to You or any party on any legal theory for any damages\n    whatsoever, including without limitation any general, special,\n    incidental or consequential damages arising in connection to this\n    license. Notwithstanding the foregoing two (2) sentences, if Creative\n    Commons has expressly identified itself as the Licensor hereunder, it\n    shall have all rights and obligations of Licensor.\n\n    Except for the limited purpose of indicating to the public that the\n    Work is licensed under the CCPL, Creative Commons does not authorize\n    the use by either party of the trademark \"Creative Commons\" or any\n    related trademark or logo of Creative Commons without the prior\n    written consent of Creative Commons. Any permitted use will be in\n    compliance with Creative Commons' then-current trademark usage\n    guidelines, as may be published on its website or otherwise made\n    available upon request from time to time. For the avoidance of doubt,\n    this trademark restriction does not form part of this License.\n\n    Creative Commons may be contacted at https://creativecommons.org/.\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Doctrine ORM Documentation\n\nThe documentation is written in [ReStructured Text](https://docutils.sourceforge.io/rst.html).\n\n## How to Generate:\n\nIn the project root, run\n\n    composer docs\n\nThis will generate the documentation into the `docs/output` subdirectory.\n\nTo browse the documentation, you need to run a webserver:\n\n    cd docs/output\n    php -S localhost:8000\n\nNow the documentation is available at [http://localhost:8000](http://localhost:8000).\n"
  },
  {
    "path": "docs/composer.json",
    "content": "{\n    \"name\": \"doctrine/orm-docs\",\n    \"description\": \"Documentation for the Object-Relational Mapper\\\"\",\n    \"type\": \"library\",\n    \"license\": \"MIT\",\n    \"require-dev\": {\n        \"doctrine/docs-builder\": \"^1.0\"\n    }\n}\n"
  },
  {
    "path": "docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst",
    "content": "Advanced field value conversion using custom mapping types\n==========================================================\n\n.. sectionauthor:: Jan Sorgalla <jsorgalla@googlemail.com>\n\nWhen creating entities, you sometimes have the need to transform field values\nbefore they are saved to the database. In Doctrine you can use Custom Mapping\nTypes to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`).\n\nThere are several ways to achieve this: converting the value inside the Type\nclass, converting the value on the database-level or a combination of both.\n\nThis article describes the third way by implementing the MySQL specific column\ntype `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.\n\nThe ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_\nof MySQL and enables you to store a single location in a coordinate space by\nusing x and y coordinates. You can use the Point type to store a\nlongitude/latitude pair to represent a geographic location.\n\nThe entity\n----------\n\nWe create a simple entity with a field ``$point`` which holds a value object\n``Point`` representing the latitude and longitude of the position.\n\nThe entity class:\n\n.. code-block:: php\n\n    <?php\n\n    namespace Geo\\Entity;\n\n    use Geo\\ValueObject\\Point;\n\n    #[Entity]\n    class Location\n    {\n        #[Column(type: 'point')]\n        private Point $point;\n\n        #[Column]\n        private string $address;\n\n        public function setPoint(Point $point): void\n        {\n            $this->point = $point;\n        }\n\n        public function getPoint(): Point\n        {\n            return $this->point;\n        }\n\n        public function setAddress(string $address): void\n        {\n            $this->address = $address;\n        }\n\n        public function getAddress(): string\n        {\n            return $this->address;\n        }\n    }\n\nWe use the custom type ``point`` in the ``#[Column]``  attribute of the\n``$point`` field. We will create this custom mapping type in the next chapter.\n\nThe point class:\n\n.. code-block:: php\n\n    <?php\n\n    namespace Geo\\ValueObject;\n\n    class Point\n    {\n        public function __construct(\n            private float $latitude,\n            private float $longitude,\n        ) {\n        }\n\n        public function getLatitude(): float\n        {\n            return $this->latitude;\n        }\n\n        public function getLongitude(): float\n        {\n            return $this->longitude;\n        }\n    }\n\nThe mapping type\n----------------\n\nNow we're going to create the ``point`` type and implement all required methods.\n\n.. code-block:: php\n\n    <?php\n\n    namespace Geo\\Types;\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n\n    use Geo\\ValueObject\\Point;\n\n    class PointType extends Type\n    {\n        const POINT = 'point';\n\n        public function getName()\n        {\n            return self::POINT;\n        }\n\n        public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)\n        {\n            return 'POINT';\n        }\n\n        public function convertToPHPValue($value, AbstractPlatform $platform)\n        {\n            list($longitude, $latitude) = sscanf($value, 'POINT(%f %f)');\n\n            return new Point($latitude, $longitude);\n        }\n\n        public function convertToDatabaseValue($value, AbstractPlatform $platform)\n        {\n            if ($value instanceof Point) {\n                $value = sprintf('POINT(%F %F)', $value->getLongitude(), $value->getLatitude());\n            }\n\n            return $value;\n        }\n\n        public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform)\n        {\n            return sprintf('AsText(%s)', $sqlExpr);\n        }\n\n        public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)\n        {\n            return sprintf('PointFromText(%s)', $sqlExpr);\n        }\n    }\n\nWe do a 2-step conversion here. In the first step, we convert the ``Point``\nobject into a string representation before saving to the database (in the\n``convertToDatabaseValue`` method) and back into an object after fetching the\nvalue from the database (in the ``convertToPHPValue`` method).\n\nThe format of the string representation format is called\n`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.\nThe advantage of this format is, that it is both human readable and parsable by MySQL.\n\nInternally, MySQL stores geometry values in a binary format that is not\nidentical to the WKT format. So, we need to let MySQL transform the WKT\nrepresentation into its internal format.\n\nThis is where the ``convertToPHPValueSQL`` and  ``convertToDatabaseValueSQL``\nmethods come into play.\n\nThis methods wrap a sql expression (the WKT representation of the Point) into\nMySQL functions `ST_PointFromText <https://dev.mysql.com/doc/refman/8.0/en/gis-wkt-functions.html#function_st-pointfromtext>`_\nand `ST_AsText <https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext>`_\nwhich convert WKT strings to and from the internal format of MySQL.\n\n.. note::\n\n    When using DQL queries, the ``convertToPHPValueSQL`` and\n    ``convertToDatabaseValueSQL`` methods only apply to identification variables\n    and path expressions in SELECT clauses. Expressions in  WHERE clauses are\n    **not** wrapped!\n\n    If you want to use Point values in WHERE clauses, you have to implement a\n    :doc:`user defined function <dql-user-defined-functions>` for\n    ``PointFromText``.\n\nExample usage\n-------------\n\n.. code-block:: php\n\n    <?php\n\n    // Bootstrapping stuff...\n    // $em = new \\Doctrine\\ORM\\EntityManager($connection, $config);\n\n    // Setup custom mapping type\n    use Doctrine\\DBAL\\Types\\Type;\n\n    Type::addType('point', 'Geo\\Types\\PointType');\n    $em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point');\n\n    // Store a Location object\n    use Geo\\Entity\\Location;\n    use Geo\\ValueObject\\Point;\n\n    $location = new Location();\n\n    $location->setAddress('1600 Amphitheatre Parkway, Mountain View, CA');\n    $location->setPoint(new Point(37.4220761, -122.0845187));\n\n    $em->persist($location);\n    $em->flush();\n    $em->clear();\n\n    // Fetch the Location object\n    $query = $em->createQuery(\"SELECT l FROM Geo\\Entity\\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'\");\n    $location = $query->getSingleResult();\n\n    /** @var Geo\\ValueObject\\Point */\n    $point = $location->getPoint();\n"
  },
  {
    "path": "docs/en/cookbook/aggregate-fields.rst",
    "content": "Aggregate Fields\n================\n\n.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>\n\nYou will often come across the requirement to display aggregate\nvalues of data that can be computed by using the MIN, MAX, COUNT or\nSUM SQL functions. For any ORM this is a tricky issue\ntraditionally. Doctrine ORM offers several ways to get access to\nthese values and this article will describe all of them from\ndifferent perspectives.\n\nYou will see that aggregate fields can become very explicit\nfeatures in your domain model and how this potentially complex\nbusiness rules can be easily tested.\n\nAn example model\n----------------\n\nSay you want to model a bank account and all their entries. Entries\ninto the account can either be of positive or negative money\nvalues. Each account has a credit limit and the account is never\nallowed to have a balance below that value.\n\nFor simplicity we live in a world where money is composed of\nintegers only. Also we omit the receiver/sender name, stated reason\nfor transfer and the execution date. These all would have to be\nadded on the ``Entry`` object.\n\nOur entities look like:\n\n.. code-block:: php\n\n    <?php\n\n    namespace Bank\\Entities;\n\n    use Doctrine\\ORM\\Mapping as ORM;\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n    use Doctrine\\Common\\Collections\\Collection;\n\n    #[ORM\\Entity]\n    class Account\n    {\n        #[ORM\\Id]\n        #[ORM\\GeneratedValue]\n        #[ORM\\Column(type: 'integer')]\n        private ?int $id;\n\n        #[ORM\\OneToMany(targetEntity: Entry::class, mappedBy: 'account', cascade: ['persist'])]\n        private Collection $entries;\n\n\n        public function __construct(\n            #[ORM\\Column(type: 'string', unique: true)]\n            private string $no,\n\n            #[ORM\\Column(type: 'integer')]\n            private int $maxCredit = 0,\n        ) {\n            $this->entries = new ArrayCollection();\n        }\n    }\n\n    #[ORM\\Entity]\n    class Entry\n    {\n        #[ORM\\Id]\n        #[ORM\\GeneratedValue]\n        #[ORM\\Column(type: 'integer')]\n        private ?int $id;\n\n        public function __construct(\n            #[ORM\\ManyToOne(targetEntity: Account::class, inversedBy: 'entries')]\n            private Account $account,\n\n            #[ORM\\Column(type: 'integer')]\n            private int $amount,\n        ) {\n            // more stuff here, from/to whom, stated reason, execution date and such\n        }\n\n        public function getAmount(): Amount\n        {\n            return $this->amount;\n        }\n    }\n\nUsing DQL\n---------\n\nThe Doctrine Query Language allows you to select for aggregate\nvalues computed from fields of your Domain Model. You can select\nthe current balance of your account by calling:\n\n.. code-block:: php\n\n    <?php\n    $dql = \"SELECT SUM(e.amount) AS balance FROM Bank\\Entities\\Entry e \" .\n           \"WHERE e.account = ?1\";\n    $balance = $em->createQuery($dql)\n                  ->setParameter(1, $myAccountId)\n                  ->getSingleScalarResult();\n\nThe ``$em`` variable in this (and forthcoming) example holds the\nDoctrine ``EntityManager``. We create a query for the SUM of all\namounts (negative amounts are withdraws) and retrieve them as a\nsingle scalar result, essentially return only the first column of\nthe first row.\n\nThis approach is simple and powerful, however it has a serious\ndrawback. We have to execute a specific query for the balance\nwhenever we need it.\n\nTo implement a powerful domain model we would rather have access to\nthe balance from our ``Account`` entity during all times (even if\nthe Account was not persisted in the database before!).\n\nAlso an additional requirement is the max credit per ``Account``\nrule.\n\nWe cannot reliably enforce this rule in our ``Account`` entity with\nthe DQL retrieval of the balance. There are many different ways to\nretrieve accounts. We cannot guarantee that we can execute the\naggregation query for all these use-cases, let alone that a\nuserland programmer checks this balance against newly added\nentries.\n\nUsing your Domain Model\n-----------------------\n\n``Account`` and all the ``Entry`` instances are connected through a\ncollection, which means we can compute this value at runtime:\n\n.. code-block:: php\n\n    <?php\n    class Account\n    {\n        // .. previous code\n\n        public function getBalance(): int\n        {\n            $balance = 0;\n            foreach ($this->entries as $entry) {\n                $balance += $entry->getAmount();\n            }\n\n            return $balance;\n        }\n    }\n\nNow we can always call ``Account::getBalance()`` to access the\ncurrent account balance.\n\nTo enforce the max credit rule we have to implement the \"Aggregate\nRoot\" pattern as described in Eric Evans book on Domain Driven\nDesign. Described with one sentence, an aggregate root controls the\ninstance creation, access and manipulation of its children.\n\nIn our case we want to enforce that new entries can only added to\nthe ``Account`` by using a designated method. The ``Account`` is\nthe aggregate root of this relation. We can also enforce the\ncorrectness of the bi-directional ``Account`` <-> ``Entry``\nrelation with this method:\n\n.. code-block:: php\n\n    <?php\n    class Account\n    {\n        public function addEntry(int $amount): void\n        {\n            $this->assertAcceptEntryAllowed($amount);\n\n            $this->entries[] = new Entry($this, $amount);\n        }\n    }\n\nNow look at the following test-code for our entities:\n\n.. code-block:: php\n\n    <?php\n\n    use PHPUnit\\Framework\\TestCase;\n\n    class AccountTest extends TestCase\n    {\n        public function testAddEntry()\n        {\n            $account = new Account(\"123456\", maxCredit: 200);\n            $this->assertEquals(0, $account->getBalance());\n\n            $account->addEntry(500);\n            $this->assertEquals(500, $account->getBalance());\n\n            $account->addEntry(-700);\n            $this->assertEquals(-200, $account->getBalance());\n        }\n\n        public function testExceedMaxLimit()\n        {\n            $account = new Account(\"123456\", maxCredit: 200);\n\n            $this->expectException(Exception::class);\n            $account->addEntry(-1000);\n        }\n    }\n\nTo enforce our rule we can now implement the assertion in\n``Account::addEntry``:\n\n.. code-block:: php\n\n    <?php\n\n    class Account\n    {\n        // .. previous code\n\n        private function assertAcceptEntryAllowed(int $amount): void\n        {\n            $futureBalance = $this->getBalance() + $amount;\n            $allowedMinimalBalance = ($this->maxCredit * -1);\n            if ($futureBalance < $allowedMinimalBalance) {\n                throw new Exception(\"Credit Limit exceeded, entry is not allowed!\");\n            }\n        }\n    }\n\nWe haven't talked to the entity manager for persistence of our\naccount example before. You can call\n``EntityManager::persist($account)`` and then\n``EntityManager::flush()`` at any point to save the account to the\ndatabase. All the nested ``Entry`` objects are automatically\nflushed to the database also.\n\n.. code-block:: php\n\n    <?php\n    $account = new Account(\"123456\", 200);\n    $account->addEntry(500);\n    $account->addEntry(-200);\n    $em->persist($account);\n    $em->flush();\n\nThe current implementation has a considerable drawback. To get the\nbalance, we have to initialize the complete ``Account::$entries``\ncollection, possibly a very large one. This can considerably hurt\nthe performance of your application.\n\nUsing an Aggregate Field\n------------------------\n\nTo overcome the previously mentioned issue (initializing the whole\nentries collection) we want to add an aggregate field called\n\"balance\" on the Account and adjust the code in\n``Account::getBalance()`` and ``Account:addEntry()``:\n\n.. code-block:: php\n\n    <?php\n    class Account\n    {\n        #[ORM\\Column(type: 'integer')]\n        private int $balance = 0;\n\n        public function getBalance(): int\n        {\n            return $this->balance;\n        }\n\n        public function addEntry(int $amount): void\n        {\n            $this->assertAcceptEntryAllowed($amount);\n\n            $this->entries[] = new Entry($this, $amount);\n            $this->balance += $amount;\n        }\n    }\n\nThis is a very simple change, but all the tests still pass. Our\naccount entities return the correct balance. Now calling the\n``Account::getBalance()`` method will not occur the overhead of\nloading all entries anymore. Adding a new Entry to the\n``Account::$entities`` will also not initialize the collection\ninternally.\n\nAdding a new entry is therefore very performant and explicitly\nhooked into the domain model. It will only update the account with\nthe current balance and insert the new entry into the database.\n\nTackling Race Conditions with Aggregate Fields\n----------------------------------------------\n\nWhenever you denormalize your database schema race-conditions can\npotentially lead to inconsistent state. See this example:\n\n.. code-block:: php\n\n    <?php\n\n    use Bank\\Entities\\Account;\n\n    // The Account $accId has a balance of 0 and a max credit limit of 200:\n    // request 1 account\n    $account1 = $em->find(Account::class, $accId);\n\n    // request 2 account\n    $account2 = $em->find(Account::class, $accId);\n\n    $account1->addEntry(-200);\n    $account2->addEntry(-200);\n\n    // now request 1 and 2 both flush the changes.\n\nThe aggregate field ``Account::$balance`` is now -200, however the\nSUM over all entries amounts yields -400. A violation of our max\ncredit rule.\n\nYou can use both optimistic or pessimistic locking to safe-guard\nyour aggregate fields against this kind of race-conditions. Reading\nEric Evans DDD carefully he mentions that the \"Aggregate Root\"\n(Account in our example) needs a locking mechanism.\n\nOptimistic locking is as easy as adding a version column:\n\n.. code-block:: php\n\n    <?php\n\n    class Account\n    {\n        #[ORM\\Column(type: 'integer')]\n        #[ORM\\Version]\n        private int $version;\n    }\n\nThe previous example would then throw an exception in the face of\nwhatever request saves the entity last (and would create the\ninconsistent state).\n\nPessimistic locking requires an additional flag set on the\n``EntityManager::find()`` call, enabling write locking directly in\nthe database using a FOR UPDATE.\n\n.. code-block:: php\n\n    <?php\n\n    use Bank\\Entities\\Account;\n    use Doctrine\\DBAL\\LockMode;\n\n    $account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_WRITE);\n\nKeeping Updates and Deletes in Sync\n-----------------------------------\n\nThe example shown in this article does not allow changes to the\nvalue in ``Entry``, which considerably simplifies the effort to\nkeep ``Account::$balance`` in sync. If your use-case allows fields\nto be updated or related entities to be removed you have to\nencapsulate this logic in your \"Aggregate Root\" entity and adjust\nthe aggregate field accordingly.\n\nConclusion\n----------\n\nThis article described how to obtain aggregate values using DQL or\nyour domain model. It showed how you can easily add an aggregate\nfield that offers serious performance benefits over iterating all\nthe related objects that make up an aggregate value. Finally I\nshowed how you can ensure that your aggregate fields do not get out\nof sync due to race-conditions and concurrent access.\n"
  },
  {
    "path": "docs/en/cookbook/custom-mapping-types.rst",
    "content": "Custom Mapping Types\n====================\n\nDoctrine allows you to create new mapping types. This can come in\nhandy when you're missing a specific mapping type or when you want\nto replace the existing implementation of a mapping type.\n\nIn order to create a new mapping type you need to subclass\n``Doctrine\\DBAL\\Types\\Type`` and implement/override the methods as\nyou wish. Here is an example skeleton of such a custom type class:\n\n.. code-block:: php\n\n    <?php\n    namespace My\\Project\\Types;\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n\n    /**\n     * My custom datatype.\n     */\n    class MyType extends Type\n    {\n        const MYTYPE = 'mytype'; // modify to match your type name\n\n        public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)\n        {\n            // return the SQL used to create your column type. To create a portable column type, use the $platform.\n        }\n\n        public function convertToPHPValue($value, AbstractPlatform $platform)\n        {\n            // This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.\n        }\n\n        public function convertToDatabaseValue($value, AbstractPlatform $platform)\n        {\n            // This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.\n        }\n\n        public function getName()\n        {\n            return self::MYTYPE; // modify to match your constant name\n        }\n    }\n\n.. note::\n\n    The following assumptions are applied to mapping types by the ORM:\n\n    -  The ``UnitOfWork`` never passes values to the database convert\n       method that did not change in the request.\n    -  The ``UnitOfWork`` internally assumes that entity identifiers are\n       castable to string. Hence, when using custom types that map to PHP\n       objects as IDs, such objects must implement the ``__toString()`` magic\n       method.\n\nWhen you have implemented the type you still need to let Doctrine\nknow about it. This can be achieved through the\n``Doctrine\\DBAL\\Types\\Type#addType($name, $className)``\nmethod. See the following example:\n\n.. code-block:: php\n\n    <?php\n    // in bootstrapping code\n\n    // ...\n\n    use Doctrine\\DBAL\\Types\\Type;\n\n    // ...\n\n    // Register my type\n    Type::addType('mytype', 'My\\Project\\Types\\MyType');\n\nTo convert the underlying database type of your\nnew \"mytype\" directly into an instance of ``MyType`` when performing\nschema operations, the type has to be registered with the database\nplatform as well:\n\n.. code-block:: php\n\n    <?php\n    $conn = $em->getConnection();\n    $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');\n\nWhen registering the custom types in the configuration you specify a unique\nname for the mapping type and map that to the corresponding fully qualified\nclass name. Now the new type can be used when mapping columns:\n\n.. code-block:: php\n\n    <?php\n    class MyPersistentClass\n    {\n        /** @Column(type=\"mytype\") */\n        private $field;\n    }\n\n"
  },
  {
    "path": "docs/en/cookbook/decorator-pattern.rst",
    "content": "Persisting the Decorator Pattern\n================================\n\n.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>\n\nThis recipe will show you a simple example of how you can use\nDoctrine ORM to persist an implementation of the\n`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_\n\nComponent\n---------\n\nThe ``Component`` class needs to be persisted, so it's going to\nbe an ``Entity``. As the top of the inheritance hierarchy, it's going\nto have to define the persistent inheritance. For this example, we\nwill use Single Table Inheritance, but Class Table Inheritance\nwould work as well. In the discriminator map, we will define two\nconcrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.\n\n.. code-block:: php\n\n    <?php\n\n    namespace Test;\n\n    #[Entity]\n    #[InheritanceType('SINGLE_TABLE')]\n    #[DiscriminatorColumn(name: 'discr', type: 'string')]\n    #[DiscriminatorMap(['cc' => Component\\ConcreteComponent::class,\n        'cd' => Decorator\\ConcreteDecorator::class])]\n    abstract class Component\n    {\n\n        #[Id, Column]\n        #[GeneratedValue(strategy: 'AUTO')]\n        protected int|null $id = null;\n\n        #[Column(type: 'string', nullable: true)]\n        protected $name;\n\n        public function getId(): int|null\n        {\n            return $this->id;\n        }\n\n        public function setName(string $name): void\n        {\n            $this->name = $name;\n        }\n\n        public function getName(): string\n        {\n            return $this->name;\n        }\n\n    }\n\nConcreteComponent\n-----------------\n\nThe ``ConcreteComponent`` class is pretty simple and doesn't do much\nmore than extend the abstract ``Component`` class (only for the\npurpose of keeping this example simple).\n\n.. code-block:: php\n\n    <?php\n\n    namespace Test\\Component;\n\n    use Test\\Component;\n\n    #[Entity]\n    class ConcreteComponent extends Component\n    {}\n\nDecorator\n---------\n\nThe ``Decorator`` class doesn't need to be persisted, but it does\nneed to define an association with a persisted ``Entity``. We can\nuse a ``MappedSuperclass`` for this.\n\n.. code-block:: php\n\n    <?php\n\n    namespace Test;\n\n    #[MappedSuperclass]\n    abstract class Decorator extends Component\n    {\n        #[OneToOne(targetEntity: Component::class, cascade: ['all'])]\n        #[JoinColumn(name: 'decorates', referencedColumnName: 'id')]\n        protected $decorates;\n\n        /**\n         * initialize the decorator\n         * @param Component $c\n         */\n        public function __construct(Component $c)\n        {\n            $this->setDecorates($c);\n        }\n\n        /**\n         * (non-PHPdoc)\n         * @see Test.Component::getName()\n         */\n        public function getName(): string\n        {\n    \t    return 'Decorated ' . $this->getDecorates()->getName();\n        }\n\n        /** the component being decorated */\n        protected function getDecorates(): Component\n        {\n    \t    return $this->decorates;\n        }\n\n        /** sets the component being decorated */\n        protected function setDecorates(Component $c): void\n        {\n    \t    $this->decorates = $c;\n        }\n\n    }\n\nAll operations on the ``Decorator`` (i.e. persist, remove, etc) will\ncascade from the ``Decorator`` to the ``Component``. This means that\nwhen we persist a ``Decorator``, Doctrine will take care of\npersisting the chain of decorated objects for us. A ``Decorator`` can\nbe treated exactly as a ``Component`` when it comes time to\npersisting it.\n\nThe ``Decorator's`` constructor accepts an instance of a\n``Component``, as defined by the ``Decorator`` pattern. The\nsetDecorates/getDecorates methods have been defined as protected to\nhide the fact that a ``Decorator`` is decorating a ``Component`` and\nkeeps the ``Component`` interface and the ``Decorator`` interface\nidentical.\n\nTo illustrate the intended result of the ``Decorator`` pattern, the\ngetName() method has been overridden to append a string to the\n``Component's`` getName() method.\n\nConcreteDecorator\n-----------------\n\nThe final class required to complete a simple implementation of the\nDecorator pattern is the ``ConcreteDecorator``. In order to further\nillustrate how the ``Decorator`` can alter data as it moves through\nthe chain of decoration, a new field, \"special\", has been added to\nthis class. The getName() has been overridden and appends the value\nof the getSpecial() method to its return value.\n\n.. code-block:: php\n\n    <?php\n\n    namespace Test\\Decorator;\n\n    use Test\\Decorator;\n\n    #[Entity]\n    class ConcreteDecorator extends Decorator\n    {\n\n        #[Column(type: 'string', nullable: true)]\n        protected string|null $special = null;\n\n        public function setSpecial(string|null $special): void\n        {\n            $this->special = $special;\n        }\n\n        public function getSpecial(): string|null\n        {\n            return $this->special;\n        }\n\n        /**\n         * (non-PHPdoc)\n         * @see Test.Component::getName()\n         */\n        public function getName(): string\n        {\n            return '[' . $this->getSpecial()\n                . '] ' . parent::getName();\n        }\n\n    }\n\nExamples\n--------\n\nHere is an example of how to persist and retrieve your decorated\nobjects\n\n.. code-block:: php\n\n    <?php\n\n    use Test\\Component\\ConcreteComponent,\n        Test\\Decorator\\ConcreteDecorator;\n\n    // assumes Doctrine ORM is configured and an instance of\n    // an EntityManager is available as $em\n\n    // create a new concrete component\n    $c = new ConcreteComponent();\n    $c->setName('Test Component 1');\n    $em->persist($c); // assigned unique ID = 1\n\n    // create a new concrete decorator\n    $c = new ConcreteComponent();\n    $c->setName('Test Component 2');\n\n    $d = new ConcreteDecorator($c);\n    $d->setSpecial('Really');\n    $em->persist($d);\n    // assigns c as unique ID = 2, and d as unique ID = 3\n\n    $em->flush();\n\n    $c = $em->find('Test\\Component', 1);\n    $d = $em->find('Test\\Component', 3);\n\n    echo get_class($c);\n    // prints: Test\\Component\\ConcreteComponent\n\n    echo $c->getName();\n    // prints: Test Component 1\n\n    echo get_class($d)\n    // prints: Test\\Component\\ConcreteDecorator\n\n    echo $d->getName();\n    // prints: [Really] Decorated Test Component 2\n"
  },
  {
    "path": "docs/en/cookbook/dql-custom-walkers/InterpolateParametersSQLOutputWalker.php",
    "content": "<?php\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Types\\BooleanType;\nuse Doctrine\\DBAL\\Types\\Exception\\ValueNotConvertible;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\n\nclass InterpolateParametersSQLOutputWalker extends SqlOutputWalker\n{\n    /** {@inheritdoc} */\n    public function walkInputParameter(AST\\InputParameter $inputParam): string\n    {\n        $parameter = $this->getQuery()->getParameter($inputParam->name);\n        if ($parameter === null) {\n            return '?';\n        }\n\n        $value = $parameter->getValue();\n        /** @var ParameterType|ArrayParameterType|int|string $typeName */\n        /** @see \\Doctrine\\ORM\\Query\\ParameterTypeInferer::inferType() */\n        $typeName = $parameter->getType();\n        $platform = $this->getConnection()->getDatabasePlatform();\n        $processParameterType = static fn(ParameterType $type) => static fn($value): string =>\n            (match ($type) { /** @see Type::getBindingType() */\n                ParameterType::NULL => 'NULL',\n                ParameterType::INTEGER => $value,\n                ParameterType::BOOLEAN => (new BooleanType())->convertToDatabaseValue($value, $platform),\n                ParameterType::STRING, ParameterType::ASCII => $platform->quoteStringLiteral($value),\n                default => throw new ValueNotConvertible($value, $type->name)\n            });\n\n        if (is_string($typeName) && Type::hasType($typeName)) {\n            return Type::getType($typeName)->convertToDatabaseValue($value, $platform);\n        }\n        if ($typeName instanceof ParameterType) {\n            return $processParameterType($typeName)($value);\n        }\n        if ($typeName instanceof ArrayParameterType && is_array($value)) {\n            $type = ArrayParameterType::toElementParameterType($typeName);\n            return implode(', ', array_map($processParameterType($type), $value));\n        }\n\n        throw new ValueNotConvertible($value, $typeName);\n    }\n}\n"
  },
  {
    "path": "docs/en/cookbook/dql-custom-walkers.rst",
    "content": "Extending DQL in Doctrine ORM: Custom AST Walkers\n===============================================\n\n.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>\n\nThe Doctrine Query Language (DQL) is a proprietary sql-dialect that\nsubstitutes tables and columns for Entity names and their fields.\nUsing DQL you write a query against the database using your\nentities. With the help of the metadata you can write very concise,\ncompact and powerful queries that are then translated into SQL by\nthe Doctrine ORM.\n\nIn Doctrine 1 the DQL language was not implemented using a real\nparser. This made modifications of the DQL by the user impossible.\nDoctrine ORM in contrast has a real parser for the DQL language,\nwhich transforms the DQL statement into an\n`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_\nand generates the appropriate SQL statement for it. Since this\nprocess is deterministic Doctrine heavily caches the SQL that is\ngenerated from any given DQL query, which reduces the performance\noverhead of the parsing process to zero.\n\nYou can modify the Abstract syntax tree by hooking into DQL parsing\nprocess by adding a Custom Tree Walker. A walker is an interface\nthat walks each node of the Abstract syntax tree, thereby\ngenerating the SQL statement.\n\nThere are two types of custom tree walkers that you can hook into\nthe DQL parser:\n\n\n-  An output walker. This one actually generates the SQL, and there\n   is only ever one of them. We implemented the default SqlWalker\n   implementation for it.\n-  A tree walker. There can be many tree walkers, they cannot\n   generate the SQL, however they can modify the AST before its\n   rendered to SQL.\n\nNow this is all awfully technical, so let me come to some use-cases\nfast to keep you motivated. Using walker implementation you can for\nexample:\n\n-  Modify the Output walker to get the raw SQL via ``Query->getSQL()``\n   with interpolated parameters.\n-  Modify the AST to generate a Count Query to be used with a\n   paginator for any given DQL query.\n-  Modify the Output Walker to generate vendor-specific SQL\n   (instead of ANSI).\n-  Modify the AST to add additional where clauses for specific\n   entities (example ACL, country-specific content...)\n-  Modify the Output walker to pretty print the SQL for debugging\n   purposes.\n\nIn this cookbook-entry I will show examples of the first three\npoints. There are probably much more use-cases.\n\nGeneric count query for pagination\n----------------------------------\n\nSay you have a blog and posts all with one category and one author.\nA query for the front-page or any archive page might look something\nlike:\n\n.. code-block:: sql\n\n    SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ...\n\nNow in this query the blog post is the root entity, meaning it's the\none that is hydrated directly from the query and returned as an\narray of blog posts. In contrast the comment and author are loaded\nfor deeper use in the object tree.\n\nA pagination for this query would want to approximate the number of\nposts that match the WHERE clause of this query to be able to\npredict the number of pages to show to the user. A draft of the DQL\nquery for pagination would look like:\n\n.. code-block:: sql\n\n    SELECT count(DISTINCT p.id) FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ...\n\nNow you could go and write each of these queries by hand, or you\ncan use a tree walker to modify the AST for you. Let's see how the\nAPI would look for this use-case:\n\n.. code-block:: php\n\n    <?php\n    $pageNum = 1;\n    $query = $em->createQuery($dql);\n    $query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20);\n\n    $totalResults = Paginate::count($query);\n    $results = $query->getResult();\n\nThe ``Paginate::count(Query $query)`` looks like:\n\n.. code-block:: php\n\n    <?php\n    class Paginate\n    {\n        static public function count(Query $query)\n        {\n            /*\n               To avoid changing the $query passed into the method and to make sure a possibly existing\n               ResultSetMapping is discarded, we create a new query object any copy relevant data over.\n            */\n            $countQuery = new Query($query->getEntityManager());\n            $countQuery->setDQL($query->getDQL());\n            $countQuery->setParameters(clone $query->getParameters());\n            foreach ($query->getHints() as $name => $value) {\n                $countQuery->setHint($name, $value);\n            }\n\n            $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\\Paginate\\CountSqlWalker'));\n            $countQuery->setFirstResult(null)->setMaxResults(null);\n\n            return $countQuery->getSingleScalarResult();\n        }\n    }\n\nThis resets the limit clause first and max results\nand registers the ``CountSqlWalker`` custom tree walker which\nwill modify the AST to execute a count query. The walkers\nimplementation is:\n\n.. code-block:: php\n\n    <?php\n    class CountSqlWalker extends TreeWalkerAdapter\n    {\n        /**\n         * Walks down a SelectStatement AST node, thereby generating the appropriate SQL.\n         *\n         * @return string The SQL.\n         */\n        public function walkSelectStatement(SelectStatement $AST)\n        {\n            $parent = null;\n            $parentName = null;\n            foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {\n                if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {\n                    $parent = $qComp;\n                    $parentName = $dqlAlias;\n                    break;\n                }\n            }\n\n            $pathExpression = new PathExpression(\n                PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,\n                $parent['metadata']->getSingleIdentifierFieldName()\n            );\n            $pathExpression->type = PathExpression::TYPE_STATE_FIELD;\n\n            $AST->selectClause->selectExpressions = array(\n                new SelectExpression(\n                    new AggregateExpression('count', $pathExpression, true), null\n                )\n            );\n        }\n    }\n\nThis will delete any given select expressions and replace them with\na distinct count query for the root entities primary key. This will\nonly work if your entity has only one identifier field (composite\nkeys won't work).\n\nModify the Output Walker to generate Vendor specific SQL\n--------------------------------------------------------\n\nMost RMDBS have vendor-specific features for optimizing select\nquery execution plans. You can write your own output walker to\nintroduce certain keywords using the Query Hint API. A query hint\ncan be set via ``Query::setHint($name, $value)`` as shown in the\nprevious example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint.\n\nWe will implement a custom Output Walker that allows to specify the\n``SQL_NO_CACHE`` query hint.\n\n.. code-block:: php\n\n    <?php\n    $dql = \"SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ...\";\n    $query = $m->createQuery($dql);\n    $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'DoctrineExtensions\\Query\\MysqlWalker');\n    $query->setHint(\"mysqlWalker.sqlNoCache\", true);\n    $results = $query->getResult();\n\nOur ``MysqlWalker`` will extend the default ``SqlWalker``. We will\nmodify the generation of the SELECT clause, adding the\n``SQL_NO_CACHE`` on those queries that need it:\n\n.. code-block:: php\n\n    <?php\n    class MysqlWalker extends SqlWalker\n    {\n         /**\n         * Walks down a SelectClause AST node, thereby generating the appropriate SQL.\n         *\n         * @param $selectClause\n         * @return string The SQL.\n         */\n        public function walkSelectClause($selectClause)\n        {\n            $sql = parent::walkSelectClause($selectClause);\n\n            if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {\n                if ($selectClause->isDistinct) {\n                    $sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);\n                } else {\n                    $sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);\n                }\n            }\n\n            return $sql;\n        }\n    }\n\nWriting extensions to the Output Walker requires a very deep\nunderstanding of the DQL Parser and Walkers, but may offer your\nhuge benefits with using vendor specific features. This would still\nallow you write DQL queries instead of NativeQueries to make use of\nvendor specific features.\n\nModifying the Output Walker to get the raw SQL with interpolated parameters\n---------------------------------------------------------------------------\n\nSometimes we may want to log or trace the raw SQL being generated from its DQL\nfor profiling slow queries afterwards or audit queries that changed many rows\n``$query->getSQL()`` will give us the prepared statement being passed to database\nwith all values of SQL parameters being replaced by positional ``?`` or named ``:name``\nas parameters are interpolated into prepared statements by the database while executing the SQL.\n``$query->getParameters()`` will give us details about SQL parameters that we've provided.\nSo we can create an output walker to interpolate all SQL parameters that will be\npassed into prepared statement in PHP before database handle them internally:\n\n.. literalinclude:: dql-custom-walkers/InterpolateParametersSQLOutputWalker.php\n   :language: php\n\nThen you may get the raw SQL with this output walker:\n\n.. code-block:: php\n\n    <?php\n    $query\n        ->where('t.int IN (:ints)')->setParameter(':ints', [1, 2])\n        ->orWhere('t.string IN (?0)')->setParameter(0, ['3', '4'])\n        ->orWhere(\"t.bool = ?1\")->setParameter('?1', true)\n        ->orWhere(\"t.string = :string\")->setParameter(':string', 'ABC')\n        ->setHint(\\Doctrine\\ORM\\Query::HINT_CUSTOM_OUTPUT_WALKER, InterpolateParametersSQLOutputWalker::class)\n        ->getSQL();\n\nThe where clause of the returned SQL should be like:\n\n.. code-block:: sql\n\n    WHERE t0_.int IN (1, 2)\n       OR t0_.string IN ('3', '4')\n       OR t0_.bool = 1\n       OR t0_.string = 'ABC'\n"
  },
  {
    "path": "docs/en/cookbook/dql-user-defined-functions.rst",
    "content": "DQL User Defined Functions\n==========================\n\n.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>\n\nBy default DQL supports a limited subset of all the vendor-specific\nSQL functions common between all the vendors. However in many cases\nonce you have decided on a specific database vendor, you will never\nchange it during the life of your project. This decision for a\nspecific vendor potentially allows you to make use of powerful SQL\nfeatures that are unique to the vendor.\n\nIt is worth to mention that Doctrine ORM also allows you to handwrite\nyour SQL instead of extending the DQL parser. Extending DQL is sort of an\nadvanced extension point. You can map arbitrary SQL to your objects\nand gain access to vendor specific functionalities using the\n``EntityManager#createNativeQuery()`` API as described in\nthe :doc:`Native Query <../reference/native-sql>` chapter.\n\n\nThe DQL Parser has hooks to register functions that can then be\nused in your DQL queries and transformed into SQL, allowing to\nextend Doctrines Query capabilities to the vendors strength. This\npost explains the User-Defined Functions API (UDF) of the Dql\nParser and shows some examples to give you some hints how you would\nextend DQL.\n\nThere are three types of functions in DQL, those that return a\nnumerical value, those that return a string and those that return a\nDate. Your custom method has to be registered as either one of\nthose. The return type information is used by the DQL parser to\ncheck possible syntax errors during the parsing process, for\nexample using a string function return value in a math expression.\n\nRegistering your own DQL functions\n----------------------------------\n\nYou can register your functions adding them to the ORM\nconfiguration:\n\n.. code-block:: php\n\n    <?php\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->addCustomStringFunction($name, $class);\n    $config->addCustomNumericFunction($name, $class);\n    $config->addCustomDatetimeFunction($name, $class);\n\n    $em = new EntityManager($connection, $config);\n\nThe ``$name`` is the name the function will be referred to in the\nDQL query. ``$class`` is a string of a class-name which has to\nextend ``Doctrine\\ORM\\Query\\Node\\FunctionNode``. This is a class\nthat offers all the necessary API and methods to implement a UDF.\n\nInstead of providing the function class name, you can also provide\na callable that returns the function object:\n\n.. code-block:: php\n\n    <?php\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->addCustomStringFunction($name, function () {\n        return new MyCustomFunction();\n    });\n\nIn this post we will implement some MySql specific Date calculation\nmethods, which are quite handy in my opinion:\n\nDate Diff\n---------\n\n`Mysql's DateDiff function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_datediff>`_\ntakes two dates as argument and calculates the difference in days\nwith ``date1-date2``.\n\nThe DQL parser is a top-down recursive descent parser to generate\nthe Abstract-Syntax Tree (AST) and uses a TreeWalker approach to\ngenerate the appropriate SQL from the AST. This makes reading the\nParser/TreeWalker code manageable in a finite amount of time.\n\nThe ``FunctionNode`` class I referred to earlier requires you to\nimplement two methods, one for the parsing process (obviously)\ncalled ``parse`` and one for the TreeWalker process called\n``getSql()``. I show you the code for the DateDiff method and\ndiscuss it step by step:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * DateDiffFunction ::= \"DATEDIFF\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n     */\n    class DateDiff extends FunctionNode\n    {\n        // (1)\n        public $firstDateExpression = null;\n        public $secondDateExpression = null;\n\n        public function parse(\\Doctrine\\ORM\\Query\\Parser $parser)\n        {\n            $parser->match(TokenType::T_IDENTIFIER); // (2)\n            $parser->match(TokenType::T_OPEN_PARENTHESIS); // (3)\n            $this->firstDateExpression = $parser->ArithmeticPrimary(); // (4)\n            $parser->match(TokenType::T_COMMA); // (5)\n            $this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)\n            $parser->match(TokenType::T_CLOSE_PARENTHESIS); // (3)\n        }\n\n        public function getSql(\\Doctrine\\ORM\\Query\\SqlWalker $sqlWalker)\n        {\n            return 'DATEDIFF(' .\n                $this->firstDateExpression->dispatch($sqlWalker) . ', ' .\n                $this->secondDateExpression->dispatch($sqlWalker) .\n            ')'; // (7)\n        }\n    }\n\nThe Parsing process of the DATEDIFF function is going to find two\nexpressions the date1 and the date2 values, whose AST Node\nrepresentations will be saved in the variables of the DateDiff\nFunctionNode instance at (1).\n\nThe parse() method has to cut the function call \"DATEDIFF\" and its\nargument into pieces. Since the parser detects the function using a\nlookahead the T\\_IDENTIFIER of the function name has to be taken\nfrom the stack (2), followed by a detection of the arguments in\n(4)-(6). The opening and closing parenthesis have to be detected\nalso. This happens during the Parsing process and leads to the\ngeneration of a DateDiff FunctionNode somewhere in the AST of the\ndql statement.\n\nThe ``ArithmeticPrimary`` method call is the most common\ndenominator of valid EBNF tokens taken from the :ref:`DQL EBNF grammar\n<dql_ebnf_grammar>`\nthat matches our requirements for valid input into the DateDiff Dql\nfunction. Picking the right tokens for your methods is a tricky\nbusiness, but the EBNF grammar is pretty helpful finding it, as is\nlooking at the Parser source code.\n\nNow in the TreeWalker process we have to pick up this node and\ngenerate SQL from it, which apparently is quite easy looking at the\ncode in (7). Since we don't know which type of AST Node the first\nand second Date expression are we are just dispatching them back to\nthe SQL Walker to generate SQL from and then wrap our DATEDIFF\nfunction call around this output.\n\nNow registering this DateDiff FunctionNode with the ORM using:\n\n.. code-block:: php\n\n    <?php\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->addCustomStringFunction('DATEDIFF', 'DoctrineExtensions\\Query\\MySql\\DateDiff');\n\nWe can do fancy stuff like:\n\n.. code-block:: sql\n\n    SELECT p FROM DoctrineExtensions\\Query\\BlogPost p WHERE DATEDIFF(CURRENT_TIME(), p.created) < 7\n\nDate Add\n--------\n\nOften useful it the ability to do some simple date calculations in\nyour DQL query using\n`MySql's DATE_ADD function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add>`_.\n\nI'll skip the blah and show the code for this function:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * DateAddFunction ::=\n     *     \"DATE_ADD\" \"(\" ArithmeticPrimary \", INTERVAL\" ArithmeticPrimary Identifier \")\"\n     */\n    class DateAdd extends FunctionNode\n    {\n        public $firstDateExpression = null;\n        public $intervalExpression = null;\n        public $unit = null;\n\n        public function parse(\\Doctrine\\ORM\\Query\\Parser $parser)\n        {\n            $parser->match(TokenType::T_IDENTIFIER);\n            $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n            $this->firstDateExpression = $parser->ArithmeticPrimary();\n\n            $parser->match(TokenType::T_COMMA);\n            $parser->match(TokenType::T_IDENTIFIER);\n\n            $this->intervalExpression = $parser->ArithmeticPrimary();\n\n            $parser->match(TokenType::T_IDENTIFIER);\n\n            /** @var Lexer $lexer */\n            $lexer = $parser->getLexer();\n            $this->unit = $lexer->token['value'];\n\n            $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n        }\n\n        public function getSql(\\Doctrine\\ORM\\Query\\SqlWalker $sqlWalker)\n        {\n            return 'DATE_ADD(' .\n                $this->firstDateExpression->dispatch($sqlWalker) . ', INTERVAL ' .\n                $this->intervalExpression->dispatch($sqlWalker) . ' ' . $this->unit .\n            ')';\n        }\n    }\n\nThe only difference compared to the DATEDIFF here is, we\nadditionally need the ``Lexer`` to access the value of the\n``T_IDENTIFIER`` token for the Date Interval unit, for example the\nMONTH in:\n\n.. code-block:: sql\n\n    SELECT p FROM DoctrineExtensions\\Query\\BlogPost p WHERE DATE_ADD(CURRENT_TIME(), INTERVAL 4 MONTH) > p.created\n\nThe above method now only supports the specification using\n``INTERVAL``, to also allow a real date in DATE\\_ADD we need to add\nsome decision logic to the parsing process (makes up for a nice\nexercise).\n\nNow as you see, the Parsing process doesn't catch all the possible\nSQL errors, here we don't match for all the valid inputs for the\ninterval unit. However where necessary we rely on the database\nvendors SQL parser to show us further errors in the parsing\nprocess, for example if the Unit would not be one of the supported\nvalues by MySql.\n\nTyped functions\n---------------\nBy default, result of custom functions is fetched as-is from the database driver.\nIf you want to be sure that the type is always the same, then your custom function needs to \nimplement ``Doctrine\\ORM\\Query\\AST\\TypedExpression``. Then, the result is wired\nthrough ``Doctrine\\DBAL\\Types\\Type::convertToPhpValue()`` of the ``Type`` returned in ``getReturnType()``.\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use Doctrine\\DBAL\\Types\\Types;\n    use Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\n    use Doctrine\\ORM\\Query\\AST\\TypedExpression;\n\n    class DateDiff extends FunctionNode implements TypedExpression\n    {\n        // ...\n\n        public function getReturnType(): Type\n        {\n            return Type::getType(Types::INTEGER);\n        }\n    }\n\n\nConclusion\n----------\n\nNow that you all know how you can implement vendor specific SQL\nfunctionalities in DQL, we would be excited to see user extensions\nthat add vendor specific function packages, for example more math\nfunctions, XML + GIS Support, Hashing functions and so on.\n\nFor ORM we will come with the current set of functions, however for\na future version we will re-evaluate if we can abstract even more\nvendor sql functions and extend the DQL languages scope.\n\nCode for this Extension to DQL and other Doctrine Extensions can be\nfound\n`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.\n"
  },
  {
    "path": "docs/en/cookbook/entities-in-session.rst",
    "content": "Entities in the Session\n=======================\n\nThere are several use-cases to save entities in the session, for example:\n\n1.  User data\n2.  Multi-step forms\n\nTo achieve this with Doctrine you have to pay attention to some details to get\nthis working.\n\nUpdating an entity\n------------------\n\nIn Doctrine an entity objects has to be \"managed\" by an EntityManager to be\nupdatable. Entities saved into the session are not managed in the next request\nanymore. This means that you have to update the entities with the stored session\ndata after you fetch the entities from the EntityManager again.\n\nFor a representative User object the code to get data from the session into a\nmanaged Doctrine object can look like these examples:\n\nWorking with scalars\n~~~~~~~~~~~~~~~~~~~~\n\nIn simpler applications there is no need to work with objects in sessions and you can use\nseparate session elements.\n\n.. code-block:: php\n\n    <?php\n    require_once 'bootstrap.php';\n\n    session_start();\n    if (isset($_SESSION['userId']) && is_int($_SESSION['userId'])) {\n        $userId = $_SESSION['userId'];\n\n        $em = GetEntityManager(); // creates an EntityManager\n        $user = $em->find(User::class, $userId);\n\n        $user->setValue($_SESSION['storedValue']);\n\n        $em->flush();\n    }\n\nWorking with custom data transfer objects\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf objects are needed, we discourage the storage of entity objects in the session. It's\npreferable to use a `DTO (data transfer object) <https://en.wikipedia.org/wiki/Data_transfer_object>`_\ninstead and merge the DTO data later with the entity.\n\n.. code-block:: php\n\n    <?php\n    require_once 'bootstrap.php';\n\n    session_start();\n    if (isset($_SESSION['user']) && $_SESSION['user'] instanceof UserDto) {\n        $userDto = $_SESSION['user'];\n\n        $em = GetEntityManager(); // creates an EntityManager\n        $userEntity = $em->find(User::class, $userDto->getId());\n\n        $userEntity->populateFromDto($userDto);\n\n        $em->flush();\n    }\n\nSerializing entity into the session\n-----------------------------------\n\nEntities that are serialized into the session normally contain references to\nother entities as well. Think of the user entity has a reference to their\narticles, groups, photos or many other different entities. If you serialize\nthis object into the session then you don't want to serialize the related\nentities as well. This is why you shouldn't serialize an entity and use\nonly the needed values of it. This can happen with the help of a DTO.\n\n.. code-block:: php\n\n    <?php\n    require_once 'bootstrap.php';\n\n    $em = GetEntityManager(); // creates an EntityManager \n\n    $user = $em->find(\"User\", 1);\n    $userDto = new UserDto($user->getId(), $user->getFirstName(), $user->getLastName());\n    // or \"UserDto::createFrom($user);\", but don't store an entity in a property. Only its values without relations.\n\n    $_SESSION['user'] = $userDto;\n\n\n"
  },
  {
    "path": "docs/en/cookbook/generated-columns/Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass Article\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column]\n    private int $id;\n\n    /**\n     * When working with Postgres, it is recommended to use the jsonb\n     * format for better performance.\n     */\n    #[ORM\\Column(options: ['jsonb' => true])]\n    private array $content;\n\n    /**\n     * Because we specify NOT NULL, inserting will fail if the content does\n     * not have a string in the title field.\n     */\n    #[ORM\\Column(\n        insertable: false,\n        updatable: false,\n        columnDefinition: \"VARCHAR(255) generated always as (content->>'title') stored NOT NULL\",\n        generated: 'ALWAYS',\n    )]\n    private string $title;\n}\n"
  },
  {
    "path": "docs/en/cookbook/generated-columns/Person.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass Person\n{\n    #[ORM\\Column(type: 'string')]\n    private string $firstName;\n\n    #[ORM\\Column(type: 'string', name: 'name')]\n    private string $lastName;\n\n    #[ORM\\Column(\n        type: 'string',\n        insertable: false,\n        updatable: false,\n        columnDefinition: \"VARCHAR(255) GENERATED ALWAYS AS (concat(firstName, ' ', name) stored NOT NULL\",\n        generated: 'ALWAYS',\n    )]\n    private string $fullName;\n}\n"
  },
  {
    "path": "docs/en/cookbook/generated-columns.rst",
    "content": "Generated Columns\n=================\n\nGenerated columns, sometimes also called virtual columns, are populated by\nthe database engine itself. They are a tool for performance optimization, to\navoid calculating a value on each query.\n\nYou can define generated columns on entities and have Doctrine map the values\nto your entity.\n\nDeclaring a generated column\n----------------------------\n\nThere is no explicit mapping instruction for generated columns. Instead, you\nspecify that the column should not be written to, and define a custom column\ndefinition.\n\n.. literalinclude:: generated-columns/Person.php\n   :language: php\n\n* ``insertable``, ``updatable``: Setting these to false tells Doctrine to never\n  write this column - writing to a generated column would result in an error\n  from the database.\n* ``columnDefinition``: We specify the full DDL to create the column. To allow\n  to use database specific features, this attribute does not use Doctrine Query\n  Language but native SQL. Note that you need to reference columns by their\n  database name (either explicitly set in the mapping or per the current\n  :doc:`naming strategy <../reference/namingstrategy>`).\n  Be aware that specifying a column definition makes the ``SchemaTool``\n  completely ignore all other configuration for this column. See also\n  :ref:`#[Column] <attrref_column>`\n* ``generated``: Specifying that this column is always generated tells Doctrine\n  to update the field on the entity with the value from the database after\n  every write operation.\n\nAdvanced example: Extracting a value from a JSON structure\n----------------------------------------------------------\n\nLets assume we have an entity that stores a blogpost as structured JSON.\nTo avoid extracting all titles on the fly when listing the posts, we create a\ngenerated column with the field.\n\n.. literalinclude:: generated-columns/Article.php\n   :language: php\n"
  },
  {
    "path": "docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst",
    "content": "Implementing ArrayAccess for Domain Objects\n===========================================\n\n.. sectionauthor:: Roman Borschel <roman@code-factory.org>\n\nThis recipe will show you how to implement ArrayAccess for your\ndomain objects in order to allow more uniform access, for example\nin templates. In these examples we will implement ArrayAccess on a\n`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_\nfor all our domain objects.\n\nOption 1\n--------\n\nIn this implementation we will make use of PHPs highly dynamic\nnature to dynamically access properties of a subtype in a supertype\nat runtime. Note that this implementation has 2 main caveats:\n\n\n-  It will not work with private fields\n-  It will not go through any getters/setters\n\n.. code-block:: php\n\n    <?php\n    abstract class DomainObject implements ArrayAccess\n    {\n        public function offsetExists($offset) {\n            return isset($this->$offset);\n        }\n    \n        public function offsetSet($offset, $value) {\n            $this->$offset = $value;\n        }\n    \n        public function offsetGet($offset) {\n            return $this->$offset;\n        }\n    \n        public function offsetUnset($offset) {\n            $this->$offset = null;\n        }\n    }\n\nOption 2\n--------\n\nIn this implementation we will dynamically invoke getters/setters.\nAgain we use PHPs dynamic nature to invoke methods on a subtype\nfrom a supertype at runtime. This implementation has the following\ncaveats:\n\n\n-  It relies on a naming convention\n-  The semantics of offsetExists can differ\n-  offsetUnset will not work with typehinted setters\n\n.. code-block:: php\n\n    <?php\n    abstract class DomainObject implements ArrayAccess\n    {\n        public function offsetExists($offset) {\n            // In this example we say that exists means it is not null\n            $value = $this->{\"get$offset\"}();\n            return $value !== null;\n        }\n    \n        public function offsetSet($offset, $value) {\n            $this->{\"set$offset\"}($value);\n        }\n    \n        public function offsetGet($offset) {\n            return $this->{\"get$offset\"}();\n        }\n    \n        public function offsetUnset($offset) {\n            $this->{\"set$offset\"}(null);\n        }\n    }\n\nRead-only\n---------\n\nYou can slightly tweak option 1 or option 2 in order to make array\naccess read-only. This will also circumvent some of the caveats of\neach option. Simply make offsetSet and offsetUnset throw an\nexception (i.e. BadMethodCallException).\n\n.. code-block:: php\n\n    <?php\n    abstract class DomainObject implements ArrayAccess\n    {\n        public function offsetExists($offset) {\n            // option 1 or option 2\n        }\n    \n        public function offsetSet($offset, $value) {\n            throw new BadMethodCallException(\"Array access of class \" . get_class($this) . \" is read-only!\");\n        }\n    \n        public function offsetGet($offset) {\n            // option 1 or option 2\n        }\n    \n        public function offsetUnset($offset) {\n            throw new BadMethodCallException(\"Array access of class \" . get_class($this) . \" is read-only!\");\n        }\n    }\n\n\n"
  },
  {
    "path": "docs/en/cookbook/mysql-enums.rst",
    "content": "Mysql Enums\n===========\n\nThe type system of Doctrine ORM consists of flyweights, which means there is only\none instance of any given type. Additionally types do not contain state. Both\nassumptions make it rather complicated to work with the Enum Type of MySQL that\nis used quite a lot by developers.\n\nWhen using Enums with a non-tweaked Doctrine ORM application you will get\nerrors from the Schema-Tool commands due to the unknown database type \"enum\".\nBy default Doctrine does not map the MySQL enum type to a Doctrine type.\nThis is because Enums contain state (their allowed values) and Doctrine\ntypes don't.\n\nThis cookbook entry shows two possible solutions to work with MySQL enums.\nBut first a word of warning. The MySQL Enum type has considerable downsides:\n\n-  Adding new values requires to rebuild the whole table, which can take hours\n   depending on the size.\n-  Enums are ordered in the way the values are specified, not in their \"natural\" order.\n-  Enums validation mechanism for allowed values is not necessarily good,\n   specifying invalid values leads to an empty enum for the default MySQL error\n   settings. You can easily replicate the \"allow only some values\" requirement\n   in your Doctrine entities.\n\nSolution 1: Mapping to Varchars\n-------------------------------\n\nYou can map ENUMs to varchars. You can register MySQL ENUMs to map to Doctrine\nvarchars. This way Doctrine always resolves ENUMs to Doctrine varchars. It\nwill even detect this match correctly when using SchemaTool update commands.\n\n.. code-block:: php\n\n    <?php\n    $conn = $em->getConnection();\n    $conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');\n\nIn this case you have to ensure that each varchar field that is an enum in the\ndatabase only gets passed the allowed values. You can easily enforce this in your\nentities:\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class Article\n    {\n        public const STATUS_VISIBLE = 'visible';\n        public const STATUS_INVISIBLE = 'invisible';\n\n        #[Column(type: \"string\")]\n        private $status;\n\n        public function setStatus(string $status): void\n        {\n            if (!in_array($status, [self::STATUS_VISIBLE, self::STATUS_INVISIBLE], true)) {\n                throw new \\InvalidArgumentException(\"Invalid status\");\n            }\n\n            $this->status = $status;\n        }\n    }\n\nIf you want to actively create enums through the Doctrine Schema-Tool by using\nthe **columnDefinition** attribute.\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class Article\n    {\n        #[Column(type: \"string\", columnDefinition: \"ENUM('visible', 'invisible')\")]\n        private $status;\n    }\n\nIn this case however Schema-Tool update will have a hard time not to request changes for this column on each call.\n\nSolution 2: Defining a Type\n---------------------------\n\nYou can make a stateless ENUM type by creating a type class for each unique set of ENUM values.\nFor example for the previous enum type:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\DBAL;\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n\n    class EnumVisibilityType extends Type\n    {\n        private const ENUM_VISIBILITY = 'enumvisibility';\n        private const STATUS_VISIBLE = 'visible';\n        private const STATUS_INVISIBLE = 'invisible';\n\n        public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string\n        {\n            return \"ENUM('visible', 'invisible')\";\n        }\n\n        public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed\n        {\n            return $value;\n        }\n\n        public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string\n        {\n            if (!in_array($value, [self::STATUS_VISIBLE, self::STATUS_INVISIBLE], true)) {\n                throw new \\InvalidArgumentException(\"Invalid status\");\n            }\n\n            return $value;\n        }\n\n        public function getName(): string\n        {\n            return self::ENUM_VISIBILITY;\n        }\n    }\n\nYou can register this type with ``Type::addType('enumvisibility', 'MyProject\\DBAL\\EnumVisibilityType');``.\nThen in your entity you can just use this type:\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class Article\n    {\n        #[Column(type: \"enumvisibility\")]\n        private $status;\n    }\n\nYou can generalize this approach easily to create a base class for enums:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\DBAL;\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n\n    abstract class EnumType extends Type\n    {\n        protected $name;\n        protected $values = [];\n\n        public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string\n        {\n            $values = array_map(fn($val) => \"'\".$val.\"'\", $this->values);\n\n            return \"ENUM(\".implode(\", \", $values).\")\";\n        }\n\n        public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed\n        {\n            return $value;\n        }\n\n        public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed\n        {\n            if (!in_array($value, $this->values, true)) {\n                throw new \\InvalidArgumentException(\"Invalid '\".$this->name.\"' value.\");\n            }\n\n            return $value;\n        }\n\n        public function getName(): string\n        {\n            return $this->name;\n        }\n    }\n\nWith this base class you can define an enum as easily as:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\DBAL;\n\n    class EnumVisibilityType extends EnumType\n    {\n        protected $name = 'enumvisibility';\n        protected $values = ['visible', 'invisible'];\n    }\n"
  },
  {
    "path": "docs/en/cookbook/resolve-target-entity-listener.rst",
    "content": "Keeping your Modules independent\n=================================\n\nOne of the goals of using modules is to create discrete units of functionality\nthat do not have many (if any) dependencies, allowing you to use that\nfunctionality in other applications without including unnecessary items.\n\nDoctrine ORM includes a new utility called the ``ResolveTargetEntityListener``,\nthat functions by intercepting certain calls inside Doctrine and rewrite\ntargetEntity parameters in your metadata mapping at runtime. It means that\nin your bundle you are able to use an interface or abstract class in your\nmappings and expect correct mapping to a concrete entity at runtime.\n\nThis functionality allows you to define relationships between different entities\nbut not making them hard dependencies.\n\nBackground\n----------\n\nIn the following example, the situation is we have an `InvoiceModule`\nwhich provides invoicing functionality, and a `CustomerModule` that\ncontains customer management tools. We want to keep these separated,\nbecause they can be used in other systems without each other, but for\nour application we want to use them together.\n\nIn this case, we have an ``Invoice`` entity with a relationship to a\nnon-existent object, an ``InvoiceSubjectInterface``. The goal is to get\nthe ``ResolveTargetEntityListener`` to replace any mention of the interface\nwith a real object that implements that interface.\n\nSet up\n------\n\nWe're going to use the following basic entities (which are incomplete\nfor brevity) to explain how to set up and use the RTEL.\n\nA Customer entity\n\n.. code-block:: php\n\n    <?php\n    // src/Acme/AppModule/Entity/Customer.php\n\n    namespace Acme\\AppModule\\Entity;\n\n    use Doctrine\\ORM\\Mapping as ORM;\n    use Acme\\CustomerModule\\Entity\\Customer as BaseCustomer;\n    use Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface;\n\n    #[ORM\\Entity]\n    #[ORM\\Table(name: 'customer')]\n    class Customer extends BaseCustomer implements InvoiceSubjectInterface\n    {\n        // In our example, any methods defined in the InvoiceSubjectInterface\n        // are already implemented in the BaseCustomer\n    }\n\nAn Invoice entity\n\n.. code-block:: php\n\n    <?php\n    // src/Acme/InvoiceModule/Entity/Invoice.php\n\n    namespace Acme\\InvoiceModule\\Entity;\n\n    use Doctrine\\ORM\\Mapping AS ORM;\n    use Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface;\n\n    #[ORM\\Entity]\n    #[ORM\\Table(name: 'invoice')]\n    class Invoice\n    {\n        #[ORM\\ManyToOne(targetEntity: InvoiceSubjectInterface::class)]\n        protected InvoiceSubjectInterface $subject;\n    }\n\nAn InvoiceSubjectInterface\n\n.. code-block:: php\n\n    <?php\n    // src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php\n\n    namespace Acme\\InvoiceModule\\Model;\n\n    /**\n     * An interface that the invoice Subject object should implement.\n     * In most circumstances, only a single object should implement\n     * this interface as the ResolveTargetEntityListener can only\n     * change the target to a single object.\n     */\n    interface InvoiceSubjectInterface\n    {\n        // List any additional methods that your InvoiceModule\n        // will need to access on the subject so that you can\n        // be sure that you have access to those methods.\n\n        /**\n         * @return string\n         */\n        public function getName();\n    }\n\nNext, we need to configure the listener. Add this to the area you set up Doctrine. You\nmust set this up in the way outlined below, otherwise you can not be guaranteed that\nthe targetEntity resolution will occur reliably:\n\n.. code-block:: php\n\n    <?php\n    $evm  = new \\Doctrine\\Common\\EventManager;\n    $rtel = new \\Doctrine\\ORM\\Tools\\ResolveTargetEntityListener;\n\n    // Adds a target-entity class\n    $rtel->addResolveTargetEntity('Acme\\\\InvoiceModule\\\\Model\\\\InvoiceSubjectInterface', 'Acme\\\\CustomerModule\\\\Entity\\\\Customer', array());\n\n    // Add the ResolveTargetEntityListener\n    $evm->addEventListener(Doctrine\\ORM\\Events::loadClassMetadata, $rtel);\n\n    $connection = \\Doctrine\\DBAL\\DriverManager::getConnection($connectionOptions, $config, $evm);\n    $em = new \\Doctrine\\ORM\\EntityManager($connection, $config, $evm);\n\nFinal Thoughts\n--------------\n\nWith the ``ResolveTargetEntityListener``, we are able to decouple our\nbundles, keeping them usable by themselves, but still being able to\ndefine relationships between different objects. By using this method,\nI've found my bundles end up being easier to maintain independently.\n"
  },
  {
    "path": "docs/en/cookbook/sql-table-prefixes.rst",
    "content": "SQL-Table Prefixes\n==================\n\nThis recipe is intended as an example of implementing a\nloadClassMetadata listener to provide a Table Prefix option for\nyour application. The method used below is not a hack, but fully\nintegrates into the Doctrine system, all SQL generated will include\nthe appropriate table prefix.\n\nIn most circumstances it is desirable to separate different\napplications into individual databases, but in certain cases, it\nmay be beneficial to have a table prefix for your Entities to\nseparate them from other vendor products in the same database.\n\nImplementing the listener\n-------------------------\n\nThe listener in this example has been set up with the\nDoctrineExtensions namespace. You create this file in your\nlibrary/DoctrineExtensions directory, but will need to set up\nappropriate autoloaders.\n\n.. code-block:: php\n\n    <?php\n    \n    namespace DoctrineExtensions;\n    use \\Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\n    \n    class TablePrefix\n    {\n        protected $prefix = '';\n    \n        public function __construct($prefix)\n        {\n            $this->prefix = (string) $prefix;\n        }\n    \n        public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)\n        {\n            $classMetadata = $eventArgs->getClassMetadata();\n\n            if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {\n                $classMetadata->setPrimaryTable([\n                    'name' => $this->prefix . $classMetadata->getTableName()\n                ]);\n            }\n\n            foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {\n                if ($mapping['type'] == \\Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {\n                    $mappedTableName = $mapping['joinTable']['name'];\n                    $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;\n                }\n            }\n        }\n\n    }\n\nTelling the EntityManager about our listener\n--------------------------------------------\n\nA listener of this type must be set up before the EntityManager has\nbeen initialised, otherwise an Entity might be created or cached\nbefore the prefix has been set.\n\n.. note::\n\n    If you set this listener up, be aware that you will need\n    to clear your caches and drop then recreate your database schema.\n\n\n.. code-block:: php\n\n    <?php\n    \n    // $connectionOptions and $config set earlier\n    \n    $evm = new \\Doctrine\\Common\\EventManager;\n    \n    // Table Prefix\n    $tablePrefix = new \\DoctrineExtensions\\TablePrefix('prefix_');\n    $evm->addEventListener(\\Doctrine\\ORM\\Events::loadClassMetadata, $tablePrefix);\n    \n    $em = new \\Doctrine\\ORM\\EntityManager($connection, $config, $evm);\n"
  },
  {
    "path": "docs/en/cookbook/strategy-cookbook-introduction.rst",
    "content": "Strategy-Pattern\n================\n\nThis recipe will give you a short introduction on how to design\nsimilar entities without using expensive (i.e. slow) inheritance\nbut with not more than *the well-known strategy pattern* event\nlisteners\n\nScenario / Problem\n------------------\n\nGiven a Content-Management-System, we probably want to add / edit\nsome so-called \"blocks\" and \"panels\". What are they for?\n\n\n-  A block might be a registration form, some text content, a table\n   with information. A good example might also be a small calendar.\n-  A panel is by definition a block that can itself contain blocks.\n   A good example for a panel might be a sidebar box: You could easily\n   add a small calendar into it.\n\nSo, in this scenario, when building your CMS, you will surely add\nlots of blocks and panels to your pages and you will find yourself\nhighly uncomfortable because of the following:\n\n\n-  Every existing page needs to know about the panels it contains -\n   therefore, you'll have an association to your panels. But if you've\n   got several types of panels - what do you do? Add an association to\n   every panel-type? This wouldn't be flexible. You might be tempted\n   to add an AbstractPanelEntity and an AbstractBlockEntity that use\n   class inheritance. Your page could then only confer to the\n   AbstractPanelType and Doctrine ORM would do the rest for you, i.e.\n   load the right entities. But - you'll for sure have lots of panels\n   and blocks, and even worse, you'd have to edit the discriminator\n   map *manually* every time you or another developer implements a new\n   block / entity. This would tear down any effort of modular\n   programming.\n\nTherefore, we need something that's far more flexible.\n\nSolution\n--------\n\nThe solution itself is pretty easy. We will have one base class\nthat will be loaded via the page and that has specific behaviour -\na Block class might render the front-end and even the backend, for\nexample. Now, every block that you'll write might look different or\nneed different data - therefore, we'll offer an API to these\nmethods but internally, we use a strategy that exactly knows what\nto do.\n\nFirst of all, we need to make sure that we have an interface that\ncontains every needed action. Such actions would be rendering the\nfront-end or the backend, solving dependencies (blocks that are\nsupposed to be placed in the sidebar could refuse to be placed in\nthe middle of your page, for example).\n\nSuch an interface could look like this:\n\n\n.. code-block:: php\n\n    <?php\n    /**\n     * This interface defines the basic actions that a block / panel needs to support.\n     *\n     * Every blockstrategy is *only* responsible for rendering a block and declaring some basic\n     * support, but *not* for updating its configuration etc. For this purpose, use controllers\n     * and models.\n     */\n    interface BlockStrategyInterface {\n        /**\n         * This could configure your entity\n         */\n        public function setConfig(Config\\EntityConfig $config);\n\n        /**\n         * Returns the config this strategy is configured with.\n         * @return Core\\Model\\Config\\EntityConfig\n         */\n        public function getConfig();\n\n        /**\n         * Set the view object.\n         * @param  \\Zend_View_Interface $view\n         * @return \\Zend_View_Helper_Interface\n         */\n        public function setView(\\Zend_View_Interface $view);\n\n        /**\n         * @return \\Zend_View_Interface\n         */\n        public function getView();\n\n        /**\n         * Renders this strategy. This method will be called when the user\n         * displays the site.\n         *\n         * @return string\n         */\n        public function renderFrontend();\n\n        /**\n         * Renders the backend of this block. This method will be called when\n         * a user tries to reconfigure this block instance.\n         *\n         * Most of the time, this method will return / output a simple form which in turn\n         * calls some controllers.\n         *\n         * @return string\n         */\n        public function renderBackend();\n\n        /**\n         * Returns all possible types of panels this block can be stacked onto\n         *\n         * @return array\n         */\n        public function getRequiredPanelTypes();\n\n        /**\n         * Determines whether a Block is able to use a given type or not\n         * @param string $typeName The typename\n         * @return boolean\n         */\n        public function canUsePanelType($typeName);\n\n        public function setBlockEntity(AbstractBlock $block);\n\n        public function getBlockEntity();\n    }\n\nAs you can see, we have a method \"setBlockEntity\" which ties a potential strategy to an object of type AbstractBlock. This type will simply define the basic behaviour of our blocks and could potentially look something like this:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * This is the base class for both Panels and Blocks.\n     * It shouldn't be extended by your own blocks - simply write a strategy!\n     */\n    abstract class AbstractBlock {\n        /**\n         * The id of the block item instance\n         * This is a doctrine field, so you need to setup generation for it\n         * @var integer\n         */\n        private $id;\n\n        // Add code for relation to the parent panel, configuration objects, ....\n\n        /**\n         * This var contains the classname of the strategy\n         * that is used for this blockitem. (This string (!) value will be persisted by Doctrine ORM)\n         *\n         * This is a doctrine field, so make sure that you use a\n           #[Column] attribute or setup your xml files correctly\n         * @var string\n         */\n        protected $strategyClassName;\n\n        /**\n         * This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine ORM.\n         *\n         * @var BlockStrategyInterface\n         */\n        protected $strategyInstance;\n\n        /**\n         * Returns the strategy that is used for this blockitem.\n         *\n         * The strategy itself defines how this block can be rendered etc.\n         *\n         * @return string\n         */\n        public function getStrategyClassName() {\n            return $this->strategyClassName;\n        }\n\n        /**\n         * Returns the instantiated strategy\n         *\n         * @return BlockStrategyInterface\n         */\n        public function getStrategyInstance() {\n            return $this->strategyInstance;\n        }\n\n        /**\n         * Sets the strategy this block / panel should work as. Make sure that you've used\n         * this method before persisting the block!\n         *\n         * @param BlockStrategyInterface $strategy\n         */\n        public function setStrategy(BlockStrategyInterface $strategy) {\n            $this->strategyInstance  = $strategy;\n            $this->strategyClassName = get_class($strategy);\n            $strategy->setBlockEntity($this);\n        }\n\nNow, the important point is that $strategyClassName is a Doctrine ORM\nfield, i.e. Doctrine will persist this value. This is only the\nclass name of your strategy and not an instance!\n\nFinishing your strategy pattern, we hook into the Doctrine postLoad\nevent and check whether a block has been loaded. If so, you will\ninitialize it - i.e. get the strategies classname, create an\ninstance of it and set it via setStrategyBlock().\n\nThis might look like this:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Common\\EventSubscriber;\n    use Doctrine\\ORM\\Event\\LifecycleEventArgs;\n    use Doctrine\\ORM\\Events;\n\n    /**\n     * The BlockStrategyEventListener will initialize a strategy after the\n     * block itself was loaded.\n     */\n    class BlockStrategyEventListener implements EventSubscriber {\n\n        protected $view;\n\n        public function __construct(\\Zend_View_Interface $view) {\n            $this->view = $view;\n        }\n\n        public function getSubscribedEvents() {\n           return array(Events::postLoad);\n        }\n\n        public function postLoad(LifecycleEventArgs $args) {\n            $blockItem = $args->getObject();\n\n            // Both blocks and panels are instances of Block\\AbstractBlock\n            if ($blockItem instanceof Block\\AbstractBlock) {\n                $strategy  = $blockItem->getStrategyClassName();\n                $strategyInstance = new $strategy();\n                if (null !== $blockItem->getConfig()) {\n                    $strategyInstance->setConfig($blockItem->getConfig());\n                }\n                $strategyInstance->setView($this->view);\n                $blockItem->setStrategy($strategyInstance);\n            }\n        }\n    }\n\nIn this example, even some variables are set - like a view object\nor a specific configuration object.\n"
  },
  {
    "path": "docs/en/cookbook/validation-of-entities.rst",
    "content": "Validation of Entities\n======================\n\n.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>\n\nDoctrine ORM does not ship with any internal validators, the reason\nbeing that we think all the frameworks out there already ship with\nquite decent ones that can be integrated into your Domain easily.\nWhat we offer are hooks to execute any kind of validation.\n\n.. note::\n\n    You don't need to validate your entities in the lifecycle\n    events. It is only one of many options. Of course you can also\n    perform validations in value setters or any other method of your\n    entities that are used in your code.\n\n\nEntities can register lifecycle event methods with Doctrine that\nare called on different occasions. For validation we would need to\nhook into the events called before persisting and updating. Even\nthough we don't support validation out of the box, the\nimplementation is even simpler than in Doctrine 1 and you will get\nthe additional benefit of being able to re-use your validation in\nany other part of your domain.\n\nSay we have an ``Order`` with several ``OrderLine`` instances. We\nnever want to allow any customer to order for a larger sum than they\nare allowed to:\n\n.. code-block:: php\n\n    <?php\n    class Order\n    {\n        public function assertCustomerAllowedBuying()\n        {\n            $orderLimit = $this->customer->getOrderLimit();\n\n            $amount = 0;\n            foreach ($this->orderLines as $line) {\n                $amount += $line->getAmount();\n            }\n\n            if ($amount > $orderLimit) {\n                throw new CustomerOrderLimitExceededException();\n            }\n        }\n    }\n\nNow this is some pretty important piece of business logic in your\ncode, enforcing it at any time is important so that customers with\na unknown reputation don't owe your business too much money.\n\nWe can enforce this constraint in any of the metadata drivers.\nFirst Attributes:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\n    use Doctrine\\ORM\\Mapping\\PrePersist;\n    use Doctrine\\ORM\\Mapping\\PreUpdate;\n\n    #[Entity]\n    #[HasLifecycleCallbacks]\n    class Order\n    {\n        #[PrePersist, PreUpdate]\n        public function assertCustomerAllowedBuying() {}\n    }\n\nIn XML Mappings:\n\n.. code-block:: xml\n\n    <doctrine-mapping>\n        <entity name=\"Order\">\n            <lifecycle-callbacks>\n                <lifecycle-callback type=\"prePersist\" method=\"assertCustomerallowedBuying\" />\n                <lifecycle-callback type=\"preUpdate\" method=\"assertCustomerallowedBuying\" />\n            </lifecycle-callbacks>\n        </entity>\n    </doctrine-mapping>\n\nNow validation is performed whenever you call\n``EntityManager#persist($order)`` or when you call\n``EntityManager#flush()`` and an order is about to be updated. Any\nException that happens in the lifecycle callbacks will be caught by\nthe EntityManager and the current transaction is rolled back.\n\nOf course you can do any type of primitive checks, not null,\nemail-validation, string size, integer and date ranges in your\nvalidation callbacks.\n\n.. code-block:: php\n\n    <?php\n    class Order\n    {\n        #[PrePersist, PreUpdate]\n        public function validate()\n        {\n            if (!($this->plannedShipDate instanceof DateTime)) {\n                throw new ValidateException();\n            }\n\n            if ($this->plannedShipDate->format('U') < time()) {\n                throw new ValidateException();\n            }\n\n            if ($this->customer == null) {\n                throw new OrderRequiresCustomerException();\n            }\n        }\n    }\n\nWhat is nice about lifecycle events is, you can also re-use the\nmethods at other places in your domain, for example in combination\nwith your form library. Additionally there is no limitation in the\nnumber of methods you register on one particular event, i.e. you\ncan register multiple methods for validation in \"PrePersist\" or\n\"PreUpdate\" or mix and share them in any combinations between those\ntwo events.\n\nThere is no limit to what you can and can't validate in\n\"PrePersist\" and \"PreUpdate\" as long as you don't create new entity\ninstances. This was already discussed in the previous blog post on\nthe Versionable extension, which requires another type of event\ncalled \"onFlush\".\n\nFurther readings: :ref:`reference-events-lifecycle-events`\n"
  },
  {
    "path": "docs/en/cookbook/working-with-datetime.rst",
    "content": "Working with DateTime Instances\n===============================\n\nThere are many nitty gritty details when working with PHPs DateTime instances. You have to know their inner\nworkings pretty well not to make mistakes with date handling. This cookbook entry holds several\ninteresting pieces of information on how to work with PHP DateTime instances in ORM.\n\nDateTime changes are detected by Reference\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen calling ``EntityManager#flush()`` Doctrine computes the changesets of all the currently managed entities\nand saves the differences to the database. In case of object properties (@Column(type=\"datetime\") or @Column(type=\"object\"))\nthese comparisons are always made **BY REFERENCE**. That means the following change will **NOT** be saved into the database:\n\n.. code-block:: php\n\n    <?php\n\n    use DateTime;\n\n    #[Entity]\n    class Article\n    {\n        #[Column(type: 'datetime')]\n        private DateTime $updated;\n\n        public function setUpdated(): void\n        {\n            // will NOT be saved in the database\n            $this->updated->modify(\"now\");\n        }\n    }\n\nThe way to go would be:\n\n.. code-block:: php\n\n    <?php\n    use DateTime;\n\n    class Article\n    {\n        public function setUpdated(): void\n        {\n            // WILL be saved in the database\n            $this->updated = new DateTime(\"now\");\n        }\n    }\n\nDefault Timezone Gotcha\n~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default Doctrine assumes that you are working with a default timezone. Each DateTime instance that\nis created by Doctrine will be assigned the timezone that is currently the default, either through\nthe ``date.timezone`` ini setting or by calling ``date_default_timezone_set()``.\n\nThis is very important to handle correctly if your application runs on different servers or is moved from one to another server\n(with different timezone settings). You have to make sure that the timezone is the correct one\non all this systems.\n\nHandling different Timezones with the DateTime Type\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you first come across the requirement to save different timezones you may be still optimistic about how\nto manage this mess,\nhowever let me crush your expectations fast. There is not a single database out there (supported by Doctrine ORM)\nthat supports timezones correctly. Correctly here means that you can cover all the use-cases that\ncan come up with timezones. If you don't believe me you should read up on `Storing DateTime\nin Databases <https://derickrethans.nl/storing-date-time-in-database.html>`_.\n\nThe problem is simple. Not a single database vendor saves the timezone, only the differences to UTC.\nHowever with frequent daylight saving and political timezone changes you can have a UTC offset that moves\nin different offset directions depending on the real location.\n\nThe solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine ORM. However there is a workaround\nthat even allows correct date-time handling with timezones:\n\n1. Always convert any DateTime instance to UTC.\n2. Only set Timezones for displaying purposes\n3. Save the Timezone in the Entity for persistence.\n\nSay we have an application for an international postal company and employees insert events regarding postal-package\naround the world, in their current timezones. To determine the exact time an event occurred means to save both\nthe UTC time at the time of the booking and the timezone the event happened in.\n\n.. code-block:: php\n\n    <?php\n\n    namespace DoctrineExtensions\\DBAL\\Types;\n\n    use DateTimeZone;\n    use Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n    use Doctrine\\DBAL\\Types\\ConversionException;\n    use Doctrine\\DBAL\\Types\\DateTimeType;\n\n    class UTCDateTimeType extends DateTimeType\n    {\n        private static DateTimeZone $utc;\n\n        public function convertToDatabaseValue($value, AbstractPlatform $platform)\n        {\n            if ($value instanceof \\DateTime) {\n                $value->setTimezone(self::getUtc());\n            }\n\n            return parent::convertToDatabaseValue($value, $platform);\n        }\n\n        public function convertToPHPValue($value, AbstractPlatform $platform)\n        {\n            if (null === $value || $value instanceof \\DateTime) {\n                return $value;\n            }\n\n            $converted = \\DateTime::createFromFormat(\n                $platform->getDateTimeFormatString(),\n                $value,\n                self::getUtc()\n            );\n\n            if (! $converted) {\n                throw ConversionException::conversionFailedFormat(\n                    $value,\n                    $this->getName(),\n                    $platform->getDateTimeFormatString()\n                );\n            }\n\n            return $converted;\n        }\n\n        private static function getUtc(): DateTimeZone\n        {\n            return self::$utc ??= new DateTimeZone('UTC');\n        }\n    }\n\nThis database type makes sure that every DateTime instance is always saved in UTC, relative\nto the current timezone that the passed DateTime instance has.\n\nTo actually use this new type instead of the default ``datetime`` type, you need to run following\ncode before bootstrapping the ORM:\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\DBAL\\Types\\Type;\n    use DoctrineExtensions\\DBAL\\Types\\UTCDateTimeType;\n\n    Type::overrideType('datetime', UTCDateTimeType::class);\n    Type::overrideType('datetimetz', UTCDateTimeType::class);\n\n\nTo be able to transform these values\nback into their real timezone you have to save the timezone in a separate field of the entity\nrequiring timezoned datetimes:\n\n.. code-block:: php\n\n    <?php\n    namespace Shipping;\n\n     #[Entity]\n    class Event\n    {\n        #[Column(type: 'datetime')]\n        private $created;\n\n        #[Column(type: 'string')]\n        private $timezone;\n\n        /**\n         * @var bool\n         */\n        private $localized = false;\n\n        public function __construct(\\DateTime $createDate)\n        {\n            $this->localized = true;\n            $this->created = $createDate;\n            $this->timezone = $createDate->getTimeZone()->getName();\n        }\n\n        public function getCreated()\n        {\n            if (!$this->localized) {\n                $this->created->setTimeZone(new \\DateTimeZone($this->timezone));\n            }\n            return $this->created;\n        }\n    }\n\nThis snippet makes use of the previously discussed \"changeset by reference only\" property of\nobjects. That means a new DateTime will only be used during updating if the reference\nchanges between retrieval and flush operation. This means we can easily go and modify\nthe instance by setting the previous local timezone.\n"
  },
  {
    "path": "docs/en/index.rst",
    "content": "Welcome to Doctrine ORM's documentation!\n==========================================\n\nThe Doctrine documentation is comprised of tutorials, a reference section and\ncookbook articles that explain different parts of the Object Relational mapper.\n\nDoctrine DBAL and Doctrine Common both have their own documentation.\n\nGetting Help\n------------\n\nIf this documentation is not helping to answer questions you have about\nDoctrine ORM don't panic. You can get help from different sources:\n\n-  There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.\n-  Slack chat room `#orm <https://www.doctrine-project.org/slack>`_\n-  Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.\n-  On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_\n\nIf you need more structure over the different topics you can browse the table\nof contents.\n\nGetting Started\n---------------\n\n* **Tutorial**:\n  :doc:`Getting Started with Doctrine <tutorials/getting-started>`\n\n* **Setup**:\n  :doc:`Installation & Configuration <reference/configuration>`\n\nMapping Objects onto a Database\n-------------------------------\n\n* **Mapping**:\n  :doc:`Objects <reference/basic-mapping>` \\|\n  :doc:`Associations <reference/association-mapping>` \\|\n  :doc:`Inheritance <reference/inheritance-mapping>`\n\n* **Drivers**:\n  :doc:`Attributes <reference/attributes-reference>` \\|\n  :doc:`XML <reference/xml-mapping>` \\|\n  :doc:`PHP <reference/php-mapping>`\n\nWorking with Objects\n--------------------\n\n* **Basic Reference**:\n  :doc:`Entities <reference/working-with-objects>` \\|\n  :doc:`Associations <reference/working-with-associations>` \\|\n  :doc:`Events <reference/events>`\n\n* **Query Reference**:\n  :doc:`DQL <reference/dql-doctrine-query-language>` \\|\n  :doc:`QueryBuilder <reference/query-builder>` \\|\n  :doc:`Native SQL <reference/native-sql>`\n\n* **Internals**:\n  :doc:`Internals explained <reference/unitofwork>` \\|\n  :doc:`Associations <reference/unitofwork-associations>`\n\nAdvanced Topics\n---------------\n\n* :doc:`Architecture <reference/architecture>`\n* :doc:`Advanced Configuration <reference/advanced-configuration>`\n* :doc:`Limitations and known issues <reference/limitations-and-known-issues>`\n* :doc:`Commandline Tools <reference/tools>`\n* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`\n* :doc:`Filters <reference/filters>`\n* :doc:`NamingStrategy <reference/namingstrategy>`\n* :doc:`TypedFieldMapper <reference/typedfieldmapper>`\n* :doc:`Improving Performance <reference/improving-performance>`\n* :doc:`Caching <reference/caching>`\n* :doc:`Partial Hydration <reference/partial-hydration>`\n* :doc:`Partial Objects <reference/partial-objects>`\n* :doc:`Change Tracking Policies <reference/change-tracking-policies>`\n* :doc:`Best Practices <reference/best-practices>`\n* :doc:`Metadata Drivers <reference/metadata-drivers>`\n* :doc:`Batch Processing <reference/batch-processing>`\n* :doc:`Second Level Cache <reference/second-level-cache>`\n\nTutorials\n---------\n\n* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`\n* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`\n* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`\n* :doc:`Ordered associations <tutorials/ordered-associations>`\n* :doc:`Pagination <tutorials/pagination>`\n* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`\n* :doc:`Embeddables <tutorials/embeddables>`\n\nChangelogs\n----------\n\n* `Upgrade <https://github.com/doctrine/orm/blob/HEAD/UPGRADE.md>`_\n\nCookbook\n--------\n\n* **Patterns**:\n  :doc:`Aggregate Fields <cookbook/aggregate-fields>` \\|\n  :doc:`Generated/Virtual Columns <cookbook/generated-columns>` \\|\n  :doc:`Decorator Pattern <cookbook/decorator-pattern>` \\|\n  :doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`\n\n* **DQL Extension Points**:\n  :doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` \\|\n  :doc:`DQL User-Defined-Functions <cookbook/dql-user-defined-functions>`\n\n* **Implementation**:\n  :doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` \\|\n  :doc:`Working with DateTime <cookbook/working-with-datetime>` \\|\n  :doc:`Validation <cookbook/validation-of-entities>` \\|\n  :doc:`Entities in the Session <cookbook/entities-in-session>` \\|\n  :doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`\n\n* **Hidden Gems**\n  :doc:`Prefixing Table Name <cookbook/sql-table-prefixes>`\n\n* **Custom Datatypes**\n  :doc:`MySQL Enums <cookbook/mysql-enums>`\n  :doc:`Custom Mapping Types <cookbook/custom-mapping-types>`\n  :doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`\n"
  },
  {
    "path": "docs/en/reference/advanced-configuration.rst",
    "content": "Advanced Configuration\n======================\n\nThe configuration of the EntityManager requires a\n``Doctrine\\ORM\\Configuration`` instance as well as some database\nconnection parameters. This example shows all the potential\nsteps of configuration.\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\ORM\\Configuration;\n    use Doctrine\\ORM\\EntityManager;\n    use Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\n    use Doctrine\\ORM\\ORMSetup;\n    use Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n    use Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter;\n\n    // ...\n\n    if ($applicationMode === \"development\") {\n        $queryCache = new ArrayAdapter();\n        $metadataCache = new ArrayAdapter();\n    } else {\n        $queryCache = new PhpFilesAdapter('doctrine_queries');\n        $metadataCache = new PhpFilesAdapter('doctrine_metadata');\n    }\n\n    $config = new Configuration;\n    $config->setMetadataCache($metadataCache);\n    $driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);\n    $config->setMetadataDriverImpl($driverImpl);\n    $config->setQueryCache($queryCache);\n\n    if (PHP_VERSION_ID > 80400) {\n        $config->enableNativeLazyObjects(true);\n    } else {\n        $config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');\n        $config->setProxyNamespace('MyProject\\Proxies');\n\n        if ($applicationMode === \"development\") {\n            $config->setAutoGenerateProxyClasses(true);\n        } else {\n            $config->setAutoGenerateProxyClasses(false);\n        }\n    }\n\n    $connection = DriverManager::getConnection([\n        'driver' => 'pdo_sqlite',\n        'path' => 'database.sqlite',\n    ], $config);\n\n    $em = new EntityManager($connection, $config);\n\nDoctrine and Caching\n--------------------\n\nDoctrine is optimized for working with caches. The main parts in Doctrine\nthat are optimized for caching are the metadata mapping information with\nthe metadata cache and the DQL to SQL conversions with the query cache.\nThese 2 caches require only an absolute minimum of memory yet they heavily\nimprove the runtime performance of Doctrine.\n\nDoctrine does not bundle its own cache implementation anymore. Instead,\nthe PSR-6 standard interfaces are used to access the cache. In the examples\nin this documentation, Symfony Cache is used as a reference implementation.\n\n.. note::\n\n    Do not use Doctrine without a metadata and query cache!\n\nConfiguration Options\n---------------------\n\nThe following sections describe all the configuration options\navailable on a ``Doctrine\\ORM\\Configuration`` instance.\n\n.. _reference-native-lazy-objects:\n\nNative Lazy Objects (**OPTIONAL**)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWith PHP 8.4 we recommend that you use native lazy objects instead of\nthe code generation approach using the ``symfony/var-exporter`` Ghost trait.\n\nWith Doctrine 4, the minimal requirement will become PHP 8.4 and native lazy objects\nwill become the only approach to lazy loading.\n\n.. code-block:: php\n\n    <?php\n    $config->enableNativeLazyObjects(true);\n\nProxy Directory\n~~~~~~~~~~~~~~~\n\nRequired except if you use native lazy objects with PHP 8.4.\nThis setting will be removed in the future.\n\n.. code-block:: php\n\n    <?php\n    $config->setProxyDir($dir);\n    $config->getProxyDir();\n\nGets or sets the directory where Doctrine generates any proxy\nclasses. For a detailed explanation on proxy classes and how they\nare used in Doctrine, refer to the \"Proxy Objects\" section further\ndown.\n\nProxy Namespace\n~~~~~~~~~~~~~~~\n\nRequired except if you use native lazy objects with PHP 8.4.\nThis setting will be removed in the future.\n\n.. code-block:: php\n\n    <?php\n    $config->setProxyNamespace($namespace);\n    $config->getProxyNamespace();\n\nGets or sets the namespace to use for generated proxy classes. For\na detailed explanation on proxy classes and how they are used in\nDoctrine, refer to the \"Proxy Objects\" section further down.\n\nMetadata Driver (**REQUIRED**)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    <?php\n    $config->setMetadataDriverImpl($driver);\n    $config->getMetadataDriverImpl();\n\nGets or sets the metadata driver implementation that is used by\nDoctrine to acquire the object-relational metadata for your\nclasses.\n\nThere are currently 3 available implementations:\n\n\n-  ``Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver``\n-  ``Doctrine\\ORM\\Mapping\\Driver\\XmlDriver``\n-  ``Doctrine\\ORM\\Mapping\\Driver\\DriverChain``\n\nThroughout the most part of this manual the AttributeDriver is\nused in the examples. For information on the usage of the\nXmlDriver please refer to the dedicated chapter ``XML Mapping``.\n\nThe attribute driver can be injected in the ``Doctrine\\ORM\\Configuration``:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\n\n    $driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);\n    $config->setMetadataDriverImpl($driverImpl);\n\nThe path information to the entities is required for the attribute\ndriver, because otherwise mass-operations on all entities through\nthe console could not work correctly. Metadata drivers can accept either\na single directory as a string or an array of directories.\n\nAttributeDriver also accepts ``Doctrine\\Persistence\\Mapping\\Driver\\ClassLocator``,\nallowing one to customize file discovery logic. You may choose to use Symfony Finder, or\nutilize directory scan with ``FileClassLocator::createFromDirectories()``:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\n\tuse Doctrine\\Persistence\\Mapping\\Driver\\FileClassLocator;\n\n\t$paths = ['/path/to/lib/MyProject/Entities'];\n\t$classLocator = FileClassLocator::createFromDirectories($paths);\n\n    $driverImpl = new AttributeDriver($classLocator);\n    $config->setMetadataDriverImpl($driverImpl);\n\nWith this feature, you're empowered to provide a fine-grained iterator of only necessary\nfiles to the Driver. For example, if you are using Vertical Slice architecture, you can\nexclude ``*Test.php``, ``*Controller.php``, ``*Service.php``, etc.:\n\n.. code-block:: php\n\n    <?php\n\tuse Symfony\\Component\\Finder\\Finder;\n\n\t$finder = new Finder()->files()->in($paths)\n\t\t->name('*.php')\n\t\t->notName(['*Test.php', '*Controller.php', '*Service.php']);\n\n\t$classLocator = new FileClassLocator($finder);\n\nIf you know the list of class names you want to track, use\n``Doctrine\\Persistence\\Mapping\\Driver\\ClassNames``:\n\n.. code-block:: php\n\n    <?php\n\tuse Doctrine\\Persistence\\Mapping\\Driver\\ClassNames;\n\tuse App\\Entity\\{Article, Book};\n\n\t$entityClasses = [Article::class, Book::class];\n\t$classLocator = new ClassNames($entityClasses);\n\n\t$driverImpl = new AttributeDriver($classLocator);\n\t$config->setMetadataDriverImpl($driverImpl);\n\nMetadata Cache (**RECOMMENDED**)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    <?php\n    $config->setMetadataCache($cache);\n    $config->getMetadataCache();\n\nGets or sets the cache adapter to use for caching metadata\ninformation, that is, all the information you supply via attributes,\nxml, so that they do not need to be parsed and loaded from scratch on\nevery single request which is a waste of resources. The cache\nimplementation must implement the PSR-6\n``Psr\\Cache\\CacheItemPoolInterface`` interface.\n\nUsage of a metadata cache is highly recommended.\n\nFor development you should use an array cache like\n``Symfony\\Component\\Cache\\Adapter\\ArrayAdapter``\nwhich only caches data on a per-request basis.\n\nQuery Cache (**RECOMMENDED**)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    <?php\n    $config->setQueryCache($cache);\n    $config->getQueryCache();\n\nGets or sets the cache implementation to use for caching DQL\nqueries, that is, the result of a DQL parsing process that includes\nthe final SQL as well as meta information about how to process the\nSQL result set of a query. Note that the query cache does not\naffect query results. You do not get stale data. This is a pure\noptimization cache without any negative side-effects (except some\nminimal memory usage in your cache).\n\nUsage of a query cache is highly recommended.\n\nFor development you should use an array cache like\n``Symfony\\Component\\Cache\\Adapter\\ArrayAdapter``\nwhich only caches data on a per-request basis.\n\nSQL Logger (**Optional**)\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    <?php\n    $config->setSQLLogger($logger);\n    $config->getSQLLogger();\n\nGets or sets the logger to use for logging all SQL statements\nexecuted by Doctrine. The logger class must implement the\ndeprecated ``Doctrine\\DBAL\\Logging\\SQLLogger`` interface.\n\nAuto-generating Proxy Classes (**OPTIONAL**)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis setting is not required if you use native lazy objects with PHP 8.4\nand will be removed in the future.\n\nProxy classes can either be generated manually through the Doctrine\nConsole or automatically at runtime by Doctrine. The configuration\noption that controls this behavior is:\n\n.. code-block:: php\n\n    <?php\n    $config->setAutoGenerateProxyClasses($mode);\n\nPossible values for ``$mode`` are:\n\n-  ``Doctrine\\ORM\\Proxy\\ProxyFactory::AUTOGENERATE_NEVER``\n\nNever autogenerate a proxy. You will need to generate the proxies\nmanually, for this use the Doctrine Console like so:\n\n.. code-block:: php\n\n    $ ./doctrine orm:generate-proxies\n\nWhen you do this in a development environment,\nbe aware that you may get class/file not found errors if certain proxies\nare not yet generated. You may also get failing lazy-loads if new\nmethods were added to the entity class that are not yet in the proxy class.\nIn such a case, simply use the Doctrine Console to (re)generate the\nproxy classes.\n\n-  ``Doctrine\\ORM\\Proxy\\ProxyFactory::AUTOGENERATE_ALWAYS``\n\nAlways generates a new proxy in every request and writes it to disk.\n\n-  ``Doctrine\\ORM\\Proxy\\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``\n\nGenerate the proxy class when the proxy file does not exist.\nThis strategy causes a file exists call whenever any proxy is\nused the first time in a request.\n\n-  ``Doctrine\\ORM\\Proxy\\ProxyFactory::AUTOGENERATE_EVAL``\n\nGenerate the proxy classes and evaluate them on the fly via eval(),\navoiding writing the proxies to disk.\nThis strategy is only sane for development.\n\nIn a production environment, it is highly recommended to use\nAUTOGENERATE_NEVER to allow for optimal performances. The other\noptions are interesting in development environment.\n\n``setAutoGenerateProxyClasses`` can accept a boolean\nvalue. This is still possible, ``FALSE`` being equivalent to\nAUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.\n\nDevelopment vs Production Configuration\n---------------------------------------\n\nYou should code your Doctrine2 bootstrapping with two different\nruntime models in mind. There are some serious benefits of using\nAPCu or Memcache in production. In development however this will\nfrequently give you fatal errors, when you change your entities and\nthe cache still keeps the outdated metadata. That is why we\nrecommend an array cache for development.\n\nFurthermore you should have the Auto-generating Proxy Classes\noption to true in development and to false in production. If this\noption is set to ``TRUE`` it can seriously hurt your script\nperformance if several proxy classes are re-generated during script\nexecution. Filesystem calls of that magnitude can even slower than\nall the database queries Doctrine issues. Additionally writing a\nproxy sets an exclusive file lock which can cause serious\nperformance bottlenecks in systems with regular concurrent\nrequests.\n\nConnection\n----------\n\nThe ``$connection`` passed as the first argument to the constructor of\n``EntityManager`` has to be an instance of ``Doctrine\\DBAL\\Connection``.\nYou can use the factory ``Doctrine\\DBAL\\DriverManager::getConnection()``\nto create such a connection. The DBAL configuration is explained in the\n`DBAL section <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/configuration.html>`_.\n\nProxy Objects\n-------------\n\nA proxy object is an object that is put in place or used instead of\nthe \"real\" object. A proxy object can add behavior to the object\nbeing proxied without that object being aware of it. In ORM,\nproxy objects are used to realize several features but mainly for\ntransparent lazy-loading.\n\nProxy objects with their lazy-loading facilities help to keep the\nsubset of objects that are already in memory connected to the rest\nof the objects. This is an essential property as without it there\nwould always be fragile partial objects at the outer edges of your\nobject graph.\n\nDoctrine ORM implements a variant of the proxy pattern where it\ngenerates classes that extend your entity classes and adds\nlazy-loading capabilities to them. Doctrine can then give you an\ninstance of such a proxy class whenever you request an object of\nthe class being proxied. This happens in two situations:\n\nReference Proxies\n~~~~~~~~~~~~~~~~~\n\nThe method ``EntityManager#getReference($entityName, $identifier)``\nlets you obtain a reference to an entity for which the identifier\nis known, without necessarily loading that entity from the database.\nThis is useful, for example, as a performance enhancement, when you\nwant to establish an association to an entity for which you have the\nidentifier.\n\nConsider the following example:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager, $cart instanceof MyProject\\Model\\Cart\n    // $itemId comes from somewhere, probably a request parameter\n    $item = $em->getReference('MyProject\\Model\\Item', $itemId);\n    $cart->addItem($item);\n\nWhether the object being returned from ``EntityManager#getReference()``\nis a proxy or a direct instance of the entity class may depend on different\nfactors, including whether the entity has already been loaded into memory\nor entity inheritance being used. But your code does not need to care\nand in fact it **should not care**. Proxy objects should be transparent to your\ncode.\n\nWhen using the ``EntityManager#getReference()`` method, you need to be aware\nof a few peculiarities.\n\nAt the best case, the ORM can avoid querying the database at all. But, that\nalso means that this method will not throw an exception when an invalid value\nfor the ``$identifier`` parameter is passed. ``$identifier`` values are\nnot checked and there is no guarantee that the requested entity instance even\nexists – the method will still return a proxy object.\n\nIts only when the proxy has to be fully initialized or associations cannot\nbe written to the database that invalid ``$identifier`` values may lead to\nexceptions.\n\nThe ``EntityManager#getReference()`` is mostly useful when you only\nneed a reference to some entity to make an association, like in the example\nabove. In that case, it can save you from loading data from the database\nthat you don't need. But remember – as soon as you read any property values\nbesides those making up the ID, a database request will be made to initialize\nall fields.\n\nAssociation proxies\n~~~~~~~~~~~~~~~~~~~\n\nThe second most important situation where Doctrine uses proxy\nobjects is when querying for objects. Whenever you query for an\nobject that has a single-valued association to another object that\nis configured LAZY, without joining that association in the same\nquery, Doctrine puts proxy objects in place where normally the\nassociated object would be. Just like other proxies it will\ntransparently initialize itself on first access.\n\n.. note::\n\n    Joining an association in a DQL or native query\n    essentially means eager loading of that association in that query.\n    This will override the 'fetch' option specified in the mapping for\n    that association, but only for that query.\n\n\nGenerating Proxy classes\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn a production environment, it is highly recommended to use\n``AUTOGENERATE_NEVER`` to allow for optimal performances.\nHowever you will be required to generate the proxies manually\nusing the Doctrine Console:\n\n.. code-block:: php\n\n    $ ./doctrine orm:generate-proxies\n\nThe other options are interesting in development environment:\n\n- ``AUTOGENERATE_ALWAYS`` will require you to create and configure\n  a proxy directory. Proxies will be generated and written to file\n  on each request, so any modification to your code will be acknowledged.\n\n- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing\n  proxy file. If your code changes, you will need to regenerate the\n  proxies manually.\n\n- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,\n  but without writing them to disk.\n\nAutoloading Proxies\n-------------------\n\nWhen you deserialize proxy objects from the session or any other storage\nit is necessary to have an autoloading mechanism in place for these classes.\nFor implementation reasons Proxy class names are not PSR-0 compliant. This\nmeans that you have to register a special autoloader for these classes:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Proxy\\Autoloader;\n\n    $proxyDir = \"/path/to/proxies\";\n    $proxyNamespace = \"MyProxies\";\n\n    Autoloader::register($proxyDir, $proxyNamespace);\n\nIf you want to execute additional logic to intercept the proxy file not found\nstate you can pass a closure as the third argument. It will be called with\nthe arguments proxydir, namespace and className when the proxy file could not\nbe found.\n\nMultiple Metadata Sources\n-------------------------\n\nWhen using different components using Doctrine ORM you may end up\nwith them using two different metadata drivers, for example XML and\nPHP. You can use the MappingDriverChain Metadata implementations to\naggregate these drivers based on namespaces:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain;\n\n    $chain = new MappingDriverChain();\n    $chain->addDriver($xmlDriver, 'Doctrine\\Tests\\Models\\Company');\n    $chain->addDriver($phpDriver, 'Doctrine\\Tests\\ORM\\Mapping');\n\nBased on the namespace of the entity the loading of entities is\ndelegated to the appropriate driver. The chain semantics come from\nthe fact that the driver loops through all namespaces and matches\nthe entity class name against the namespace using a\n``strpos() === 0`` call. This means you need to order the drivers\ncorrectly if sub-namespaces use different metadata driver\nimplementations.\n\n\nDefault Repository (**OPTIONAL**)\n-----------------------------------\n\nSpecifies the FQCN of a subclass of the EntityRepository.\nThat will be available for all entities without a custom repository class.\n\n.. code-block:: php\n\n    <?php\n    $config->setDefaultRepositoryClassName($fqcn);\n    $config->getDefaultRepositoryClassName();\n\nThe default value is ``Doctrine\\ORM\\EntityRepository``.\nAny repository class must be a subclass of EntityRepository otherwise you got an ORMException\n\nIgnoring entities (**OPTIONAL**)\n-----------------------------------\n\nSpecifies the Entity FQCNs to ignore.\nSchemaTool will then skip these (e.g. when comparing schemas).\n\n.. code-block:: php\n\n    <?php\n    $config->setSchemaIgnoreClasses([$fqcn]);\n    $config->getSchemaIgnoreClasses();\n\n\nSetting up the Console\n----------------------\n\nDoctrine uses the Symfony Console component for generating the command\nline interface. You can take a look at the\n:doc:`tools chapter <../reference/tools>` for inspiration how to setup the cli.\n"
  },
  {
    "path": "docs/en/reference/architecture.rst",
    "content": "Architecture\n============\n\nThis chapter gives an overview of the overall architecture,\nterminology and constraints of Doctrine ORM. It is recommended to\nread this chapter carefully.\n\nUsing an Object-Relational Mapper\n---------------------------------\n\nAs the term ORM already hints at, Doctrine ORM aims to simplify the\ntranslation between database rows and the PHP object model. The\nprimary use case for Doctrine are therefore applications that\nutilize the Object-Oriented Programming Paradigm. For applications\nthat do not primarily work with objects Doctrine ORM is not suited very\nwell.\n\nRequirements\n------------\n\nDoctrine ORM requires a minimum of PHP 8.1. For greatly improved\nperformance it is also recommended that you use APC with PHP.\n\nDoctrine ORM Packages\n-------------------\n\nDoctrine ORM is divided into four main packages.\n\n-  `Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html>`_\n-  `Event Manager <https://www.doctrine-project.org/projects/doctrine-event-manager/en/stable/index.html>`_\n-  `Persistence <https://www.doctrine-project.org/projects/doctrine-persistence/en/stable/index.html>`_\n-  `DBAL <https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/index.html>`_\n-  ORM (depends on DBAL+Persistence+Collections)\n\nThis manual mainly covers the ORM package, sometimes touching parts\nof the underlying DBAL and Persistence packages. The Doctrine codebase\nis split into these packages for a few reasons:\n\n\n-  to make things more maintainable and decoupled\n-  to allow you to use the code in Doctrine Persistence and Collections without the ORM or DBAL\n-  to allow you to use the DBAL without the ORM\n\nCollection, Event Manager and Persistence\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe Collection, Event Manager and Persistence packages contain highly\nreusable components that have no dependencies beyond the packages\nthemselves (and PHP, of course). The root namespace of the Persistence\npackage is ``Doctrine\\Persistence``. The root namespace of the\nCollection package is ``Doctrine\\Common\\Collections``, for historical\nreasons. The root namespace of the Event Manager package is just\n``Doctrine\\Common``, also for historical reasons.\n\nThe DBAL Package\n~~~~~~~~~~~~~~~~\n\nThe DBAL package contains an enhanced database abstraction layer on\ntop of PDO but is not strongly bound to PDO. The purpose of this\nlayer is to provide a single API that bridges most of the\ndifferences between the different RDBMS vendors. The root namespace\nof the DBAL package is ``Doctrine\\DBAL``.\n\nThe ORM Package\n~~~~~~~~~~~~~~~\n\nThe ORM package contains the object-relational mapping toolkit that\nprovides transparent relational persistence for plain PHP objects.\nThe root namespace of the ORM package is ``Doctrine\\ORM``.\n\nTerminology\n-----------\n\n.. _terminology_entities:\n\nEntities\n~~~~~~~~\n\nAn entity is a lightweight, persistent domain object. An entity can\nbe any regular PHP class observing the following restrictions:\n\n-  An entity class can be final or read-only when\n   you use :ref:`native lazy objects <reference-native-lazy-objects>`.\n   It may contain final methods or read-only properties too.\n-  Any two entity classes in a class hierarchy that inherit\n   directly or indirectly from one another must not have a mapped\n   property with the same name. That is, if B inherits from A then B\n   must not have a mapped field with the same name as an already\n   mapped field that is inherited from A.\n\nEntities support inheritance, polymorphic associations, and\npolymorphic queries. Both abstract and concrete classes can be\nentities. Entities may extend non-entity classes as well as entity\nclasses, and non-entity classes may extend entity classes.\n\n.. note::\n\n    The constructor of an entity is only ever invoked when\n    *you* construct a new instance with the *new* keyword. Doctrine\n    never calls entity constructors, thus you are free to use them as\n    you wish and even have it require arguments of any type.\n\nMapped Superclasses\n~~~~~~~~~~~~~~~~~~~\n\nA mapped superclass is an abstract or concrete class that provides\npersistent entity state and mapping information for its subclasses,\nbut which is not itself an entity.\n\nMapped superclasses are explained in greater detail in the chapter\non :doc:`inheritance mapping </reference/inheritance-mapping>`.\n\nTransient Classes\n~~~~~~~~~~~~~~~~~\n\nThe term \"transient class\" appears in some places in the mapping\ndrivers as well as the code dealing with metadata handling.\n\nA transient class is a class that is neither an entity nor a mapped\nsuperclass. From the ORM's point of view, these classes can be\ncompletely ignored, and no class metadata is loaded for them at all.\n\nEntity states\n~~~~~~~~~~~~~\n\nAn entity instance can be characterized as being NEW, MANAGED,\nDETACHED or REMOVED.\n\n\n-  A NEW entity instance has no persistent identity, and is not yet\n   associated with an EntityManager and a UnitOfWork (i.e. those just\n   created with the \"new\" operator).\n-  A MANAGED entity instance is an instance with a persistent\n   identity that is associated with an EntityManager and whose\n   persistence is thus managed.\n-  A DETACHED entity instance is an instance with a persistent\n   identity that is not (or no longer) associated with an\n   EntityManager and a UnitOfWork.\n-  A REMOVED entity instance is an instance with a persistent\n   identity, associated with an EntityManager, that will be removed\n   from the database upon transaction commit.\n\n.. _architecture_persistent_fields:\n\nPersistent fields\n~~~~~~~~~~~~~~~~~\n\nThe persistent state of an entity is represented by instance\nvariables. An instance variable must be directly accessed only from\nwithin the methods of the entity by the entity instance itself.\nInstance variables must not be accessed by clients of the entity.\nThe state of the entity is available to clients only through the\nentity’s methods, i.e. accessor methods (getter/setter methods) or\nother business methods.\n\nCollection-valued persistent fields and properties must be defined\nin terms of the ``Doctrine\\Common\\Collections\\Collection``\ninterface. The collection implementation type may be used by the\napplication to initialize fields or properties before the entity is\nmade persistent. Once the entity becomes managed (or detached),\nsubsequent access must be through the interface type.\n\nSerializing entities\n~~~~~~~~~~~~~~~~~~~~\n\nSerializing entities can be problematic and is not really\nrecommended, at least not as long as an entity instance still holds\nreferences to proxy objects or is still managed by an EntityManager.\nBy default, serializing proxy objects does not initialize them. On\nunserialization, resulting objects are detached from the entity\nmanager and cannot be initialized anymore. You can implement the\n``__serialize()`` method if you want to change that behavior, but\nthen you need to ensure that you won't generate large serialized\nobject graphs and take care of circular associations.\n\nThe EntityManager\n~~~~~~~~~~~~~~~~~\n\nThe ``EntityManager`` class is a central access point to the\nfunctionality provided by Doctrine ORM. The ``EntityManager`` API is\nused to manage the persistence of your objects and to query for\npersistent objects.\n\nTransactional write-behind\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAn ``EntityManager`` and the underlying ``UnitOfWork`` employ a\nstrategy called \"transactional write-behind\" that delays the\nexecution of SQL statements in order to execute them in the most\nefficient way and to execute them at the end of a transaction so\nthat all write locks are quickly released. You should see Doctrine\nas a tool to synchronize your in-memory objects with the database\nin well defined units of work. Work with your objects and modify\nthem as usual and when you're done call ``EntityManager#flush()``\nto make your changes persistent.\n\n.. _unit-of-work:\n\nThe Unit of Work\n~~~~~~~~~~~~~~~~\n\nInternally an ``EntityManager`` uses a ``UnitOfWork``, which is a\ntypical implementation of the\n`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,\nto keep track of all the things that need to be done the next time\n``flush`` is invoked. You usually do not directly interact with a\n``UnitOfWork`` but with the ``EntityManager`` instead.\n"
  },
  {
    "path": "docs/en/reference/association-mapping.rst",
    "content": "Association Mapping\n===================\n\nThis chapter explains mapping associations between objects.\n\nInstead of working with foreign keys in your code, you will always work with\nreferences to objects instead and Doctrine will convert those references\nto foreign keys internally.\n\n- A reference to a single object is represented by a foreign key.\n- A collection of objects is represented by many foreign keys pointing to the object holding the collection\n\nThis chapter is split into three different sections.\n\n- A list of all the possible association mapping use-cases is given.\n- :ref:`association_mapping_defaults` are explained that simplify the use-case examples.\n- :ref:`collections` are introduced that contain entities in associations.\n\nOne tip for working with relations is to read the relation from left to right, where the left word refers to the current Entity. For example:\n\n- OneToMany - One instance of the current Entity has Many instances (references) to the referred Entity.\n- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.\n- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.\n\nSee below for all the possible relations.\n\nAn association is considered to be unidirectional if only one side of the association has\na property referring to the other side.\n\nTo gain a full understanding of associations you should also read about :doc:`owning and\ninverse sides of associations <unitofwork-associations>`\n\nMany-To-One, Unidirectional\n---------------------------\n\nA many-to-one association is the most common association between objects. Example: Many Users have One Address:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class User\n        {\n            // ...\n\n            #[ManyToOne(targetEntity: Address::class)]\n            #[JoinColumn(name: 'address_id', referencedColumnName: 'id')]\n            private Address|null $address = null;\n        }\n\n        #[Entity]\n        class Address\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <many-to-one field=\"address\" target-entity=\"Address\">\n                    <join-column name=\"address_id\" referenced-column-name=\"id\" />\n                </many-to-one>\n            </entity>\n        </doctrine-mapping>\n\n.. note::\n\n    The above ``#[JoinColumn]`` is optional as it would default\n    to ``address_id`` and ``id`` anyways. You can omit it and let it\n    use the defaults.\n    Likewise, inside the ``#[ManyToOne]`` attribute you can omit the\n    ``targetEntity`` argument and it will default to ``Address``.\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE User (\n        id INT AUTO_INCREMENT NOT NULL,\n        address_id INT DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE Address (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);\n\nOne-To-One, Unidirectional\n--------------------------\n\nHere is an example of a one-to-one association with a ``Product`` entity that\nreferences one ``Shipment`` entity.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class Product\n        {\n            // ...\n\n            /** One Product has One Shipment. */\n            #[OneToOne(targetEntity: Shipment::class)]\n            #[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]\n            private Shipment|null $shipment = null;\n\n            // ...\n        }\n\n        #[Entity]\n        class Shipment\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"Product\">\n                <one-to-one field=\"shipment\" target-entity=\"Shipment\">\n                    <join-column name=\"shipment_id\" referenced-column-name=\"id\" />\n                </one-to-one>\n            </entity>\n        </doctrine-mapping>\n\nNote that the ``#[JoinColumn]`` is not really necessary in this example,\nas the defaults would be the same.\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE Product (\n        id INT AUTO_INCREMENT NOT NULL,\n        shipment_id INT DEFAULT NULL,\n        UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipment_id),\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE Shipment (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Product ADD FOREIGN KEY (shipment_id) REFERENCES Shipment(id);\n\nOne-To-One, Bidirectional\n-------------------------\n\nHere is a one-to-one relationship between a ``Customer`` and a\n``Cart``. The ``Cart`` has a reference back to the ``Customer`` so\nit is bidirectional.\n\nHere we see the ``mappedBy`` and ``inversedBy`` attributes for the first time.\nThey are used to tell Doctrine which property on the other side refers to the\nobject.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class Customer\n        {\n            // ...\n\n            /** One Customer has One Cart. */\n            #[OneToOne(targetEntity: Cart::class, mappedBy: 'customer')]\n            private Cart|null $cart = null;\n\n            // ...\n        }\n\n        #[Entity]\n        class Cart\n        {\n            // ...\n\n            /** One Cart has One Customer. */\n            #[OneToOne(targetEntity: Customer::class, inversedBy: 'cart')]\n            #[JoinColumn(name: 'customer_id', referencedColumnName: 'id')]\n            private Customer|null $customer = null;\n\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"Customer\">\n                <one-to-one field=\"cart\" target-entity=\"Cart\" mapped-by=\"customer\" />\n            </entity>\n            <entity name=\"Cart\">\n                <one-to-one field=\"customer\" target-entity=\"Customer\" inversed-by=\"cart\">\n                    <join-column name=\"customer_id\" referenced-column-name=\"id\" />\n                </one-to-one>\n            </entity>\n        </doctrine-mapping>\n\nNote that the @JoinColumn is not really necessary in this example,\nas the defaults would be the same.\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE Cart (\n        id INT AUTO_INCREMENT NOT NULL,\n        customer_id INT DEFAULT NULL,\n        UNIQUE INDEX UNIQ_BA388B79395C3F3 (customer_id),\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE Customer (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id);\n\nWe had a choice of sides on which to place the ``inversedBy`` attribute. Because it\nis on the ``Cart``, that is the owning side of the relation, and thus holds the\nforeign key.\n\nOne-To-One, Self-referencing\n----------------------------\n\nYou can define a self-referencing one-to-one relationships like\nbelow.\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class Student\n    {\n        // ...\n\n        /** One Student has One Mentor. */\n        #[OneToOne(targetEntity: Student::class)]\n        #[JoinColumn(name: 'mentor_id', referencedColumnName: 'id')]\n        private Student|null $mentor = null;\n\n        // ...\n    }\n\nNote that the @JoinColumn is not really necessary in this example,\nas the defaults would be the same.\n\nWith the generated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE Student (\n        id INT AUTO_INCREMENT NOT NULL,\n        mentor_id INT DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id);\n\nOne-To-Many, Bidirectional\n--------------------------\n\nA one-to-many association has to be bidirectional, unless you are using a\njoin table. This is because the \"many\" side in a one-to-many association holds\nthe foreign key, making it the owning side. Doctrine needs the \"many\" side\ndefined in order to understand the association.\n\nThis bidirectional mapping requires the ``mappedBy`` attribute on the\n\"one\" side and the ``inversedBy`` attribute on the \"many\" side.\n\nThis means there is no difference between a bidirectional one-to-many and a\nbidirectional many-to-one.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\Common\\Collections\\ArrayCollection;\n\n        #[Entity]\n        class Product\n        {\n            // ...\n            /**\n             * One product has many features. This is the inverse side.\n             * @var Collection<int, Feature>\n             */\n            #[OneToMany(targetEntity: Feature::class, mappedBy: 'product')]\n            private Collection $features;\n            // ...\n\n            public function __construct() {\n                $this->features = new ArrayCollection();\n            }\n        }\n\n        #[Entity]\n        class Feature\n        {\n            // ...\n            /** Many features have one product. This is the owning side. */\n            #[ManyToOne(targetEntity: Product::class, inversedBy: 'features')]\n            #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n            private Product|null $product = null;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"Product\">\n                <one-to-many field=\"features\" target-entity=\"Feature\" mapped-by=\"product\" />\n            </entity>\n            <entity name=\"Feature\">\n                <many-to-one field=\"product\" target-entity=\"Product\" inversed-by=\"features\">\n                    <join-column name=\"product_id\" referenced-column-name=\"id\" />\n                </many-to-one>\n            </entity>\n        </doctrine-mapping>\n\nNote that the @JoinColumn is not really necessary in this example,\nas the defaults would be the same.\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE Product (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE Feature (\n        id INT AUTO_INCREMENT NOT NULL,\n        product_id INT DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);\n\nOne-To-Many, Unidirectional with Join Table\n-------------------------------------------\n\nA unidirectional one-to-many association can be mapped through a\njoin table. From Doctrine's point of view, it is simply mapped as a\nunidirectional many-to-many whereby a unique constraint on one of\nthe join columns enforces the one-to-many cardinality.\n\nThe following example sets up such a unidirectional one-to-many association:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class User\n        {\n            // ...\n\n            /**\n             * Many Users have Many Phonenumbers.\n             * @var Collection<int, Phonenumber>\n             */\n            #[JoinTable(name: 'users_phonenumbers')]\n            #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n            #[InverseJoinColumn(name: 'phonenumber_id', referencedColumnName: 'id', unique: true)]\n            #[ManyToMany(targetEntity: 'Phonenumber')]\n            private Collection $phonenumbers;\n\n            public function __construct()\n            {\n                $this->phonenumbers = new ArrayCollection();\n            }\n\n            // ...\n        }\n\n        #[Entity]\n        class Phonenumber\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <many-to-many field=\"phonenumbers\" target-entity=\"Phonenumber\">\n                    <join-table name=\"users_phonenumbers\">\n                        <join-columns>\n                            <join-column name=\"user_id\" referenced-column-name=\"id\" />\n                        </join-columns>\n                        <inverse-join-columns>\n                            <join-column name=\"phonenumber_id\" referenced-column-name=\"id\" unique=\"true\" />\n                        </inverse-join-columns>\n                    </join-table>\n                </many-to-many>\n            </entity>\n        </doctrine-mapping>\n\nGenerates the following MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE User (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE users_phonenumbers (\n        user_id INT NOT NULL,\n        phonenumber_id INT NOT NULL,\n        UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id),\n        PRIMARY KEY(user_id, phonenumber_id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE Phonenumber (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);\n    ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);\n\nOne-To-Many, Self-referencing\n-----------------------------\n\nYou can also setup a one-to-many association that is\nself-referencing. In this example we setup a hierarchy of\n``Category`` objects by creating a self referencing relationship.\nThis effectively models a hierarchy of categories and from the\ndatabase perspective is known as an adjacency list approach.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class Category\n        {\n            // ...\n            /**\n             * One Category has Many Categories.\n             * @var Collection<int, Category>\n             */\n            #[OneToMany(targetEntity: Category::class, mappedBy: 'parent')]\n            private Collection $children;\n\n            /** Many Categories have One Category. */\n            #[ManyToOne(targetEntity: Category::class, inversedBy: 'children')]\n            #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')]\n            private Category|null $parent = null;\n            // ...\n\n            public function __construct() {\n                $this->children = new ArrayCollection();\n            }\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"Category\">\n                <one-to-many field=\"children\" target-entity=\"Category\" mapped-by=\"parent\" />\n                <many-to-one field=\"parent\" target-entity=\"Category\" inversed-by=\"children\" />\n            </entity>\n        </doctrine-mapping>\n\nNote that the @JoinColumn is not really necessary in this example,\nas the defaults would be the same.\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE Category (\n        id INT AUTO_INCREMENT NOT NULL,\n        parent_id INT DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id);\n\nMany-To-Many, Unidirectional\n----------------------------\n\nReal many-to-many associations are less common. The following\nexample shows a unidirectional association between User and Group\nentities:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class User\n        {\n            // ...\n\n            /**\n             * Many Users have Many Groups.\n             * @var Collection<int, Group>\n             */\n            #[JoinTable(name: 'users_groups')]\n            #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n            #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n            #[ManyToMany(targetEntity: Group::class)]\n            private Collection $groups;\n\n            // ...\n\n            public function __construct() {\n                $this->groups = new ArrayCollection();\n            }\n        }\n\n        #[Entity]\n        class Group\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <many-to-many field=\"groups\" target-entity=\"Group\">\n                    <join-table name=\"users_groups\">\n                        <join-columns>\n                            <join-column name=\"user_id\" referenced-column-name=\"id\" />\n                        </join-columns>\n                        <inverse-join-columns>\n                            <join-column name=\"group_id\" referenced-column-name=\"id\" />\n                        </inverse-join-columns>\n                    </join-table>\n                </many-to-many>\n            </entity>\n        </doctrine-mapping>\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE User (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE users_groups (\n        user_id INT NOT NULL,\n        group_id INT NOT NULL,\n        PRIMARY KEY(user_id, group_id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE Group (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE users_groups ADD FOREIGN KEY (user_id) REFERENCES User(id);\n    ALTER TABLE users_groups ADD FOREIGN KEY (group_id) REFERENCES Group(id);\n\n.. note::\n\n    Why are many-to-many associations less common? Because\n    frequently you want to associate additional attributes with an\n    association, in which case you introduce an association class.\n    Consequently, the direct many-to-many association disappears and is\n    replaced by one-to-many/many-to-one associations between the 3\n    participating classes.\n\n.. note::\n\n    For many-to-many associations, the ORM takes care of managing rows\n    in the join table connecting both sides. Due to the way it deals\n    with entity removals, database-level constraints may not work the\n    way one might intuitively assume. Thus, be sure not to miss the section\n    on :ref:`join table management <remove_object_many_to_many_join_tables>`.\n\n\nMany-To-Many, Bidirectional\n---------------------------\n\nHere is a similar many-to-many relationship as above except this\none is bidirectional.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class User\n        {\n            // ...\n\n            /**\n             * Many Users have Many Groups.\n             * @var Collection<int, Group>\n             */\n            #[ManyToMany(targetEntity: Group::class, inversedBy: 'users')]\n            #[JoinTable(name: 'users_groups')]\n            private Collection $groups;\n\n            public function __construct() {\n                $this->groups = new ArrayCollection();\n            }\n\n            // ...\n        }\n\n        #[Entity]\n        class Group\n        {\n            // ...\n            /**\n             * Many Groups have Many Users.\n             * @var Collection<int, User>\n             */\n            #[ManyToMany(targetEntity: User::class, mappedBy: 'groups')]\n            private Collection $users;\n\n            public function __construct() {\n                $this->users = new ArrayCollection();\n            }\n\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <many-to-many field=\"groups\" inversed-by=\"users\" target-entity=\"Group\">\n                    <join-table name=\"users_groups\">\n                        <join-columns>\n                            <join-column name=\"user_id\" referenced-column-name=\"id\" />\n                        </join-columns>\n                        <inverse-join-columns>\n                            <join-column name=\"group_id\" referenced-column-name=\"id\" />\n                        </inverse-join-columns>\n                    </join-table>\n                </many-to-many>\n            </entity>\n\n            <entity name=\"Group\">\n                <many-to-many field=\"users\" mapped-by=\"groups\" target-entity=\"User\"/>\n            </entity>\n        </doctrine-mapping>\n\nThe MySQL schema is exactly the same as for the Many-To-Many\nuni-directional case above.\n\n.. note::\n\n    For many-to-many associations, the ORM takes care of managing rows\n    in the join table connecting both sides. Due to the way it deals\n    with entity removals, database-level constraints may not work the\n    way one might intuitively assume. Thus, be sure not to miss the section\n    on :ref:`join table management <remove_object_many_to_many_join_tables>`.\n\n\nOwning and Inverse Side on a ManyToMany Association\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor Many-To-Many associations you can chose which entity is the\nowning and which the inverse side. There is a very simple semantic\nrule to decide which side is more suitable to be the owning side\nfrom a developers perspective. You only have to ask yourself which\nentity is responsible for the connection management, and pick that\nas the owning side.\n\nTake an example of two entities ``Article`` and ``Tag``. Whenever\nyou want to connect an Article to a Tag and vice-versa, it is\nmostly the Article that is responsible for this relation. Whenever\nyou add a new article, you want to connect it with existing or new\ntags. Your \"Create Article\" form will probably support this notion\nand allow specifying the tags directly. This is why you should pick\nthe Article as owning side, as it makes the code more\nunderstandable:\n\n.. code-block:: php\n\n    <?php\n    class Article\n    {\n        private Collection $tags;\n\n        public function addTag(Tag $tag): void\n        {\n            $tag->addArticle($this); // synchronously updating inverse side\n            $this->tags[] = $tag;\n        }\n    }\n\n    class Tag\n    {\n        private Collection $articles;\n\n        public function addArticle(Article $article): void\n        {\n            $this->articles[] = $article;\n        }\n    }\n\nThis allows to group the tag adding on the ``Article`` side of the\nassociation:\n\n.. code-block:: php\n\n    <?php\n    $article = new Article();\n    $article->addTag($tagA);\n    $article->addTag($tagB);\n\nMany-To-Many, Self-referencing\n------------------------------\n\nYou can even have a self-referencing many-to-many association. A\ncommon scenario is where a ``User`` has friends and the target\nentity of that relationship is a ``User`` so it is self\nreferencing. In this example it is bidirectional so ``User`` has a\nfield named ``$friendsWithMe`` and ``$myFriends``.\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class User\n    {\n        // ...\n\n        /**\n         * Many Users have Many Users.\n         * @var Collection<int, User>\n         */\n        #[ManyToMany(targetEntity: User::class, mappedBy: 'myFriends')]\n        private Collection $friendsWithMe;\n\n        /**\n         * Many Users have many Users.\n         * @var Collection<int, User>\n         */\n        #[JoinTable(name: 'friends')]\n        #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n        #[InverseJoinColumn(name: 'friend_user_id', referencedColumnName: 'id')]\n        #[ManyToMany(targetEntity: 'User', inversedBy: 'friendsWithMe')]\n        private Collection $myFriends;\n\n        public function __construct() {\n            $this->friendsWithMe = new ArrayCollection();\n            $this->myFriends = new ArrayCollection();\n        }\n\n        // ...\n    }\n\nGenerated MySQL Schema:\n\n.. code-block:: sql\n\n    CREATE TABLE User (\n        id INT AUTO_INCREMENT NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE friends (\n        user_id INT NOT NULL,\n        friend_user_id INT NOT NULL,\n        PRIMARY KEY(user_id, friend_user_id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE friends ADD FOREIGN KEY (user_id) REFERENCES User(id);\n    ALTER TABLE friends ADD FOREIGN KEY (friend_user_id) REFERENCES User(id);\n\n.. _association_mapping_defaults:\n\nMapping Defaults\n----------------\n\nThe ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have\nsensible default values. The defaults for a join column in a\none-to-one/many-to-one association is as follows:\n\n::\n\n    name: \"<fieldname>_id\"\n    referencedColumnName: \"id\"\n\nAs an example, consider this mapping:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[OneToOne(targetEntity: Shipment::class)]\n        private Shipment|null $shipment = null;\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"Product\">\n                <one-to-one field=\"shipment\" target-entity=\"Shipment\" />\n            </entity>\n        </doctrine-mapping>\n\nThis is essentially the same as the following, more verbose,\nmapping:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        /** One Product has One Shipment. */\n        #[OneToOne(targetEntity: Shipment::class)]\n        #[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]\n        private Shipment|null $shipment = null;\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"Product\">\n                <one-to-one field=\"shipment\" target-entity=\"Shipment\">\n                    <join-column name=\"shipment_id\" referenced-column-name=\"id\" />\n                </one-to-one>\n            </entity>\n        </doctrine-mapping>\n\nThe @JoinTable definition used for many-to-many mappings has\nsimilar defaults. As an example, consider this mapping:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class User\n        {\n            // ...\n            /** @var Collection<int, Group> */\n            #[ManyToMany(targetEntity: Group::class)]\n            private Collection $groups;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"User\">\n                <many-to-many field=\"groups\" target-entity=\"Group\" />\n            </entity>\n        </doctrine-mapping>\n\nThis is essentially the same as the following, more verbose, mapping:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class User\n        {\n            // ...\n            /**\n             * Many Users have Many Groups.\n             * @var Collection<int, Group>\n             */\n            #[JoinTable(name: 'User_Group')]\n            #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n            #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n            #[ManyToMany(targetEntity: Group::class)]\n            private Collection $groups;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"User\">\n                <many-to-many field=\"groups\" target-entity=\"Group\">\n                    <join-table name=\"User_Group\">\n                        <join-columns>\n                            <join-column id=\"user_id\" referenced-column-name=\"id\" />\n                        </join-columns>\n                        <inverse-join-columns>\n                            <join-column id=\"group_id\" referenced-column-name=\"id\" />\n                        </inverse-join-columns>\n                    </join-table>\n                </many-to-many>\n            </entity>\n        </doctrine-mapping>\n\nIn that case, the name of the join table defaults to a combination\nof the simple, unqualified class names of the participating\nclasses, separated by an underscore character. The names of the\njoin columns default to the simple, unqualified class name of the\ntargeted class followed by \"\\_id\". The referencedColumnName always\ndefaults to \"id\", just as in one-to-one or many-to-one mappings.\n\nAdditionally, when using typed properties with Doctrine 2.9 or newer\nyou can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``\nassociations as they will be set based on type. So that:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[OneToOne]\n        private Shipment $shipment;\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"Product\">\n                <one-to-one field=\"shipment\" />\n            </entity>\n        </doctrine-mapping>\n\nIs essentially the same as following:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        /** One Product has One Shipment. */\n        #[OneToOne(targetEntity: Shipment::class)]\n        #[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]\n        private Shipment $shipment;\n\n    .. code-block:: annotation\n\n        <?php\n        /**\n         * One Product has One Shipment.\n         * @OneToOne(targetEntity=\"Shipment\")\n         * @JoinColumn(name=\"shipment_id\", referencedColumnName=\"id\")\n         */\n        private Shipment $shipment;\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity class=\"Product\">\n                <one-to-one field=\"shipment\" target-entity=\"Shipment\">\n                    <join-column name=\"shipment_id\" referenced-column-name=\"id\" nullable=false />\n                </one-to-one>\n            </entity>\n        </doctrine-mapping>\n\nIf you accept these defaults, you can reduce the mapping code to a\nminimum.\n\n.. _collections:\n\nCollections\n-----------\n\nUnfortunately, PHP arrays, while being great for many things, are missing\nfeatures that make them suitable for lazy loading in the context of an ORM.\nThis is why in all the examples of many-valued associations in this manual we\nwill make use of a ``Collection`` interface and its\ndefault implementation ``ArrayCollection`` that are both defined in the\n``Doctrine\\Common\\Collections`` namespace. A collection implements\nthe PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``.\n\n.. note::\n\n    The Collection interface and ArrayCollection class,\n    like everything else in the Doctrine namespace, are neither part of\n    the ORM, nor the DBAL, it is a plain PHP class that has no outside\n    dependencies apart from dependencies on PHP itself (and the SPL).\n    Therefore using this class in your model and elsewhere\n    does not introduce a coupling to the ORM.\n\nInitializing Collections\n------------------------\n\nYou should always initialize the collections of your ``@OneToMany``\nand ``@ManyToMany`` associations in the constructor of your entities:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Common\\Collections\\Collection;\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n\n    #[Entity]\n    class User\n    {\n        /** Many Users have Many Groups. */\n        #[ManyToMany(targetEntity: Group::class)]\n        private Collection $groups;\n\n        public function __construct()\n        {\n            $this->groups = new ArrayCollection();\n        }\n\n        public function getGroups(): Collection\n        {\n            return $this->groups;\n        }\n    }\n\nThe following code will then work even if the Entity hasn't\nbeen associated with an EntityManager yet:\n\n.. code-block:: php\n\n    <?php\n    $group = new Group();\n    $user = new User();\n    $user->getGroups()->add($group);\n"
  },
  {
    "path": "docs/en/reference/attributes-reference.rst",
    "content": "Attributes Reference\n====================\n\nPHP 8 adds native support for metadata with its \"Attributes\" feature.\nDoctrine ORM provides support for mapping metadata using PHP attributes as of version 2.9.\n\nThe attributes metadata support is closely modelled after the already\nexisting and now removed annotation metadata supported since the first\nversion 2.0.\n\nIndex\n-----\n\n-  :ref:`#[AssociationOverride] <attrref_associationoverride>`\n-  :ref:`#[AttributeOverride] <attrref_attributeoverride>`\n-  :ref:`#[Column] <attrref_column>`\n-  :ref:`#[Cache] <attrref_cache>`\n-  :ref:`#[ChangeTrackingPolicy] <attrref_changetrackingpolicy>`\n-  :ref:`#[CustomIdGenerator] <attrref_customidgenerator>`\n-  :ref:`#[DiscriminatorColumn] <attrref_discriminatorcolumn>`\n-  :ref:`#[DiscriminatorMap] <attrref_discriminatormap>`\n-  :ref:`#[Embeddable] <attrref_embeddable>`\n-  :ref:`#[Embedded] <attrref_embedded>`\n-  :ref:`#[Entity] <attrref_entity>`\n-  :ref:`#[GeneratedValue] <attrref_generatedvalue>`\n-  :ref:`#[HasLifecycleCallbacks] <attrref_haslifecyclecallbacks>`\n-  :ref:`#[Index] <attrref_index>`\n-  :ref:`#[Id] <attrref_id>`\n-  :ref:`#[InheritanceType] <attrref_inheritancetype>`\n-  :ref:`#[JoinColumn] <attrref_joincolumn>`\n-  :ref:`#[JoinTable] <attrref_jointable>`\n-  :ref:`#[ManyToOne] <attrref_manytoone>`\n-  :ref:`#[ManyToMany] <attrref_manytomany>`\n-  :ref:`#[MappedSuperclass] <attrref_mappedsuperclass>`\n-  :ref:`#[OneToOne] <attrref_onetoone>`\n-  :ref:`#[OneToMany] <attrref_onetomany>`\n-  :ref:`#[OrderBy] <attrref_orderby>`\n-  :ref:`#[PostLoad] <attrref_postload>`\n-  :ref:`#[PostPersist] <attrref_postpersist>`\n-  :ref:`#[PostRemove] <attrref_postremove>`\n-  :ref:`#[PostUpdate] <attrref_postupdate>`\n-  :ref:`#[PrePersist] <attrref_prepersist>`\n-  :ref:`#[PreRemove] <attrref_preremove>`\n-  :ref:`#[PreUpdate] <attrref_preupdate>`\n-  :ref:`#[SequenceGenerator] <attrref_sequencegenerator>`\n-  :ref:`#[Table] <attrref_table>`\n-  :ref:`#[UniqueConstraint] <attrref_uniqueconstraint>`\n-  :ref:`#[Version] <attrref_version>`\n\n\nReference\n---------\n\n.. _attrref_associationoverride:\n\n#[AssociationOverride]\n~~~~~~~~~~~~~~~~~~~~~~\n\nIn an inheritance hierarchy this attribute allows to override the\nassocation mapping definitions of the parent mappings. It needs to be nested\nwithin a ``#[AssociationOverrides]`` on the class level.\n\nRequired parameters:\n\n-  **name**: Name of the association mapping to overwrite.\n\nOptional parameters:\n\n-  **joinColumns**: A list of nested ``#[JoinColumn]`` declarations.\n-  **joinTable**: A nested ``#[JoinTable]`` declaration in case of a many-to-many overwrite.\n-  **inversedBy**: The name of the inversedBy field on the target entity side.\n-  **fetch**: The fetch strategy, one of: EAGER, LAZY, EXTRA_LAZY.\n\nExamples:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\AssociationOverride;\n    use Doctrine\\ORM\\Mapping\\AssociationOverrides;\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\Entity;\n\n    #[AssociationOverrides([\n        new AssociationOverride(\n            name: \"groups\",\n            joinTable: new JoinTable(\n                name: \"ddc964_users_admingroups\",\n            ),\n            joinColumns: [new JoinColumn(name: \"adminuser_id\")],\n            inverseJoinColumns: [new JoinColumn(name: \"admingroup_id\")]\n        ),\n        new AssociationOverride(\n            name: \"address\",\n            joinColumns: [new JoinColumn(name: \"adminaddress_id\", referencedColumnName: \"id\")]\n        )\n    ])]\n    class DDC964Admin extends DDC964User\n    {\n    }\n\n.. _attrref_attributeoverride:\n\n#[AttributeOverride]\n~~~~~~~~~~~~~~~~~~~~\n\nIn an inheritance hierarchy this attribute allows to override the\nfield mapping definitions of the parent mappings. It needs to be nested\nwithin a ``#[AttributeOverrides]`` on the class level.\n\nRequired parameters:\n\n-  **name**: Name of the association mapping to overwrite.\n-  **column**: A nested ``#[Column]`` attribute with the overwritten field settings.\n\nExamples:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\AttributeOverride;\n    use Doctrine\\ORM\\Mapping\\AttributeOverrides;\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\Entity;\n\n    #[Entity]\n    #[AttributeOverrides([\n        new AttributeOverride(\n            name: \"id\",\n            column: new Column(name: \"guest_id\", type: \"integer\", length: 140)\n        ),\n        new AttributeOverride(\n            name: \"name\",\n            column: new Column(name: \"guest_name\", nullable: false, unique: true, length: 240)\n        )]\n    )]\n    class DDC964Guest extends DDC964User\n    {\n    }\n\n.. _attrref_column:\n\n#[Column]\n~~~~~~~~~\n\nMarks an annotated instance variable as \"persistent\". It has to be\ninside the instance variables PHP DocBlock comment. Any value hold\ninside this variable will be saved to and loaded from the database\nas part of the lifecycle of the instance variables entity-class.\n\nRequired parameters:\n\n-  **type**: Name of the DBAL Type which does the conversion between PHP\n   and Database representation.\n\nOptional parameters:\n\n-  **name**: By default the property name is used for the database\n   column name also, however the ``name`` attribute allows you to\n   determine the column name.\n\n-  **length**: Used by the ``string`` type to determine its maximum\n   length in the database. Doctrine does not validate the length of a\n   string value for you.\n\n-  **precision**: The precision for a decimal (exact numeric) column\n   (applies only for decimal column), which is the maximum number of\n   digits that are stored for the values.\n\n-  **scale**: The scale for a decimal (exact numeric) column (applies\n   only for decimal column), which represents the number of digits\n   to the right of the decimal point and must not be greater than\n   *precision*.\n\n-  **unique**: Boolean value to determine if the value of the column\n   should be unique across all rows of the underlying entities table.\n\n-  **index**: Boolean value to generate an index for this column.\n   For more advanced usages, take a look at :ref:`#[Index] <attrref_index>`.\n   If not specified, default value is ``false``.\n\n-  **nullable**: Determines if NULL values allowed for this column.\n    If not specified, default value is ``false``.\n\n-  **insertable**: Boolean value to determine if the column should be\n   included when inserting a new row into the underlying entities table.\n   If not specified, default value is true.\n\n-  **updatable**: Boolean value to determine if the column should be\n   included when updating the row of the underlying entities table.\n   If not specified, default value is true.\n\n-  **generated**: An enum with the possible values ALWAYS, INSERT, NEVER.  Is\n   used after an INSERT or UPDATE statement to determine if the database\n   generated this value and it needs to be fetched using a SELECT statement.\n\n-  **options**: Array of additional options:\n\n   -  ``default``: The default value to set for the column if no value\n      is supplied.\n\n   -  ``unsigned``: Boolean value to determine if the column should\n      be capable of representing only non-negative integers\n      (applies only for integer column and might not be supported by\n      all vendors).\n\n   -  ``fixed``: Boolean value to determine if the specified length of\n      a string column should be fixed or varying (applies only for\n      string/binary column and might not be supported by all vendors).\n\n   -  ``comment``: The comment of the column in the schema (might not\n      be supported by all vendors).\n\n   -  ``charset``: The charset of the column (only supported by Mysql, PostgreSQL, Sqlite and SQLServer).\n\n   -  ``collation``: The collation of the column (only supported by Mysql, PostgreSQL, Sqlite and SQLServer).\n\n   -  ``check``: Adds a check constraint type to the column (might not\n      be supported by all vendors).\n\n-  **columnDefinition**: Specify the DDL SQL snippet that starts after the column\n   name and specifies the complete (non-portable!) column definition.\n   This attribute allows to make use of advanced RMDBS features.\n   However, as this needs to be specified in the DDL native to the database,\n   the resulting schema changes are no longer portable. If you specify a\n   ``columnDefinition``, the ``SchemaTool`` ignores all other attributes\n   that are normally used to build the definition DDL. Changes to the\n   ``columnDefinition`` are not detected, you will need to manually create a\n   migration to apply changes.\n\n   Additionally you should remember that the ``type``\n   attribute still handles the conversion between PHP and Database\n   values. If you use this attribute on a column that is used for\n   joins between tables you should also take a look at\n   :ref:`#[JoinColumn] <attrref_joincolumn>`.\n\n.. note::\n\n    For more detailed information on each attribute, please refer to\n    the DBAL ``Schema-Representation`` documentation.\n\nExamples:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Column;\n\n    #[Column(type: \"string\", length: 32, unique: true, nullable: false)]\n    protected $username;\n\n    #[Column(type: \"string\", index: true)]\n    protected $firstName;\n\n    #[Column(type: \"string\", columnDefinition: \"CHAR(2) NOT NULL\")]\n    protected $country;\n\n    #[Column(type: \"decimal\", precision: 2, scale: 1)]\n    protected $height;\n\n    #[Column(type: \"string\", length: 2, options: [\n        \"fixed\" => true,\n        \"comment\" => \"Initial letters of first and last name\"\n    ])]\n    protected $initials;\n\n    #[Column(\n        type: \"integer\",\n        name: \"login_count\",\n        nullable: false,\n        options: [\"unsigned\" => true, \"default\" => 0]\n    )]\n    protected $loginCount;\n\n    // columnDefinition is raw SQL, not DQL. This example works for MySQL:\n    #[Column(\n        type: \"string\",\n        name: \"user_fullname\",\n        columnDefinition: \"VARCHAR(255) GENERATED ALWAYS AS (concat(firstname,' ',lastname))\",\n        insertable: false,\n        updatable: false\n    )]\n    protected $fullname;\n\n.. _attrref_cache:\n\n#[Cache]\n~~~~~~~~\nAdd caching strategy to a root entity or a collection.\n\nOptional parameters:\n\n-  **usage**: One of ``READ_ONLY``, ``READ_WRITE`` or ``NONSTRICT_READ_WRITE``, By default this is ``READ_ONLY``.\n-  **region**: An specific region name\n\n.. _attrref_changetrackingpolicy:\n\n#[ChangeTrackingPolicy]\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe Change Tracking Policy attribute allows to specify how the\nDoctrine ORM ``UnitOfWork`` should detect changes in properties of\nentities during flush. By default each entity is checked according\nto a deferred implicit strategy, which means upon flush ``UnitOfWork``\ncompares all the properties of an entity to a previously stored\nsnapshot. This works out of the box, however you might want to\ntweak the flush performance where using another change tracking\npolicy is an interesting option.\n\nThe :doc:`details on all the available change tracking policies <change-tracking-policies>`\ncan be found in the configuration section.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy;\n\n    #[\n        Entity,\n        ChangeTrackingPolicy(\"DEFERRED_IMPLICIT\"),\n        ChangeTrackingPolicy(\"DEFERRED_EXPLICIT\"),\n    ]\n    class User {}\n\n.. _attrref_customidgenerator:\n\n#[CustomIdGenerator]\n~~~~~~~~~~~~~~~~~~~~\n\nThis attribute allows you to specify a user-provided class to generate identifiers. This attribute only works when both :ref:`#[Id] <attrref_id>` and :ref:`#[GeneratedValue(strategy: \"CUSTOM\")] <attrref_generatedvalue>` are specified.\n\nRequired parameters:\n\n-  **class**: name of the class which should extend Doctrine\\ORM\\Id\\AbstractIdGenerator\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Id;\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\GeneratedValue;\n    use Doctrine\\ORM\\Mapping\\CustomIdGenerator;\n    use App\\Doctrine\\MyIdGenerator;\n\n    #[Id]\n    #[Column(type: \"integer\")]\n    #[GeneratedValue(strategy: \"CUSTOM\")]\n    #[CustomIdGenerator(class: MyIdGenerator::class)]\n    public $id;\n\n.. _attrref_discriminatorcolumn:\n\n#[DiscriminatorColumn]\n~~~~~~~~~~~~~~~~~~~~~~\n\nThis attribute is optional and set on the root entity\nclass of an inheritance hierarchy. It specifies the details of the\ncolumn which saves the name of the class, which the entity is\nactually instantiated as.\n\nIf this attribute is not specified, the discriminator column defaults\nto a string column of length 255 called ``dtype``.\n\nRequired parameters:\n\n\n-  **name**: The column name of the discriminator. This name is also\n   used during Array hydration as key to specify the class-name.\n\nOptional parameters:\n\n\n-  **type**: By default this is string.\n-  **length**: By default this is 255.\n-  **columnDefinition**: Allows to override how the column is generated. See the \"columnDefinition\" attribute on :ref:`#[Column] <attrref_column>`\n-  **enumType**: By default this is `null`. Allows to map discriminatorColumn value to PHP enum\n-  **options**: See \"options\" attribute on :ref:`#[Column] <attrref_column>`.\n\n.. _attrref_discriminatormap:\n\n#[DiscriminatorMap]\n~~~~~~~~~~~~~~~~~~~\n\nThe discriminator map is a required attribute on the\nroot entity class in an inheritance hierarchy. Its only argument is an\narray which defines which class should be saved under\nwhich name in the database. Keys are the database value and values\nare the classes, either as fully- or as unqualified class names\ndepending on whether the classes are in the namespace or not.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\InheritanceType;\n    use Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\n    use Doctrine\\ORM\\Mapping\\DiscriminatorMap;\n\n    #[Entity]\n    #[InheritanceType(\"JOINED\")]\n    #[DiscriminatorColumn(name: \"discr\", type: \"string\")]\n    #[DiscriminatorMap([\"person\" => Person::class, \"employee\" => Employee::class])]\n    class Person\n    {\n        // ...\n    }\n\n\n.. _attrref_embeddable:\n\n#[Embeddable]\n~~~~~~~~~~~~~\n\nThe embeddable attribute is required on a class, in order to make it\nembeddable inside an entity. It works together with the :ref:`#[Embedded] <attrref_embedded>`\nattribute to establish the relationship between the two classes.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Embeddable;\n    use Doctrine\\ORM\\Mapping\\Embedded;\n\n    #[Embeddable]\n    class Address\n    { /* .. */ }\n\n    class User\n    {\n        #[Embedded(class: Address::class)]\n        private $address;\n\n\n.. _attrref_embedded:\n\n#[Embedded]\n~~~~~~~~~~~\n\nThe embedded attribute is required on an entity's member variable,\nin order to specify that it is an embedded class.\n\nRequired parameters:\n\n-  **class**: The embeddable class\n\n.. _attrref_entity:\n\n#[Entity]\n~~~~~~~~~\n\nRequired attribute to mark a PHP class as an entity. Doctrine manages\nthe persistence of all classes marked as entities.\n\nOptional parameters:\n\n-  **repositoryClass**: Specifies the FQCN of a subclass of the\n   ``EntityRepository``. Use of repositories for entities is encouraged to keep\n   specialized DQL and SQL operations separated from the Model/Domain\n   Layer.\n-  **readOnly**: Specifies that this entity is marked as read only and not\n   considered for change-tracking. Entities of this type can be persisted\n   and removed though.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use MyProject\\Repository\\UserRepository;\n\n    #[Entity(repositoryClass: UserRepository::class, readOnly: false)]\n    class User\n    {\n        // ...\n    }\n\n.. _attrref_generatedvalue:\n\n#[GeneratedValue]\n~~~~~~~~~~~~~~~~~\n\nSpecifies which strategy is used for identifier generation for an\ninstance variable which is annotated by :ref:`#[Id] <attrref_id>`. This\nattribute is optional and only has meaning when used in\nconjunction with #[Id].\n\nIf this attribute is not specified with ``#[Id]`` the ``NONE`` strategy is\nused as default.\n\nOptional parameters:\n\n-  **strategy**: Set the name of the identifier generation strategy.\n   Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``CUSTOM`` and\n   ``NONE``. If not specified, the default value is ``AUTO``.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\GeneratedValue;\n    use Doctrine\\ORM\\Mapping\\Id;\n\n    #[Id, Column(type: \"integer\"), GeneratedValue(strategy: \"IDENTITY\")]\n    protected $id = null;\n\n.. _attrref_haslifecyclecallbacks:\n\n#[HasLifecycleCallbacks]\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis attribute has to be set on the entity-class to\nnotify Doctrine that this entity has entity lifecycle callback\nattributes set on at least one of its methods. Using #[PostLoad],\n``#[PrePersist]``, ``#[PostPersist]``, ``#[PreRemove]``, ``#[PostRemove]``,\n``#[PreUpdate]`` or ``#[PostUpdate]`` without this marker attribute will\nmake Doctrine ignore the callbacks.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\n    use Doctrine\\ORM\\Mapping\\PostPersist;\n\n    #[Entity, HasLifecycleCallbacks]\n    class User\n    {\n        #[PostPersist]\n        public function sendOptinMail() {}\n    }\n\n.. _attrref_index:\n\n#[Index]\n~~~~~~~~\n\nAttribute is used on the entity-class level. It provides a hint to the SchemaTool to\ngenerate a database index on the specified table columns. It only\nhas meaning in the ``SchemaTool`` schema generation context.\n\nRequired parameters:\n\n-  **fields**: Array of fields. Exactly one of **fields, columns** is required.\n-  **columns**: Array of columns. Exactly one of **fields, columns** is required.\n\n\nOptional parameters:\n\n-  **name**: Name of the Index. If not provided, a generated name will be assigned.\n-  **options**: Array of platform specific options:\n\n   -  ``where``: SQL WHERE condition to be used for partial indexes. It will\n      only have effect on supported platforms.\n\nBasic example:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\Index;\n\n    #[Entity]\n    #[Index(name: \"category_idx\", columns: [\"category\"])]\n    #[Index(name: \"brand_idx\", fields: [\"brand\"])]\n    class ECommerceProduct\n    {\n    }\n\nExample with partial indexes:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Index;\n\n    #[Index(name: \"search_idx\", columns: [\"category\"],\n        options: [\n            \"where\" => \"((category IS NOT NULL))\"\n        ]\n    )]\n    class ECommerceProduct\n    {\n    }\n\n.. _attrref_id:\n\n#[Id]\n~~~~~\n\nThe annotated instance variable will be marked as entity\nidentifier, the primary key in the database. This attribute is a\nmarker only and has no required or optional attributes. For\nentities that have multiple identifier columns each column has to\nbe marked with ``#[Id]``.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\Id;\n\n    #[Id, Column(type: \"integer\")]\n    protected $id = null;\n\n.. _attrref_inheritancetype:\n\n#[InheritanceType]\n~~~~~~~~~~~~~~~~~~\n\nIn an inheritance hierarchy you have to use this attribute on the\ntopmost/super class to define which strategy should be used for\ninheritance. Currently Single Table and Class Table Inheritance are\nsupported.\n\nThis attribute has always been used in conjunction with the\n:ref:`#[DiscriminatorMap] <attrref_discriminatormap>` and\n:ref:`#[DiscriminatorColumn] <attrref_discriminatorcolumn>` attributes.\n\nExamples:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\InheritanceType;\n    use Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\n    use Doctrine\\ORM\\Mapping\\DiscriminatorMap;\n\n    #[Entity]\n    #[InheritanceType(\"SINGLE_TABLE\")]\n    #[DiscriminatorColumn(name: \"discr\", type: \"string\")]\n    #[DiscriminatorMap([\"person\" => \"Person\", \"employee\" => \"Employee\"])]\n    class Person\n    {\n        // ...\n    }\n\n    #[Entity]\n    #[InheritanceType(\"JOINED\")]\n    #[DiscriminatorColumn(name: \"discr\", type: \"string\")]\n    #[DiscriminatorMap([\"person\" => \"Person\", \"employee\" => \"Employee\"])]\n    class Person\n    {\n        // ...\n    }\n\n.. _attrref_joincolumn:\n\n#[JoinColumn], #[InverseJoinColumn]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis attribute is used in the context of relations in\n:ref:`#[ManyToOne] <attrref_manytoone>`, :ref:`#[OneToOne] <attrref_onetoone>` fields\nand in the Context of a :ref:`#[ManyToMany] <attrref_manytomany>`. If this attribute or both *name* and *referencedColumnName*\nare missing they will be computed considering the field's name and the current\n:doc:`naming strategy <namingstrategy>`.\n\nOptional parameters:\n\n-  **name**: Column name that holds the foreign key identifier for\n   this relation. In the context of ``#[JoinTable]`` it specifies the column\n   name in the join table.\n-  **referencedColumnName**: Name of the primary key identifier that\n   is used for joining of this relation. Defaults to ``id``.\n-  **unique**: Determines whether this relation is exclusive between the\n   affected entities and should be enforced as such on the database\n   constraint level. Defaults to false.\n-  **deferrable**: Determines whether this relation constraint can be deferred. Defaults to false.\n-  **nullable**: Determine whether the related entity is required, or if\n   null is an allowed state for the relation. Defaults to true.\n-  **onDelete**: Cascade Action (Database-level)\n-  **columnDefinition**: DDL SQL snippet that starts after the column\n   name and specifies the complete (non-portable!) column definition.\n   This attribute enables the use of advanced RMDBS features. Note that you\n   need to reference columns by their database name (either explicitly set in\n   the mapping or per the current :doc:`naming strategy <namingstrategy>`).\n   Using this attribute on ``#[JoinColumn]`` is necessary if you need\n   different column definitions for joining columns, for example\n   regarding NULL/NOT NULL defaults. However by default a\n   \"columnDefinition\" attribute on :ref:`#[Column] <attrref_column>` also sets\n   the related ``#[JoinColumn]``'s columnDefinition. This is necessary to\n   make foreign keys work.\n-  **options**:\n   See \"options\" attribute on :ref:`#[Column] <attrref_column>`.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\OneToOne;\n    use Doctrine\\ORM\\Mapping\\JoinColumn;\n\n    #[OneToOne(targetEntity: Customer::class)]\n    #[JoinColumn(name: \"customer_id\", referencedColumnName: \"id\")]\n    private $customer;\n\n.. _attrref_jointable:\n\n#[JoinTable]\n~~~~~~~~~~~~\n\nUsing\n:ref:`#[ManytoMany] <attrref_manytomany>` on the owning side of the relation\nrequires to specify the #[JoinTable] attribute which describes the\ndetails of the database join table. If you do not specify\n``#[JoinTable]`` on these relations reasonable mapping defaults apply\nusing the affected table and the column names.\n\nRequired attribute:\n\n-  **name**: Database name of the join-table\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\ManyToMany;\n    use Doctrine\\ORM\\Mapping\\JoinTable;\n\n    #[ManyToMany(targetEntity: \"Phonenumber\")]\n    #[JoinTable(name: \"users_phonenumbers\")]\n    public $phonenumbers;\n\n.. _attrref_manytoone:\n\n#[ManyToOne]\n~~~~~~~~~~~~\n\nDefines that the annotated instance variable holds a reference that\ndescribes a many-to-one relationship between two entities.\n\nRequired parameters:\n\n\n-  **targetEntity**: FQCN of the referenced target entity. Can be the\n   unqualified class name if both classes are in the same namespace.\n   *IMPORTANT:* No leading backslash!\n\nOptional parameters:\n\n\n-  **cascade**: Cascade Option\n-  **fetch**: One of LAZY or EAGER\n-  inversedBy - The inversedBy attribute designates the field in\n   the entity that is the inverse side of the relationship.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\ManyToOne;\n\n    #[ManyToOne(targetEntity: \"Cart\", cascade: [\"all\"], fetch: \"EAGER\")]\n    private $cart;\n\n.. _attrref_manytomany:\n\n#[ManyToMany]\n~~~~~~~~~~~~~\n\nDefines that the annotated instance variable holds a many-to-many relationship\nbetween two entities. :ref:`#[JoinTable] <attrref_jointable>` is an\nadditional, optional attribute that has reasonable default\nconfiguration values using the table and names of the two related\nentities.\n\nRequired parameters:\n\n\n-  **targetEntity**: FQCN of the referenced target entity. Can be the\n   unqualified class name if both classes are in the same namespace.\n   *IMPORTANT:* No leading backslash!\n\nOptional parameters:\n\n\n-  **mappedBy**: This option specifies the property name on the\n   targetEntity that is the owning side of this relation. It is a\n   required attribute for the inverse side of a relationship.\n-  **inversedBy**: The inversedBy attribute designates the field in the\n   entity that is the inverse side of the relationship.\n-  **cascade**: Cascade Option\n-  **fetch**: One of ``LAZY``, ``EXTRA_LAZY`` or ``EAGER``\n-  **indexBy**: Index the collection by a field on the target entity.\n\n.. note::\n\n    For ``ManyToMany`` bidirectional relationships either side may\n    be the owning side (the side that defines the ``#[JoinTable]`` and/or\n    does not make use of the mappedBy attribute, thus using a default\n    join table).\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\ManyToMany;\n    use Doctrine\\ORM\\Mapping\\JoinColumn;\n    use Doctrine\\ORM\\Mapping\\InverseJoinColumn;\n    use Doctrine\\ORM\\Mapping\\JoinTable;\n\n    /** Owning Side */\n    #[ManyToMany(targetEntity: \"Group\", inversedBy: \"features\")]\n    #[JoinTable(name: \"user_groups\")]\n    #[JoinColumn(name: \"user_id\", referencedColumnName: \"id\")]\n    #[InverseJoinColumn(name: \"group_id\", referencedColumnName: \"id\")]\n    private $groups;\n\n    /** Inverse Side */\n    #[ManyToMany(targetEntity: \"User\", mappedBy: \"groups\")]\n    private $features;\n\n.. _attrref_mappedsuperclass:\n\n#[MappedSuperclass]\n~~~~~~~~~~~~~~~~~~~\n\nA mapped superclass is an abstract or concrete class that provides\npersistent entity state and mapping information for its subclasses,\nbut which is not itself an entity. This attribute is specified on\nthe Class level and has no additional settings.\n\nThe ``#[MappedSuperclass]`` attribute cannot be used in conjunction with\n``#[Entity]``. See the Inheritance Mapping section for\n:doc:`more details on the restrictions of mapped superclasses <inheritance-mapping>`.\n\nOptional parameters:\n\n-  **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.\n   That will be inherited for all subclasses of that Mapped Superclass.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\MappedSuperclass;\n    use Doctrine\\ORM\\Mapping\\Entity;\n\n    #[MappedSuperclass]\n    abstract class BaseEntity\n    {\n        // ... fields and methods\n    }\n\n    #[Entity]\n    class EntitySubClassFoo extends BaseEntity\n    {\n        // ... fields and methods\n    }\n\n.. _attrref_onetoone:\n\n#[OneToOne]\n~~~~~~~~~~~\n\nThe ``#[OneToOne]`` attribute works almost exactly as the\n:ref:`#[ManyToOne] <attrref_manytoone>` with one additional option which can\nbe specified. When no\n:ref:`#[JoinColumn] <attrref_joincolumn>` is specified it defaults to using the target entity table and\nprimary key column names and the current naming strategy to determine a name for the join column.\n\nRequired parameters:\n\n-  **targetEntity**: FQCN of the referenced target entity. Can be the\n   unqualified class name if both classes are in the same namespace.\n   *IMPORTANT:* No leading backslash!\n\nOptional parameters:\n\n-  **cascade**: Cascade Option\n-  **fetch**: One of LAZY or EAGER\n-  **orphanRemoval**: Boolean that specifies if orphans, inverse\n   OneToOne entities that are not connected to any owning instance,\n   should be removed by Doctrine. Defaults to false.\n-  **inversedBy**: The inversedBy attribute designates the field in the\n   entity that is the inverse side of the relationship.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    #[OneToOne(targetEntity: \"Customer\")]\n    #[JoinColumn(name: \"customer_id\", referencedColumnName: \"id\")]\n    private $customer;\n\n.. _attrref_onetomany:\n\n#[OneToMany]\n~~~~~~~~~~~~\n\nRequired parameters:\n\n-  **targetEntity**: FQCN of the referenced target entity. Can be the\n   unqualified class name if both classes are in the same namespace.\n   *IMPORTANT:* No leading backslash!\n\nOptional parameters:\n\n-  **cascade**: Cascade Option\n-  **orphanRemoval**: Boolean that specifies if orphans, inverse\n   OneToOne entities that are not connected to any owning instance,\n   should be removed by Doctrine. Defaults to false.\n-  **mappedBy**: This option specifies the property name on the\n   targetEntity that is the owning side of this relation. Its a\n   required attribute for the inverse side of a relationship.\n-  **fetch**: One of LAZY, EXTRA_LAZY or EAGER.\n-  **indexBy**: Index the collection by a field on the target entity.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\OneToMany;\n\n    #[OneToMany(\n        targetEntity: \"Phonenumber\",\n        mappedBy: \"user\",\n        cascade: [\"persist\", \"remove\"],\n        orphanRemoval: true)\n    ]\n    public $phonenumbers;\n\n.. _attrref_orderby:\n\n#[OrderBy]\n~~~~~~~~~~\n\nOptional attribute that can be specified with a\n:ref:`#[ManyToMany] <attrref_manytomany>` or :ref:`#[OneToMany] <attrref_onetomany>`\nattribute to specify by which criteria the collection should be\nretrieved from the database by using an ORDER BY clause.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    #[ManyToMany(targetEntity: \"Group\")]\n    #[OrderBy([\"name\" => \"ASC\"])]\n    private $groups;\n\nThe key in ``OrderBy`` is only allowed to consist of\nunqualified, unquoted field names and of an optional ``ASC``/``DESC``\npositional statement. Multiple Fields are separated by a comma (,).\nThe referenced field names have to exist on the ``targetEntity``\nclass of the ``#[ManyToMany]`` or ``#[OneToMany]`` attribute.\n\n.. _attrref_postload:\n\n#[PostLoad]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PostLoad]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_postpersist:\n\n#[PostPersist]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PostPersist]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_postremove:\n\n#[PostRemove]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PostRemove]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_postupdate:\n\n#[PostUpdate]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PostUpdate]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_prepersist:\n\n#[PrePersist]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PrePersist]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_preremove:\n\n#[PreRemove]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a #``[PreRemove]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_preupdate:\n\n#[PreUpdate]\n~~~~~~~~~~~~~~\n\nMarks a method on the entity to be called as a ``#[PreUpdate]`` event.\nOnly works with ``#[HasLifecycleCallbacks]`` in the entity class PHP\nlevel.\n\n.. _attrref_sequencegenerator:\n\n#[SequenceGenerator]\n~~~~~~~~~~~~~~~~~~~~~\n\nFor use with ``#[GeneratedValue(strategy: \"SEQUENCE\")]`` this\nattribute allows to specify details about the sequence, such as\nthe increment size and initial values of the sequence.\n\nRequired parameters:\n\n-  **sequenceName**: Name of the sequence\n\nOptional parameters:\n\n-  **allocationSize**: Increment the sequence by the allocation size\n   when its fetched. A value larger than 1 allows optimization for\n   scenarios where you create more than one new entity per request.\n   Defaults to 10\n-  **initialValue**: Where the sequence starts, defaults to 1.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Id;\n    use Doctrine\\ORM\\Mapping\\GeneratedValue;\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\SequenceGenerator;\n\n    #[Id]\n    #[GeneratedValue(strategy: \"SEQUENCE\")]\n    #[Column(type: \"integer\")]\n    #[SequenceGenerator(sequenceName: \"tablename_seq\", initialValue: 1, allocationSize: 100)]\n    protected $id = null;\n\n.. _attrref_table:\n\n#[Table]\n~~~~~~~~\n\nAttribute describes the table an entity is persisted in. It is\nplaced on the entity-class level and is optional. If it is\nnot specified the table name will default to the entity's\nunqualified classname.\n\nRequired parameters:\n\n-  **name**: Name of the table\n\nOptional parameters:\n\n-  **schema**: Name of the schema the table lies in.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\Table;\n\n    #[Entity]\n    #[Table(name: \"user\", schema: \"schema_name\")]\n    class User { }\n\n.. _attrref_uniqueconstraint:\n\n#[UniqueConstraint]\n~~~~~~~~~~~~~~~~~~~\n\nAttribute is used on\nthe entity-class level. It allows to hint the ``SchemaTool`` to\ngenerate a database unique constraint on the specified table\ncolumns. It only has meaning in the SchemaTool schema generation\ncontext.\n\nRequired parameters:\n\n-  **fields**: Array of fields (the names of the properties, used in the entity class).\n-  **columns**: Array of columns (the names of the columns, used in the schema).\n\nOptional parameters:\n\n-  **name**: Name of the Index. If not provided, a generated name will be assigned.\n-  **options**: Array of platform specific options:\n\n   -  ``where``: SQL WHERE condition to be used for partial indexes. It will\n      only have effect on supported platforms.\n\nBasic example:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Entity;\n    use Doctrine\\ORM\\Mapping\\UniqueConstraint;\n\n    #[Entity]\n    #[UniqueConstraint(name: \"ean\", columns: [\"ean\"])]\n    class ECommerceProduct\n    {\n    }\n\n.. _attrref_version:\n\n#[Version]\n~~~~~~~~~~\n\nMarker attribute that defines a specified column as version attribute used in\nan :ref:`optimistic locking <transactions-and-concurrency_optimistic-locking>`\nscenario. It only works on :ref:`#[Column] <attrref_column>` attributes that have\nthe type ``integer`` or ``datetime``. Setting ``#[Version]`` on a property with\n:ref:`#[Id] <attrref_id>` is not supported.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\Version;\n\n    #[Column(type: \"integer\")]\n    #[Version]\n    protected $version;\n"
  },
  {
    "path": "docs/en/reference/basic-mapping/DefaultValues.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Entity;\n\nuse DateTime;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Message\n{\n    #[Column(options: ['default' => 'Hello World!'])]\n    private string $text;\n\n    #[Column(options: ['default' => new CurrentTimestamp()], insertable: false, updatable: false)]\n    private DateTime $createdAt;\n}\n"
  },
  {
    "path": "docs/en/reference/basic-mapping/default-values.xml",
    "content": "<doctrine-mapping>\n  <entity name=\"Message\">\n    <field name=\"text\">\n      <options>\n        <option name=\"default\">Hello World!</option>\n      </options>\n    </field>\n    <field name=\"createdAt\" insertable=\"false\" updatable=\"false\">\n      <options>\n        <option name=\"default\">\n            <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n        </option>\n      </options>\n    </field>\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "docs/en/reference/basic-mapping.rst",
    "content": "Basic Mapping\n=============\n\nThis guide explains the basic mapping of entities and properties.\nAfter working through this guide you should know:\n\n- How to create PHP objects that can be saved to the database with Doctrine;\n- How to configure the mapping between columns on tables and properties on\n  entities;\n- What Doctrine mapping types are;\n- Defining primary keys and how identifiers are generated by Doctrine;\n- How quoting of reserved symbols works in Doctrine.\n\nMapping of associations will be covered in the next chapter on\n:doc:`Association Mapping <association-mapping>`.\n\nCreating Classes for the Database\n---------------------------------\n\nEvery PHP object that you want to save in the database using Doctrine\nis called an *Entity*. The term \"Entity\" describes objects\nthat have an identity over many independent requests. This identity is\nusually achieved by assigning a unique identifier to an entity.\nIn this tutorial the following ``Message`` PHP class will serve as the\nexample Entity:\n\n.. code-block:: php\n\n    <?php\n    class Message\n    {\n        private $id;\n        private $text;\n        private $postedAt;\n    }\n\nBecause Doctrine is a generic library, it only knows about your\nentities because you will describe their existence and structure using\nmapping metadata, which is configuration that tells Doctrine how your\nentity should be stored in the database. The documentation will often\nspeak of \"mapping something\", which means writing the mapping metadata\nthat describes your entity.\n\nDoctrine provides several different ways to specify object-relational\nmapping metadata:\n\n-  :doc:`Attributes <attributes-reference>`\n-  :doc:`XML <xml-mapping>`\n-  :doc:`PHP code <php-mapping>`\n\nThis manual will usually show mapping metadata via attributes, though\nmany examples also show the equivalent configuration in XML.\n\n.. note::\n\n    All metadata drivers perform equally. Once the metadata of a class has been\n    read from the source (attributes, XML, etc.) it is stored in an instance\n    of the ``Doctrine\\ORM\\Mapping\\ClassMetadata`` class which are\n    stored in the metadata cache.  If you're not using a metadata cache (not\n    recommended!) then the XML driver is the fastest.\n\nMarking our ``Message`` class as an entity for Doctrine is straightforward:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\ORM\\Mapping\\Entity;\n\n        #[Entity]\n        class Message\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\">\n              <!-- ... -->\n          </entity>\n        </doctrine-mapping>\n\nWith no additional information, Doctrine expects the entity to be saved\ninto a table with the same name as the class in our case ``Message``.\nYou can change this by configuring information about the table:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\ORM\\Mapping\\Entity;\n        use Doctrine\\ORM\\Mapping\\Table;\n\n        #[Entity]\n        #[Table(name: 'message')]\n        class Message\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\" table=\"message\">\n              <!-- ... -->\n          </entity>\n        </doctrine-mapping>\n\nNow the class ``Message`` will be saved and fetched from the table ``message``.\n\nProperty Mapping\n----------------\n\nThe next step is mapping its properties to columns in the table.\n\nTo configure a property use the ``Column`` attribute. The ``type``\nargument specifies the :ref:`Doctrine Mapping Type\n<reference-mapping-types>` to use for the field. If the type is not\nspecified, ``string`` is used as the default.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\ORM\\Mapping\\Column;\n        use Doctrine\\DBAL\\Types\\Types;\n\n        #[Entity]\n        class Message\n        {\n            #[Column(type: Types::INTEGER)]\n            private $id;\n            #[Column(length: 140)]\n            private $text;\n            #[Column(name: 'posted_at', type: Types::DATETIME)]\n            private $postedAt;\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\">\n            <field name=\"id\" type=\"integer\" />\n            <field name=\"text\" length=\"140\" />\n            <field name=\"postedAt\" column=\"posted_at\" type=\"datetime\" />\n          </entity>\n        </doctrine-mapping>\n\nWhen we don't explicitly specify a column name via the ``name`` option, Doctrine\nassumes the field name is also the column name. So in this example:\n\n* the ``id`` property will map to the column ``id`` using the type ``integer``;\n* the ``text`` property will map to the column ``text`` with the default mapping type ``string``;\n* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type.\n\nHere is a complete list of ``Column``s attributes (all optional):\n\n- ``type`` (default: 'string'): The mapping type to use for the column.\n- ``name`` (default: name of property): The name of the column in the database.\n- ``length`` (default: 255): The length of the column in the database.\n  Applies only if a string-valued column is used.\n- ``unique`` (default: ``false``): Whether the column is a unique key.\n- ``nullable`` (default: ``false``): Whether the column is nullable.\n- ``insertable`` (default: ``true``): Whether the column should be inserted.\n- ``updatable`` (default: ``true``): Whether the column should be updated.\n- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.\n- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.\n- ``precision`` (default: 0): The precision for a decimal (exact numeric) column\n  (applies only for decimal column),\n  which is the maximum number of digits that are stored for the values.\n- ``scale`` (default: 0): The scale for a decimal (exact\n  numeric) column (applies only for decimal column), which represents\n  the number of digits to the right of the decimal point and must\n  not be greater than ``precision``.\n- ``columnDefinition``: Allows to define a custom\n  DDL snippet that is used to create the column. Warning: This normally\n  confuses the :doc:`SchemaTool <tools>` to always detect the column as changed.\n- ``options``: Key-value pairs of options that get passed\n  to the underlying database platform when generating DDL statements.\n\nSpecifying default values\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhile it is possible to specify default values for properties in your\nPHP class, Doctrine also allows you to specify default values for\ndatabase columns using the ``default`` key in the ``options`` array of\nthe ``Column`` attribute.\n\nWhen using XML, you can specify object instances using the ``<object>``\nelement:\n\n.. code-block:: xml\n\n    <field name=\"createdAt\" type=\"datetime\" insertable=\"false\" updatable=\"false\">\n        <options>\n            <option name=\"default\">\n                <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n            </option>\n        </options>\n    </field>\n\nThe ``<object>`` element requires a ``class`` attribute specifying the\nfully qualified class name to instantiate.\n\n.. configuration-block::\n   .. literalinclude:: basic-mapping/DefaultValues.php\n       :language: attribute\n\n   .. literalinclude:: basic-mapping/default-values.xml\n       :language: xml\n\n.. _reference-php-mapping-types:\n\nPHP Types Mapping\n_________________\n\n.. versionadded:: 2.9\n\nThe column types can be inferred automatically from PHP's property types.\nHowever, when the property type is nullable this has no effect on the ``nullable`` Column attribute.\n\nThese are the \"automatic\" mapping rules:\n\n+-----------------------+-------------------------------+\n| PHP property type     | Doctrine column type          |\n+=======================+===============================+\n| ``DateInterval``      | ``Types::DATEINTERVAL``       |\n+-----------------------+-------------------------------+\n| ``DateTime``          | ``Types::DATETIME_MUTABLE``   |\n+-----------------------+-------------------------------+\n| ``DateTimeImmutable`` | ``Types::DATETIME_IMMUTABLE`` |\n+-----------------------+-------------------------------+\n| ``array``             | ``Types::JSON``               |\n+-----------------------+-------------------------------+\n| ``bool``              | ``Types::BOOLEAN``            |\n+-----------------------+-------------------------------+\n| ``float``             | ``Types::FLOAT``              |\n+-----------------------+-------------------------------+\n| ``int``               | ``Types::INTEGER``            |\n+-----------------------+-------------------------------+\n| Any other type        | ``Types::STRING``             |\n+-----------------------+-------------------------------+\n\n.. versionadded:: 2.11\n\nAs of version 2.11 Doctrine can also automatically map typed properties using a\nPHP 8.1 enum to set the right ``type`` and ``enumType``.\n\n.. versionadded:: 2.14\n\nSince version 2.14 you can specify custom typed field mapping between PHP type and DBAL type using ``Configuration``\nand a custom ``Doctrine\\ORM\\Mapping\\TypedFieldMapper`` implementation.\n\n:doc:`Read more about TypedFieldMapper <typedfieldmapper>`.\n\nProperty Hooks\n--------------\n\n.. versionadded:: 3.4\n\nDoctrine supports mapping hooked properties as long as they have a backed property\nand are not virtual.\n\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\ORM\\Mapping\\Column;\n        use Doctrine\\DBAL\\Types\\Types;\n\n        #[Entity]\n        class Message\n        {\n            #[Column(type: Types::INTEGER)]\n            private $id;\n            #[Column(type: Types::STRING)]\n            public string $language = 'de' {\n                // Override the \"read\" action with arbitrary logic.\n                get => strtoupper($this->language);\n\n                // Override the \"write\" action with arbitrary logic.\n                set {\n                    $this->language = strtolower($value);\n                }\n            }\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\">\n            <field name=\"id\" type=\"integer\" />\n            <field name=\"language\" />\n          </entity>\n        </doctrine-mapping>\n\nIf you attempt to map a virtual property with ``#[Column]`` an exception will be thrown.\n\nSome caveats apply to the use of property hooks, as they behave differently when accessing the property through\nthe entity or directly through DQL/EntityRepository. Because the property hook can modify the value of the property in a way\nthat value and raw value are different, you have to use the raw value representation when querying for the property.\n\n.. code-block:: php\n\n    <?php\n    $queryBuilder = $entityManager->createQueryBuilder();\n    $queryBuilder->select('m')\n        ->from(Message::class, 'm')\n        ->where('m.language = :language')\n        ->setParameter('language', 'de'); // Use lower case here for raw value representation\n\n    $query = $queryBuilder->getQuery();\n    $result = $query->getResult();\n\n    $messageRepository = $entityManager->getRepository(Message::class);\n    $deMessages = $messageRepository->findBy(['language' => 'de']); // Use lower case here for raw value representation\n\n.. _reference-mapping-types:\n\nDoctrine Mapping Types\n----------------------\n\nThe ``type`` option used in the ``@Column`` accepts any of the\n`existing Doctrine DBAL types <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/types.html#reference>`_\nor :doc:`your own custom mapping types\n<../cookbook/custom-mapping-types>`. A Doctrine type defines\nthe conversion between PHP and SQL types, independent from the database vendor\nyou are using.\n\n.. note::\n\n    DateTime and Object types are compared by reference, not by value. Doctrine\n    updates this values if the reference changes and therefore behaves as if\n    these objects are immutable value objects.\n\n.. warning::\n\n    All Date types assume that you are exclusively using the default timezone\n    set by `date_default_timezone_set() <https://php.net/manual/en/function.date-default-timezone-set.php>`_\n    or by the php.ini configuration ``date.timezone``. Working with\n    different timezones will cause troubles and unexpected behavior.\n\n    If you need specific timezone handling you have to handle this\n    in your domain, converting all the values back and forth from UTC.\n    There is also a :doc:`cookbook entry <../cookbook/working-with-datetime>`\n    on working with datetimes that gives hints for implementing\n    multi timezone applications.\n\nIdentifiers / Primary Keys\n--------------------------\n\nEvery entity class must have an identifier/primary key. You can select\nthe field that serves as the identifier with the ``#[Id]`` attribute.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class Message\n        {\n            #[Id]\n            #[Column(type: 'integer')]\n            #[GeneratedValue]\n            private int|null $id = null;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\">\n            <id name=\"id\" type=\"integer\">\n                <generator strategy=\"AUTO\" />\n            </id>\n            <!-- -->\n          </entity>\n        </doctrine-mapping>\n\nIn most cases using the automatic generator strategy (``#[GeneratedValue]``) is\nwhat you want, but for backwards-compatibility reasons it might not. It\ndefaults to the identifier generation mechanism your current database\nvendor preferred at the time that strategy was introduced:\n``AUTO_INCREMENT`` with MySQL, sequences with PostgreSQL and Oracle and\nso on.\nIf you are using `doctrine/dbal` 4, we now recommend using ``IDENTITY``\nfor PostgreSQL, and ``AUTO`` resolves to it because of that.\nYou can stick with ``SEQUENCE`` while still using the ``AUTO``\nstrategy, by configuring what it defaults to.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\n    use Doctrine\\ORM\\Configuration;\n\n    $config = new Configuration();\n    $config->setIdentityGenerationPreferences([\n        PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,\n    ]);\n\n.. _identifier-generation-strategies:\n\nIdentifier Generation Strategies\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe previous example showed how to use the default identifier\ngeneration strategy without knowing the underlying database with\nthe AUTO-detection strategy. It is also possible to specify the\nidentifier generation strategy more explicitly, which allows you to\nmake use of some additional features.\n\nHere is the list of possible generation strategies:\n\n-  ``AUTO`` (default): Tells Doctrine to pick the strategy that is\n   preferred by the used database platform. The preferred strategies\n   are ``IDENTITY`` for MySQL, SQLite, MsSQL, SQL Anywhere and\n   PostgreSQL (on DBAL 4) and, for historical reasons, ``SEQUENCE``\n   for Oracle and PostgreSQL (on DBAL 3). This strategy provides\n   full portability.\n-  ``IDENTITY``: Tells Doctrine to use special identity columns in\n   the database that generate a value on insertion of a row. This\n   strategy does currently not provide full portability and is\n   supported by the following platforms: MySQL/SQLite/SQL Anywhere\n   (``AUTO_INCREMENT``), MSSQL (``IDENTITY``) and PostgreSQL (``SERIAL``\n   on DBAL 3, ``GENERATED BY DEFAULT AS IDENTITY`` on DBAL 4).\n-  ``SEQUENCE``: Tells Doctrine to use a database sequence for ID\n   generation. This strategy does currently not provide full\n   portability. Sequences are supported by Oracle, PostgreSQL and\n   SQL Anywhere.\n-  ``NONE``: Tells Doctrine that the identifiers are assigned (and\n   thus generated) by your code. The assignment must take place before\n   a new entity is passed to ``EntityManager#persist``. NONE is the\n   same as leaving off the ``#[GeneratedValue]`` entirely.\n-  ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute.\n   It will allow you to pass a :ref:`class of your own to generate the identifiers. <attrref_customidgenerator>`\n\nSequence Generator\n^^^^^^^^^^^^^^^^^^\n\nThe Sequence Generator can currently be used in conjunction with\nOracle or Postgres and allows some additional configuration options\nbesides specifying the sequence's name:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class Message\n        {\n            #[Id]\n            #[GeneratedValue(strategy: 'SEQUENCE')]\n            #[SequenceGenerator(sequenceName: 'message_seq', initialValue: 1, allocationSize: 100)]\n            protected int|null $id = null;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"Message\">\n            <id name=\"id\" type=\"integer\">\n                <generator strategy=\"SEQUENCE\" />\n                <sequence-generator sequence-name=\"message_seq\" allocation-size=\"100\" initial-value=\"1\" />\n            </id>\n          </entity>\n        </doctrine-mapping>\n\nThe initial value specifies at which value the sequence should\nstart.\n\nThe allocationSize is a powerful feature to optimize INSERT\nperformance of Doctrine. The allocationSize specifies by how much\nvalues the sequence is incremented whenever the next value is\nretrieved. If this is larger than 1 (one) Doctrine can generate\nidentifier values for the allocationSizes amount of entities. In\nthe above example with ``allocationSize=100`` Doctrine ORM would only\nneed to access the sequence once to generate the identifiers for\n100 new entities.\n\n.. caution::\n\n    The allocationSize is detected by SchemaTool and\n    transformed into an \"INCREMENT BY \" clause in the CREATE SEQUENCE\n    statement. For a database schema created manually (and not\n    SchemaTool) you have to make sure that the allocationSize\n    configuration option is never larger than the actual sequences\n    INCREMENT BY value, otherwise you may get duplicate keys.\n\n\n.. note::\n\n    It is possible to use strategy=\"AUTO\" and at the same time\n    specifying a @SequenceGenerator. In such a case, your custom\n    sequence settings are used in the case where the preferred strategy\n    of the underlying platform is SEQUENCE, such as for Oracle and\n    PostgreSQL.\n\n\nComposite Keys\n~~~~~~~~~~~~~~\n\nWith Doctrine ORM you can use composite primary keys, using ``#[Id]`` on\nmore than one column. Some restrictions exist opposed to using a single\nidentifier in this case: The use of the ``#[GeneratedValue]`` attribute\nis not supported, which means you can only use composite keys if you\ngenerate the primary key values yourself before calling\n``EntityManager#persist()`` on the entity.\n\nMore details on composite primary keys are discussed in a :doc:`dedicated tutorial\n<../tutorials/composite-primary-keys>`.\n\nQuoting Reserved Words\n----------------------\n\nSometimes it is necessary to quote a column or table name because of reserved\nword conflicts. Doctrine does not quote identifiers automatically, because it\nleads to more problems than it would solve. Quoting tables and column names\nneeds to be done explicitly using ticks in the definition.\n\n.. code-block:: php\n\n    <?php\n\n    #[Column(name: '`number`', type: 'integer')]\n    private $number;\n\nDoctrine will then quote this column name in all SQL statements\naccording to the used database platform.\n\n.. warning::\n\n    Identifier Quoting does not work for join column names or discriminator\n    column names unless you are using a custom ``QuoteStrategy``.\n\n.. _reference-basic-mapping-custom-mapping-types:\n\nFor more control over column quoting the ``Doctrine\\ORM\\Mapping\\QuoteStrategy`` interface\nwas introduced in ORM. It is invoked for every column, table, alias and other\nSQL names. You can implement the QuoteStrategy and set it by calling\n``Doctrine\\ORM\\Configuration#setQuoteStrategy()``.\n\nThe ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.\nYou can use it with the following code:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\AnsiQuoteStrategy;\n\n    $configuration->setQuoteStrategy(new AnsiQuoteStrategy());\n"
  },
  {
    "path": "docs/en/reference/batch-processing.rst",
    "content": "Batch Processing\n================\n\nThis chapter shows you how to accomplish bulk inserts, updates and\ndeletes with Doctrine in an efficient way. The main problem with\nbulk operations is usually not to run out of memory and this is\nespecially what the strategies presented here provide help with.\n\n.. warning::\n\n    An ORM tool is not primarily well-suited for mass\n    inserts, updates or deletions. Every RDBMS has its own, most\n    effective way of dealing with such operations and if the options\n    outlined below are not sufficient for your purposes we recommend\n    you use the tools for your particular RDBMS for these bulk\n    operations.\n\n\n.. note::\n\n    Having an SQL logger enabled when processing batches can have a\n    serious impact on performance and resource usage.\n    To avoid that, you should use a PSR logger implementation that can be\n    disabled at runtime.\n    For example, with Monolog, you can use ``Logger::pushHandler()``\n    to push a ``NullHandler`` to the logger instance, and then pop it\n    when you need to enable logging again.\n\n    With DBAL 2, you can disable the SQL logger like below:\n\n.. code-block:: php\n\n    <?php\n    $em->getConnection()->getConfiguration()->setSQLLogger(null);\n\nBulk Inserts\n------------\n\nBulk inserts in Doctrine are best performed in batches, taking\nadvantage of the transactional write-behind behavior of an\n``EntityManager``. The following code shows an example for\ninserting 10000 objects with a batch size of 20. You may need to\nexperiment with the batch size to find the size that works best for\nyou. Larger batch sizes mean more prepared statement reuse\ninternally but also mean more work during ``flush``.\n\n.. code-block:: php\n\n    <?php\n    $batchSize = 20;\n    for ($i = 1; $i <= 10000; ++$i) {\n        $user = new CmsUser;\n        $user->setStatus('user');\n        $user->setUsername('user' . $i);\n        $user->setName('Mr.Smith-' . $i);\n        $em->persist($user);\n        if (($i % $batchSize) === 0) {\n            $em->flush();\n            $em->clear(); // Detaches all objects from Doctrine!\n        }\n    }\n    $em->flush(); // Persist objects that did not make up an entire batch\n    $em->clear();\n\nBulk Updates\n------------\n\nThere are 2 possibilities for bulk updates with Doctrine.\n\nDQL UPDATE\n~~~~~~~~~~\n\nThe by far most efficient way for bulk updates is to use a DQL\nUPDATE query. Example:\n\n.. code-block:: php\n\n    <?php\n    $q = $em->createQuery('update MyProject\\Model\\Manager m set m.salary = m.salary * 0.9');\n    $numUpdated = $q->execute();\n\nIterating results\n~~~~~~~~~~~~~~~~~\n\nAn alternative solution for bulk updates is to use the\n``Query#toIterable()`` facility to iterate over the query results step\nby step instead of loading the whole result into memory at once.\nThe following example shows how to do this, combining the iteration\nwith the batching strategy that was already used for bulk inserts:\n\n.. code-block:: php\n\n    <?php\n    $batchSize = 20;\n    $i = 0;\n    $q = $em->createQuery('select u from MyProject\\Model\\User u');\n    foreach ($q->toIterable() as $user) {\n        $user->increaseCredit();\n        $user->calculateNewBonuses();\n        ++$i;\n        if (($i % $batchSize) === 0) {\n            $em->flush(); // Executes all updates.\n            $em->clear(); // Detaches all objects from Doctrine!\n        }\n    }\n    $em->flush();\n\n.. note::\n\n    Iterating results is not possible with queries that\n    fetch-join a collection-valued association. The nature of such SQL\n    result sets is not suitable for incremental hydration.\n\n.. note::\n\n    Results may be fully buffered by the database client/ connection allocating\n    additional memory not visible to the PHP process. For large sets this\n    may easily kill the process for no apparent reason.\n\n\nBulk Deletes\n------------\n\nThere are two possibilities for bulk deletes with Doctrine. You can\neither issue a single DQL DELETE query or you can iterate over\nresults removing them one at a time.\n\nDQL DELETE\n~~~~~~~~~~\n\nThe by far most efficient way for bulk deletes is to use a DQL\nDELETE query.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    $q = $em->createQuery('delete from MyProject\\Model\\Manager m where m.salary > 100000');\n    $numDeleted = $q->execute();\n\nIterating results\n~~~~~~~~~~~~~~~~~\n\nAn alternative solution for bulk deletes is to use the\n``Query#toIterable()`` facility to iterate over the query results step\nby step instead of loading the whole result into memory at once.\nThe following example shows how to do this:\n\n.. code-block:: php\n\n    <?php\n    $batchSize = 20;\n    $i = 0;\n    $q = $em->createQuery('select u from MyProject\\Model\\User u');\n    foreach($q->toIterable() as $row) {\n        $em->remove($row);\n        ++$i;\n        if (($i % $batchSize) === 0) {\n            $em->flush(); // Executes all deletions.\n            $em->clear(); // Detaches all objects from Doctrine!\n        }\n    }\n    $em->flush();\n\n.. note::\n\n    Iterating results is not possible with queries that\n    fetch-join a collection-valued association. The nature of such SQL\n    result sets is not suitable for incremental hydration.\n\n\nIterating Large Results for Data-Processing\n-------------------------------------------\n\nYou can use the ``toIterable()`` method just to iterate over a large\nresult and no UPDATE or DELETE intention. ``$query->toIterable()`` returns ``iterable``\nso you can process a large result without memory\nproblems using the following approach:\n\n.. code-block:: php\n\n    <?php\n    $q = $this->_em->createQuery('select u from MyProject\\Model\\User u');\n    foreach ($q->toIterable() as $row) {\n        // do stuff with the data in the row\n\n        // detach from Doctrine, so that it can be Garbage-Collected immediately\n        $this->_em->detach($row[0]);\n    }\n\n.. note::\n\n    Iterating results is not possible with queries that\n    fetch-join a collection-valued association. The nature of such SQL\n    result sets is not suitable for incremental hydration.\n"
  },
  {
    "path": "docs/en/reference/best-practices.rst",
    "content": "Best Practices\n==============\n\nThe best practices mentioned here that affect database\ndesign generally refer to best practices when working with Doctrine\nand do not necessarily reflect best practices for database design\nin general.\n\nConstrain relationships as much as possible\n-------------------------------------------\n\nIt is important to constrain relationships as much as possible.\nThis means:\n\n\n-  Impose a traversal direction (avoid bidirectional associations\n   if possible)\n-  Eliminate nonessential associations\n\nThis has several benefits:\n\n\n-  Reduced coupling in your domain model\n-  Simpler code in your domain model (no need to maintain\n   bidirectionality properly)\n-  Less work for Doctrine\n\nAvoid composite keys\n--------------------\n\nEven though Doctrine fully supports composite keys it is best not\nto use them if possible. Composite keys require additional work by\nDoctrine and thus have a higher probability of errors.\n\nUse events judiciously\n----------------------\n\nThe event system of Doctrine is great and fast. Even though making\nheavy use of events, especially lifecycle events, can have a\nnegative impact on the performance of your application. Thus you\nshould use events judiciously.\n\nUse cascades judiciously\n------------------------\n\nAutomatic cascades of the persist/remove/etc. operations are\nvery handy but should be used wisely. Do NOT simply add all\ncascades to all associations. Think about which cascades actually\ndo make sense for you for a particular association, given the\nscenarios it is most likely used in.\n\nDon't use special characters\n----------------------------\n\nAvoid using any non-ASCII characters in class, field, table or\ncolumn names. Doctrine itself is not unicode-safe in many places\nand will not be until PHP itself is fully unicode-aware.\n\nDon't use identifier quoting\n----------------------------\n\nIdentifier quoting is a workaround for using reserved words that\noften causes problems in edge cases. Do not use identifier quoting\nand avoid using reserved words as table or column names.\n\nInitialize collections in the constructor\n-----------------------------------------\n\nIt is recommended best practice to initialize any business\ncollections in entities in the constructor. Example:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\Model;\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n\n    class User {\n        /** @var Collection<int, Address> */\n        private Collection $addresses;\n        /** @var Collection<int, Article> */\n        private Collection $articles;\n\n        public function __construct() {\n            $this->addresses = new ArrayCollection;\n            $this->articles = new ArrayCollection;\n        }\n    }\n\nDon't map foreign keys to fields in an entity\n---------------------------------------------\n\nForeign keys have no meaning whatsoever in an object model. Foreign\nkeys are how a relational database establishes relationships. Your\nobject model establishes relationships through object references.\nThus mapping foreign keys to object fields heavily leaks details of\nthe relational model into the object model, something you really\nshould not do.\n\nUse explicit transaction demarcation\n------------------------------------\n\nWhile Doctrine will automatically wrap all DML operations in a\ntransaction on flush(), it is considered best practice to\nexplicitly set the transaction boundaries yourself. Otherwise every\nsingle query is wrapped in a small transaction (Yes, SELECT\nqueries, too) since you can not talk to your database outside of a\ntransaction. While such short transactions for read-only (SELECT)\nqueries generally don't have any noticeable performance impact, it\nis still preferable to use fewer, well-defined transactions that\nare established through explicit transaction boundaries.\n\n\n"
  },
  {
    "path": "docs/en/reference/caching.rst",
    "content": "Caching\n=======\n\nThe Doctrine ORM package can leverage cache adapters implementing the PSR-6\nstandard to allow you to improve the performance of various aspects of\nDoctrine by simply making some additional configurations and method calls.\n\n.. _types-of-caches:\n\nTypes of Caches\n---------------\n\nQuery Cache\n~~~~~~~~~~~\n\nIt is highly recommended that in a production environment you cache\nthe transformation of a DQL query to its SQL counterpart. It\ndoesn't make sense to do this parsing multiple times as it doesn't\nchange unless you alter the DQL query.\n\nThis can be done by configuring the query cache implementation to\nuse on your ORM configuration.\n\n.. code-block:: php\n\n    <?php\n    $cache = new \\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter('doctrine_queries');\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->setQueryCache($cache);\n\nResult Cache\n~~~~~~~~~~~~\n\nThe result cache can be used to cache the results of your queries\nso that we don't have to query the database again after the first time.\nYou just need to configure the result cache implementation.\n\n.. code-block:: php\n\n    <?php\n    $cache = new \\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter(\n        'doctrine_results',\n        0,\n        '/path/to/writable/directory'\n    );\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->setResultCache($cache);\n\nNow when you're executing DQL queries you can configure them to use\nthe result cache.\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('select u from \\Entities\\User u');\n    $query->enableResultCache();\n\nYou can also configure an individual query to use a different\nresult cache driver.\n\n.. code-block:: php\n\n    <?php\n    $cache = new \\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter(\n        'doctrine_results',\n        0,\n        '/path/to/writable/directory'\n    );\n    $query->setResultCache($cache);\n\n.. note::\n\n    Setting the result cache driver on the query will\n    automatically enable the result cache for the query. If you want to\n    disable it use ``disableResultCache()``.\n\n    ::\n\n        <?php\n        $query->disableResultCache();\n\n\nIf you want to set the time the cache has to live you can use the\n``setResultCacheLifetime()`` method.\n\n.. code-block:: php\n\n    <?php\n    $query->setResultCacheLifetime(3600);\n\nThe ID used to store the result set cache is a hash which is\nautomatically generated for you if you don't set a custom ID\nyourself with the ``setResultCacheId()`` method.\n\n.. code-block:: php\n\n    <?php\n    $query->setResultCacheId('my_custom_id');\n\nYou can also set the lifetime and cache ID by passing the values as\nthe first and second argument to ``enableResultCache()``.\n\n.. code-block:: php\n\n    <?php\n    $query->enableResultCache(3600, 'my_custom_id');\n\nMetadata Cache\n~~~~~~~~~~~~~~\n\nYour class metadata can be parsed from a few different sources like\nXML, Attributes, etc. Instead of parsing this\ninformation on each request we should cache it using one of the cache\ndrivers.\n\nJust like the query and result cache we need to configure it\nfirst.\n\n.. code-block:: php\n\n    <?php\n    $cache = \\Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter(\n        'doctrine_metadata',\n        0,\n        '/path/to/writable/directory'\n    );\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->setMetadataCache($cache);\n\nNow the metadata information will only be parsed once and stored in\nthe cache driver.\n\nClearing the Cache\n------------------\n\nWe've already shown you how you can use the API of the\ncache drivers to manually delete cache entries. For your\nconvenience we offer command line tasks to help you with\nclearing the query, result and metadata cache.\n\nFrom the Doctrine command line you can run the following commands:\n\nTo clear the query cache use the ``orm:clear-cache:query`` task.\n\n.. code-block:: php\n\n    $ ./doctrine orm:clear-cache:query\n\nTo clear the metadata cache use the ``orm:clear-cache:metadata`` task.\n\n.. code-block:: php\n\n    $ ./doctrine orm:clear-cache:metadata\n\nTo clear the result cache use the ``orm:clear-cache:result`` task.\n\n.. code-block:: php\n\n    $ ./doctrine orm:clear-cache:result\n\nAll these tasks accept a ``--flush`` option to flush the entire\ncontents of the cache instead of invalidating the entries.\n\n.. note::\n\n    None of these tasks will work with APC, APCu, or XCache drivers\n    because the memory that the cache is stored in is only accessible\n    to the webserver.\n\nCache Chaining\n--------------\n\nA common pattern is to use a static cache to store data that is\nrequested many times in a single PHP request. Even though this data\nmay be stored in a fast memory cache, often that cache is over a\nnetwork link leading to sizable network traffic.\n\nA chain cache class allows multiple caches to be registered at once.\nFor example, a per-request array cache can be used first, followed by\na (relatively) slower Memcached cache if the array cache misses.\nThe chain cache automatically handles pushing data up to faster caches in\nthe chain and clearing data in the entire stack when it is deleted.\n\nSymfony Cache provides such a chain cache. To find out how to use it,\nplease have a look at the\n`Symfony Documentation <https://symfony.com/doc/current/components/cache/adapters/chain_adapter.html>`_.\n\nCache Slams\n-----------\n\nSomething to be careful of when using the cache drivers is\n\"cache slams\". Imagine you have a heavily trafficked website with some\ncode that checks for the existence of a cache record and if it does\nnot exist it generates the information and saves it to the cache.\nNow, if 100 requests were issued all at the same time and each one\nsees the cache does not exist and they all try to insert the same\ncache entry it could lock up APC, Xcache, etc. and cause problems.\nWays exist to work around this, like pre-populating your cache and\nnot letting your users' requests populate the cache.\n\nYou can read more about cache slams\n`in this blog post <http://notmysock.org/blog/php/user-cache-timebomb.html>`_.\n"
  },
  {
    "path": "docs/en/reference/change-tracking-policies.rst",
    "content": "Change Tracking Policies\n========================\n\nChange tracking is the process of determining what has changed in\nmanaged entities since the last time they were synchronized with\nthe database.\n\nDoctrine provides 2 different change tracking policies, each having\nits particular advantages and disadvantages. The change tracking\npolicy can be defined on a per-class basis (or more precisely,\nper-hierarchy).\n\nDeferred Implicit\n~~~~~~~~~~~~~~~~~\n\nThe deferred implicit policy is the default change tracking policy\nand the most convenient one. With this policy, Doctrine detects the\nchanges by a property-by-property comparison at commit time and\nalso detects changes to entities or new entities that are\nreferenced by other managed entities (\"persistence by\nreachability\"). Although the most convenient policy, it can have\nnegative effects on performance if you are dealing with large units\nof work (see \"Understanding the Unit of Work\"). Since Doctrine\ncan't know what has changed, it needs to check all managed entities\nfor changes every time you invoke EntityManager#flush(), making\nthis operation rather costly.\n\nDeferred Explicit\n~~~~~~~~~~~~~~~~~\n\nThe deferred explicit policy is similar to the deferred implicit\npolicy in that it detects changes through a property-by-property\ncomparison at commit time. The difference is that Doctrine ORM only\nconsiders entities that have been explicitly marked for change detection\nthrough a call to EntityManager#persist(entity) or through a save\ncascade. All other entities are skipped. This policy therefore\ngives improved performance for larger units of work while\nsacrificing the behavior of \"automatic dirty checking\".\n\nTherefore, flush() operations are potentially cheaper with this\npolicy. The negative aspect this has is that if you have a rather\nlarge application and you pass your objects through several layers\nfor processing purposes and business tasks you may need to track\nyourself which entities have changed on the way so you can pass\nthem to EntityManager#persist().\n\nThis policy can be configured as follows:\n\n.. code-block:: php\n\n    <?php\n\n    #[Entity]\n    #[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\n    class User\n    {\n        // ...\n    }\n"
  },
  {
    "path": "docs/en/reference/configuration.rst",
    "content": "Installation and Configuration\n==============================\n\nDoctrine can be installed with `Composer <https://getcomposer.org>`_.\n\nDefine the following requirement in your ``composer.json`` file:\n\n::\n\n    {\n        \"require\": {\n            \"doctrine/orm\": \"*\"\n        }\n    }\n\nThen call ``composer install`` from your command line. If you don't know\nhow Composer works, check out their `Getting Started <https://getcomposer.org/doc/00-intro.md>`_ to set up.\n\nClass loading\n-------------\n\nAutoloading is taken care of by Composer. You just have to include the composer autoload file in your project:\n\n.. code-block:: php\n\n    <?php\n    // bootstrap.php\n    // Include Composer Autoload (relative to project root).\n    require_once \"vendor/autoload.php\";\n\nObtaining an EntityManager\n--------------------------\n\nOnce you have prepared the class loading, you acquire an\n*EntityManager* instance. The EntityManager class is the primary\naccess point to ORM functionality provided by Doctrine.\n\n.. code-block:: php\n\n    <?php\n    // bootstrap.php\n    require_once \"vendor/autoload.php\";\n\n    use Doctrine\\DBAL\\DriverManager;\n    use Doctrine\\ORM\\EntityManager;\n    use Doctrine\\ORM\\ORMSetup;\n\n    $paths = ['/path/to/entity-files'];\n    $isDevMode = false;\n\n    // the connection configuration\n    $dbParams = [\n        'driver'   => 'pdo_mysql',\n        'user'     => 'root',\n        'password' => '',\n        'dbname'   => 'foo',\n    ];\n\n    $config = ORMSetup::createAttributeMetadataConfig($paths, $isDevMode);\n    // on PHP < 8.4, use ORMSetup::createAttributeMetadataConfiguration() instead\n    $connection = DriverManager::getConnection($dbParams, $config);\n    $entityManager = new EntityManager($connection, $config);\n\nOr if you prefer XML:\n\n.. code-block:: php\n\n    <?php\n    $paths = ['/path/to/xml-mappings'];\n    $config = ORMSetup::createXMLMetadataConfig($paths, $isDevMode);\n    // on PHP < 8.4, use ORMSetup::createXMLMetadataConfiguration() instead\n    $connection = DriverManager::getConnection($dbParams, $config);\n    $entityManager = new EntityManager($connection, $config);\n\nInside the ``ORMSetup`` methods several assumptions are made:\n\n-  If ``$isDevMode`` is true caching is done in memory with the ``ArrayAdapter``. Proxy objects are recreated on every request.\n-  If ``$isDevMode`` is false, check for Caches in the order APCu, Redis (127.0.0.1:6379), Memcache (127.0.0.1:11211) unless `$cache` is passed as fourth argument.\n-  If ``$isDevMode`` is false, set then proxy classes have to be explicitly created through the command line.\n-  If third argument `$proxyDir` is not set, use the systems temporary directory.\n\n.. note::\n\n    In order to have ``ORMSetup`` configure the cache automatically, the library ``symfony/cache``\n    has to be installed as a dependency.\n\nIf you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration </reference/advanced-configuration>` section.\n\n.. note::\n\n    You can learn more about the database connection configuration in the\n    `Doctrine DBAL connection configuration reference <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/configuration.html>`_.\n\nSetting up the Commandline Tool\n-------------------------------\n\nDoctrine ships with a number of command line tools that are very helpful\nduring development. In order to make use of them, create an executable PHP\nscript in your project as described in the\n:doc:`tools chapter <../reference/tools>`.\n"
  },
  {
    "path": "docs/en/reference/dql-doctrine-query-language.rst",
    "content": "Doctrine Query Language\n=======================\n\nDQL stands for Doctrine Query Language and is an Object\nQuery Language derivative that is very similar to the Hibernate\nQuery Language (HQL) or the Java Persistence Query Language (JPQL).\n\nIn essence, DQL provides powerful querying capabilities over your\nobject model. Imagine all your objects lying around in some storage\n(like an object database). When writing DQL queries, think about\nquerying that storage to pick a certain subset of your objects.\n\n.. note::\n\n    A common mistake for beginners is to mistake DQL for\n    being just some form of SQL and therefore trying to use table names\n    and column names or join arbitrary tables together in a query. You\n    need to think about DQL as a query language for your object model,\n    not for your relational schema.\n\n\nDQL is case in-sensitive, except for namespace, class and field\nnames, which are case sensitive.\n\nTypes of DQL queries\n--------------------\n\nDQL as a query language has SELECT, UPDATE and DELETE constructs\nthat map to their corresponding SQL statement types. INSERT\nstatements are not allowed in DQL, because entities and their\nrelations have to be introduced into the persistence context\nthrough ``EntityManager#persist()`` to ensure consistency of your\nobject model.\n\nDQL SELECT statements are a very powerful way of retrieving parts\nof your domain model that are not accessible via associations.\nAdditionally they allow you to retrieve entities and their associations\nin one single SQL select statement which can make a huge difference\nin performance compared to using several queries.\n\nDQL UPDATE and DELETE statements offer a way to execute bulk\nchanges on the entities of your domain model. This is often\nnecessary when you cannot load all the affected entities of a bulk\nupdate into memory.\n\nSELECT queries\n--------------\n\nDQL SELECT clause\n~~~~~~~~~~~~~~~~~\n\nHere is an example that selects all users with an age > 20:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM MyProject\\Model\\User u WHERE u.age > 20');\n    $users = $query->getResult();\n\nLets examine the query:\n\n\n-  ``u`` is a so called identification variable or alias that\n   refers to the ``MyProject\\Model\\User`` class. By placing this alias\n   in the SELECT clause we specify that we want all instances of the\n   User class that are matched by this query to appear in the query\n   result.\n-  The FROM keyword is always followed by a fully-qualified class\n   name which in turn is followed by an identification variable or\n   alias for that class name. This class designates a root of our\n   query from which we can navigate further via joins (explained\n   later) and path expressions.\n-  The expression ``u.age`` in the WHERE clause is a path\n   expression. Path expressions in DQL are easily identified by the\n   use of the '.' operator that is used for constructing paths. The\n   path expression ``u.age`` refers to the ``age`` field on the User\n   class.\n\nThe result of this query would be a list of User objects where all\nusers are older than 20.\n\nResult format\n~~~~~~~~~~~~~\nThe composition of the expressions in the SELECT clause also\ninfluences the nature of the query result. There are three\ncases:\n\n**All objects**\n\n.. code-block:: sql\n\n    SELECT u, p, n FROM Users u...\n\nIn this case, the result will be an array of User objects because of\nthe FROM clause, with children ``p`` and ``n`` hydrated because of\ntheir inclusion in the SELECT clause.\n\n**All scalars**\n\n.. code-block:: sql\n\n    SELECT u.name, u.address FROM Users u...\n\nIn this case, the result will be an array of arrays.  In the example\nabove, each element of the result array would be an array of the\nscalar name and address values.\n\nYou can select scalars from any entity in the query.\n\n**Mixed**\n\n.. code-block:: sql\n\n    SELECT u, p.quantity FROM Users u...\n\nHere, the result will again be an array of arrays, with each element\nbeing an array made up of a User object and the scalar value\n``p.quantity``.\n\nMultiple FROM clauses are allowed, which would cause the result\narray elements to cycle through the classes included in the\nmultiple FROM clauses.\n\n.. note::\n\n    You cannot select other entities unless you also select the\n    root of the selection (which is the first entity in FROM).\n\n    For example, ``SELECT p,n FROM Users u...`` would be wrong because\n    ``u`` is not part of the SELECT\n\n    Doctrine throws an exception if you violate this constraint.\n\n\nJoins\n~~~~~\n\nA SELECT query can contain joins. There are 2 types of JOINs:\n\"Regular\" Joins and \"Fetch\" Joins.\n\n**Regular Joins**: Used to limit the results and/or compute\naggregate values.\n\n**Fetch Joins**: In addition to the uses of regular joins: Used to\nfetch related entities and include them in the hydrated result of a\nquery.\n\nThere is no special DQL keyword that distinguishes a regular join\nfrom a fetch join. A join (be it an inner or outer join) becomes a\n\"fetch join\" as soon as fields of the joined entity appear in the\nSELECT part of the DQL query outside of an aggregate function.\nOtherwise its a \"regular join\".\n\nExample:\n\nRegular join of the address:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'\");\n    $users = $query->getResult();\n\nFetch join of the address:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'\");\n    $users = $query->getResult();\n\nWhen Doctrine hydrates a query with fetch-join it returns the class\nin the FROM clause on the root level of the result array. In the\nprevious example an array of User instances is returned and the\naddress of each user is fetched and hydrated into the\n``User#address`` variable. If you access the address Doctrine does\nnot need to lazy load the association with another query.\n\n.. note::\n\n    Doctrine allows you to walk all the associations between\n    all the objects in your domain model. Objects that were not already\n    loaded from the database are replaced with lazy-loading proxy\n    instances. Non-loaded Collections are also replaced by lazy-loading\n    instances that fetch all the contained objects upon first access.\n    However relying on the lazy-loading mechanism leads to many small\n    queries executed against the database, which can significantly\n    affect the performance of your application. **Fetch Joins** are the\n    solution to hydrate most or all of the entities that you need in a\n    single SELECT query.\n\n\nNamed and Positional Parameters\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDQL supports both named and positional parameters, however in\ncontrast to many SQL dialects positional parameters are specified\nwith numbers, for example \"?1\", \"?2\" and so on. Named parameters\nare specified with \":name1\", \":name2\" and so on.\n\nWhen referencing the parameters in ``Query#setParameter($param, $value)``\nboth named and positional parameters are used **without** their prefixes.\n\nDQL SELECT Examples\n~~~~~~~~~~~~~~~~~~~\n\nThis section contains a large set of DQL queries and some\nexplanations of what is happening. The actual result also depends\non the hydration mode.\n\nHydrate all User entities:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM MyProject\\Model\\User u');\n    $users = $query->getResult(); // array of User objects\n\nRetrieve the IDs of all CmsUsers:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.id FROM CmsUser u');\n    $ids = $query->getResult(); // array of CmsUser ids\n\nRetrieve the IDs of all users that have written an article:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u');\n    $ids = $query->getResult(); // array of CmsUser ids\n\nRetrieve all articles and sort them by the name of the articles\nusers instance:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC');\n    $articles = $query->getResult(); // array of CmsArticle objects\n\nRetrieve the Username and Name of a CmsUser:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.username, u.name FROM CmsUser u');\n    $users = $query->getResult(); // array of CmsUser username and name values\n    echo $users[0]['username'];\n\nRetrieve a ForumUser and its single associated entity:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a');\n    $users = $query->getResult(); // array of ForumUser objects with the avatar association loaded\n    echo get_class($users[0]->getAvatar());\n\nRetrieve a CmsUser and fetch join all the phonenumbers it has:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p');\n    $users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded\n    $phonenumbers = $users[0]->getPhonenumbers();\n\nHydrate a result in Ascending:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC');\n    $users = $query->getResult(); // array of ForumUser objects\n\nOr in Descending Order:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC');\n    $users = $query->getResult(); // array of ForumUser objects\n\nUsing Aggregate Functions:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT COUNT(u.id) FROM Entities\\User u');\n    $count = $query->getSingleScalarResult();\n\n    $query = $em->createQuery('SELECT u, count(g.id) FROM Entities\\User u JOIN u.groups g GROUP BY u.id');\n    $result = $query->getResult();\n\nWith WHERE Clause and Positional Parameter:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1');\n    $query->setParameter(1, 321);\n    $users = $query->getResult(); // array of ForumUser objects\n\nWith WHERE Clause and Named Parameter:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM ForumUser u WHERE u.username = :name');\n    $query->setParameter('name', 'Bob');\n    $users = $query->getResult(); // array of ForumUser objects\n\nWith Nested Conditions in WHERE Clause:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id');\n    $query->setParameters([\n        'name' => 'Bob',\n        'name2' => 'Alice',\n        'id' => 321,\n    ]);\n    $users = $query->getResult(); // array of ForumUser objects\n\nWith COUNT DISTINCT:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser');\n    $users = $query->getResult(); // array of ForumUser objects\n\nWith Arithmetic Expression in WHERE clause:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');\n    $users = $query->getResult(); // array of ForumUser objects\n\nRetrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score');\n    $users = $query->getResult(); // array of User objects\n\nUsing a LEFT JOIN to hydrate all user-ids and optionally associated\narticle-ids:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a');\n    $results = $query->getResult(); // array of user ids and every article_id for each user\n\nRestricting a JOIN clause by additional conditions specified by\nWITH:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE :foo\");\n    $query->setParameter('foo', '%foo%');\n    $users = $query->getResult();\n\nUsing several Fetch JOINs:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c');\n    $users = $query->getResult();\n\nBETWEEN in WHERE clause:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2');\n    $query->setParameter(1, 123);\n    $query->setParameter(2, 321);\n    $usernames = $query->getResult();\n\nDQL Functions in WHERE clause:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'\");\n    $usernames = $query->getResult();\n\nIN() Expression:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.name FROM CmsUser u WHERE u.id IN(46)');\n    $usernames = $query->getResult();\n\n    $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id IN (1, 2)');\n    $users = $query->getResult();\n\n    $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)');\n    $users = $query->getResult();\n\nCONCAT() DQL Function:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1\");\n    $query->setParameter(1, 'Jess');\n    $ids = $query->getResult();\n\n    $query = $em->createQuery('SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1');\n    $query->setParameter(1, 321);\n    $idUsernames = $query->getResult();\n\nEXISTS in WHERE clause with correlated Subquery\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)');\n    $ids = $query->getResult();\n\nGet all users who are members of $group.\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups');\n    $query->setParameter('groupId', $group);\n    $ids = $query->getResult();\n\nGet all users that have more than 1 phonenumber\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1');\n    $users = $query->getResult();\n\nGet all users that have no phonenumber\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY');\n    $users = $query->getResult();\n\nGet all instances of a specific type, for use with inheritance\nhierarchies:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyEmployee');\n    $query = $em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF ?1');\n    $query = $em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u NOT INSTANCE OF ?1');\n    $query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class));\n\n.. note::\n    To use a class as parameter, you have to bind its class metadata:\n    ``$query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class);``.\n\nGet all users visible on a given website that have chosen certain gender:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)');\n\nThe IDENTITY() DQL function also works for composite primary keys\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1\");\n\nJoins between entities without associations are available,\nwhere you can generate an arbitrary join with the following syntax:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM User u JOIN Banlist b ON u.email = b.email');\n\nWith an arbitrary join the result differs from the joins using a mapped property.\nThe result of an arbitrary join is an one dimensional array with a mix of the entity from the ``SELECT``\nand the joined entity fitting to the filtering of the query. In case of the example with ``User``\nand ``Banlist``, it can look like this:\n\n- User\n- Banlist\n- Banlist\n- User\n- Banlist\n- User\n- Banlist\n- Banlist\n- Banlist\n\nIn this form of join, the ``Banlist`` entities found by the filtering in the ``WITH`` part are not fetched by an accessor\nmethod on ``User``, but are already part of the result. In case the accessor method for Banlists is invoked on a User instance,\nit loads all the related ``Banlist`` objects corresponding to this ``User``. This change of behaviour needs to be considered\nwhen the DQL is switched to an arbitrary join.\n\n.. note::\n    The differences between WHERE, WITH, ON and HAVING clauses may be\n    confusing.\n\n    - WHERE is applied to the results of an entire query\n    - ON is applied to arbitrary joins as the join condition. For\n      arbitrary joins (SELECT f, b FROM Foo f, Bar b ON f.id = b.id)\n      the ON is required, even if it is 1 = 1. WITH is also\n      supported as alternative keyword for that case for BC reasons.\n    - WITH is applied to an association join as an additional condition.\n    - HAVING is applied to the results of a query after\n      aggregation (GROUP BY)\n\n\nPartial Hydration Syntax\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nBy default when you run a DQL query in Doctrine and select only a\nsubset of the fields for a given entity, you do not receive objects\nback. Instead, you receive only arrays as a flat rectangular result\nset, similar to how you would if you were just using SQL directly\nand joining some data.\n\nIf you want to select partial objects or fields in array hydration you can use the ``partial``\nDQL keyword:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT partial u.{id, username} FROM CmsUser u');\n    $users = $query->getResult(); // array of partially loaded CmsUser objects\n\nYou can use the partial syntax when joining as well:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a');\n    $usersArray = $query->getArrayResult(); // array of partially loaded CmsUser and CmsArticle fields\n    $users = $query->getResult(); // array of partially loaded CmsUser objects\n\n\"NEW\" Operator Syntax\n^^^^^^^^^^^^^^^^^^^^^\n\nUsing the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries.\n\n- When using ``SELECT NEW`` you don't need to specify a mapped entity.\n- You can specify any PHP class, it only requires that the constructor of this class matches the ``NEW`` statement.\n- This approach involves determining exactly which columns you really need,\n  and instantiating a data-transfer object that contains a constructor with those arguments.\n\nIf you want to select data-transfer objects you should create a class:\n\n.. code-block:: php\n\n    <?php\n    class CustomerDTO\n    {\n        public function __construct($name, $email, $city, $value = null)\n        {\n            // Bind values to the object properties.\n        }\n    }\n\nAnd then use the ``NEW`` DQL keyword :\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c');\n    $users = $query->getResult(); // array of CustomerDTO\n\nYou can also nest several DTO :\n\n.. code-block:: php\n\n    <?php\n    class CustomerDTO\n    {\n        public function __construct(string $name, string $email, AddressDTO $address, string|null $value = null)\n        {\n            // Bind values to the object properties.\n        }\n    }\n\n    class AddressDTO\n    {\n        public function __construct(string $street, string $city, string $zip)\n        {\n            // Bind values to the object properties.\n        }\n    }\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, NEW AddressDTO(a.street, a.city, a.zip)) FROM Customer c JOIN c.email e JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\nNote that you can only pass scalar expressions or other Data Transfer Objects to the constructor.\n\nIf you use your data transfer objects for multiple queries, and you would rather not have to\nspecify arguments that precede the ones you are really interested in, you can use named arguments.\n\nConsider the following DTO, which uses optional arguments:\n\n.. code-block:: php\n\n    <?php\n\n    class CustomerDTO\n    {\n        public function __construct(\n            public string|null $name = null,\n            public string|null $email = null,\n            public string|null $city = null,\n            public mixed|null $value = null,\n            public AddressDTO|null $address = null,\n        ) {\n        }\n    }\n\nYou can specify arbitrary arguments in an arbitrary order by using the named argument syntax, and the ORM will try to match argument names with the selected column names.\nThe syntax relies on the NAMED keyword, like so:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW NAMED CustomerDTO(a.city, c.name) FROM Customer c JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\n    // CustomerDTO => {name : 'SMITH', email: null, city: 'London', value: null}\n\nORM will also give precedence to column aliases over column names :\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW NAMED CustomerDTO(c.name, CONCAT(a.city, ' ' , a.zip) AS value) FROM Customer c JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\n    // CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'}\n\nTo define a custom name for a DTO constructor argument, you can either alias the column with the ``AS`` keyword.\n\nThe ``NAMED`` keyword must precede all DTO you want to instantiate :\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW NAMED CustomerDTO(c.name, NEW NAMED AddressDTO(a.street, a.city, a.zip) AS address) FROM Customer c JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\n    // CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'}\n\nIf two arguments have the same name, a ``DuplicateFieldException`` is thrown.\nIf a field cannot be matched with a property name, a ``NoMatchingPropertyException`` is thrown. This typically happens when using functions without aliasing them.\n\nYou can hydrate an entity nested in a DTO :\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT NEW CustomerDTO(c.name, a AS address) FROM Customer c JOIN c.address a');\n    $users = $query->getResult(); // array of CustomerDTO\n\n    // CustomerDTO => {name : 'DOE', email: null, address : {city: 'New York', zip: '10011', address: 'Abbey Road'}\n\nUsing INDEX BY\n~~~~~~~~~~~~~~\n\nThe INDEX BY construct is nothing that directly translates into SQL\nbut that affects object and array hydration. After each FROM and\nJOIN clause you specify by which field this class should be indexed\nin the result. By default a result is incremented by numerical keys\nstarting with 0. However with INDEX BY you can specify any other\ncolumn to be the key of your result, it really only makes sense\nwith primary or unique fields though:\n\n.. code-block:: sql\n\n    SELECT u.id, u.status, upper(u.name) nameUpper FROM User u INDEX BY u.id\n    JOIN u.phonenumbers p INDEX BY p.phonenumber\n\nReturns an array of the following kind, indexed by both user-id\nthen phonenumber-id:\n\n.. code-block:: php\n\n    array\n      0 =>\n        array\n          1 =>\n            object(stdClass)[299]\n              public '__CLASS__' => string 'Doctrine\\Tests\\Models\\CMS\\CmsUser' (length=33)\n              public 'id' => int 1\n              ..\n          'nameUpper' => string 'ROMANB' (length=6)\n      1 =>\n        array\n          2 =>\n            object(stdClass)[298]\n              public '__CLASS__' => string 'Doctrine\\Tests\\Models\\CMS\\CmsUser' (length=33)\n              public 'id' => int 2\n              ...\n          'nameUpper' => string 'JWAGE' (length=5)\n\nYou can also index by a to-one association, which will use the id of\nthe associated entity (the join column) as the key in the result set:\n\n.. code-block:: sql\n\n    SELECT p, u FROM Participant INDEX BY p.user JOIN p.user u WHERE p.event = 3\n\nUPDATE queries\n--------------\n\nDQL not only allows to select your Entities using field names, you\ncan also execute bulk updates on a set of entities using an\nDQL-UPDATE query. The Syntax of an UPDATE query works as expected,\nas the following example shows:\n\n.. code-block:: sql\n\n    UPDATE MyProject\\Model\\User u SET u.password = 'new' WHERE u.id IN (1, 2, 3)\n\nReferences to related entities are only possible in the WHERE\nclause and using sub-selects.\n\n.. warning::\n\n    DQL UPDATE statements are ported directly into a\n    Database UPDATE statement and therefore bypass any locking scheme, events\n    and do not increment the version column. Entities that are already\n    loaded into the persistence context will *NOT* be synced with the\n    updated database state. It is recommended to call\n    ``EntityManager#clear()`` and retrieve new instances of any\n    affected entity.\n\n\nDELETE queries\n--------------\n\nDELETE queries can also be specified using DQL and their syntax is\nas simple as the UPDATE syntax:\n\n.. code-block:: sql\n\n    DELETE MyProject\\Model\\User u WHERE u.id = 4\n\nThe same restrictions apply for the reference of related entities.\n\n.. warning::\n\n    DQL DELETE statements are ported directly into an SQL DELETE statement.\n    Therefore, some limitations apply:\n\n    - Lifecycle events for the affected entities are not executed.\n    - A cascading ``remove`` operation (as indicated e. g. by ``cascade: ['remove']``\n      or ``cascade: ['all']`` in the mapping configuration) is not being performed\n      for associated entities. You can rely on database level cascade operations by\n      configuring each join column with the ``onDelete`` option.\n    - Checks for the version column are bypassed if they are not explicitly added\n      to the WHERE clause of the query.\n\n    When you rely on one of these features, one option is to use the\n    ``EntityManager#remove($entity)`` method. This, however, is costly performance-wise:\n    It means collections and related entities are fetched into memory\n    (even if they are marked as lazy). Pulling object graphs into memory on cascade\n    can cause considerable performance overhead, especially when the cascaded collections\n    are large. Make sure to weigh the benefits and downsides.\n\nComments in queries\n-------------------\n\nWe can use comments with the SQL syntax of comments.\n\n.. code-block:: sql\n\n    SELECT u FROM MyProject\\Model\\User u\n    -- my comment\n    WHERE u.age > 20 -- comment at the end of a line\n\nFunctions, Operators, Aggregates\n--------------------------------\nIt is possible to wrap both fields and identification values into\naggregation and DQL functions. Numerical fields can be part of\ncomputations using mathematical operations.\n\nDQL Functions\n~~~~~~~~~~~~~\n\nThe following functions are supported in SELECT, WHERE and HAVING\nclauses:\n\n\n-  ``IDENTITY(single_association_path_expression [, fieldMapping])`` -\n   Retrieve the foreign key column of association of the owning side\n-  ``ABS(arithmetic_expression)``\n-  ``CONCAT(str1, str2)``\n-  ``CURRENT_DATE()`` - Return the current date\n-  ``CURRENT_TIME()`` - Returns the current time\n-  ``CURRENT_TIMESTAMP()`` - Returns a timestamp of the current date\n   and time.\n-  ``LENGTH(str)`` - Returns the length of the given string\n-  ``LOCATE(needle, haystack [, offset])`` - Locate the first\n   occurrence of the substring in the string.\n-  ``LOWER(str)`` - returns the string lowercased.\n-  ``MOD(a, b)`` - Return a MOD b.\n-  ``SIZE(collection)`` - Return the number of elements in the\n   specified collection\n-  ``SQRT(q)`` - Return the square-root of q.\n-  ``SUBSTRING(str, start [, length])`` - Return substring of given\n   string.\n-  ``TRIM([LEADING | TRAILING | BOTH] ['trchar' FROM] str)`` - Trim\n   the string by the given trim char, defaults to whitespaces.\n-  ``UPPER(str)`` - Return the upper-case of the given string.\n-  ``DATE_ADD(date, value, unit)`` - Add the given time to a given date.\n   (Supported units are ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``,\n   ``WEEK``, ``MONTH``, ``YEAR``)\n-  ``DATE_SUB(date, value, unit)`` - Subtract the given time from a\n   given date. (Supported units are ``SECOND``, ``MINUTE``, ``HOUR``,\n   ``DAY``, ``WEEK``, ``MONTH``, ``YEAR``)\n-  ``DATE_DIFF(date1, date2)`` - Calculate the difference in days\n   between date1-date2.\n\nArithmetic operators\n~~~~~~~~~~~~~~~~~~~~\n\nYou can do math in DQL using numeric values, for example:\n\n.. code-block:: sql\n\n    SELECT person.salary * 1.5 FROM CompanyPerson person WHERE person.salary < 100000\n\nAggregate Functions\n~~~~~~~~~~~~~~~~~~~\n\nThe following aggregate functions are allowed in SELECT and GROUP\nBY clauses: AVG, COUNT, MIN, MAX, SUM\n\nOther Expressions\n~~~~~~~~~~~~~~~~~\n\nDQL offers a wide-range of additional expressions that are known\nfrom SQL, here is a list of all the supported constructs:\n\n\n-  ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a\n   sub-select this works like the equivalent constructs in SQL.\n-  ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to\n   match ranges of arithmetic values.\n-  ``IN (x1, x2, ...)`` and ``NOT IN (x1, x2, ..)`` can be used to\n   match a set of given values.\n-  ``LIKE ..`` and ``NOT LIKE ..`` match parts of a string or text\n   using % as a wildcard.\n-  ``IS NULL`` and ``IS NOT NULL`` to check for null values\n-  ``EXISTS`` and ``NOT EXISTS`` in combination with a sub-select\n\nAdding your own functions to the DQL language\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default DQL comes with functions that are part of a large basis\nof underlying databases. However you will most likely choose a\ndatabase platform at the beginning of your project and most likely\nnever change it. For this cases you can easily extend the DQL\nparser with own specialized platform functions.\n\nYou can register custom DQL functions in your ORM Configuration:\n\n.. code-block:: php\n\n    <?php\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $config->addCustomStringFunction($name, $class);\n    $config->addCustomNumericFunction($name, $class);\n    $config->addCustomDatetimeFunction($name, $class);\n\n    $em = new EntityManager($connection, $config);\n\nThe functions have to return either a string, numeric or datetime\nvalue depending on the registered function type. As an example we\nwill add a MySQL specific FLOOR() functionality. All the given\nclasses have to implement the base class :\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\Query\\AST;\n\n    use Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\n    use Doctrine\\ORM\\Query\\TokenType;\n\n    class MysqlFloor extends FunctionNode\n    {\n        public $simpleArithmeticExpression;\n\n        public function getSql(\\Doctrine\\ORM\\Query\\SqlWalker $sqlWalker)\n        {\n            return 'FLOOR(' . $sqlWalker->walkSimpleArithmeticExpression(\n                $this->simpleArithmeticExpression\n            ) . ')';\n        }\n\n        public function parse(\\Doctrine\\ORM\\Query\\Parser $parser)\n        {\n            $parser->match(TokenType::T_IDENTIFIER);\n            $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n            $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n            $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n        }\n    }\n\nWe will register the function by calling and can then use it:\n\n.. code-block:: php\n\n    <?php\n    $config = $em->getConfiguration();\n    $config->registerNumericFunction('FLOOR', 'MyProject\\Query\\MysqlFloor');\n\n    $dql = \"SELECT FLOOR(person.salary * 1.75) FROM CompanyPerson person\";\n\nQuerying Inherited Classes\n--------------------------\n\nThis section demonstrates how you can query inherited classes and\nwhat type of results to expect.\n\nSingle Table\n~~~~~~~~~~~~\n\n`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_\nis an inheritance mapping strategy where all classes of a hierarchy\nare mapped to a single database table. In order to distinguish\nwhich row represents which type in the hierarchy a so-called\ndiscriminator column is used.\n\nFirst we need to setup an example set of entities to use. In this\nscenario it is a generic Person and Employee example:\n\n.. code-block:: php\n\n    <?php\n    namespace Entities;\n\n    #[Entity]\n    #[InheritanceType('SINGLE_TABLE')]\n    #[DiscriminatorColumn(name: 'discr', type: 'string')]\n    #[DiscriminatorMap(['person' => 'Person', 'employee' => 'Employee'])]\n    class Person\n    {\n        #[Id, Column(type: 'integer')]\n        #[GeneratedValue]\n        protected int|null $id = null;\n\n        #[Column(type: 'string', length: 50)]\n        protected string $name;\n\n        // ...\n    }\n\n    #[Entity]\n    class Employee extends Person\n    {\n        #[Column(type: 'string', length: 50)]\n        private $department;\n\n        // ...\n    }\n\nFirst notice that the generated SQL to create the tables for these\nentities looks like the following:\n\n.. code-block:: sql\n\n    CREATE TABLE Person (\n        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n        name VARCHAR(50) NOT NULL,\n        discr VARCHAR(255) NOT NULL,\n        department VARCHAR(50) NOT NULL\n    )\n\nNow when persist a new ``Employee`` instance it will set the\ndiscriminator value for us automatically:\n\n.. code-block:: php\n\n    <?php\n    $employee = new \\Entities\\Employee();\n    $employee->setName('test');\n    $employee->setDepartment('testing');\n    $em->persist($employee);\n    $em->flush();\n\nNow lets run a simple query to retrieve the ``Employee`` we just\ncreated:\n\n.. code-block:: sql\n\n    SELECT e FROM Entities\\Employee e WHERE e.name = 'test'\n\nIf we check the generated SQL you will notice it has some special\nconditions added to ensure that we will only get back ``Employee``\nentities:\n\n.. code-block:: sql\n\n    SELECT p0_.id AS id0, p0_.name AS name1, p0_.department AS department2,\n           p0_.discr AS discr3 FROM Person p0_\n    WHERE (p0_.name = ?) AND p0_.discr IN ('employee')\n\nClass Table Inheritance\n~~~~~~~~~~~~~~~~~~~~~~~\n\n`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_\nis an inheritance mapping strategy where each class in a hierarchy\nis mapped to several tables: its own table and the tables of all\nparent classes. The table of a child class is linked to the table\nof a parent class through a foreign key constraint. Doctrine ORM\nimplements this strategy through the use of a discriminator column\nin the topmost table of the hierarchy because this is the easiest\nway to achieve polymorphic queries with Class Table Inheritance.\n\nThe example for class table inheritance is the same as single\ntable, you just need to change the inheritance type from\n``SINGLE_TABLE`` to ``JOINED``:\n\n.. code-block:: php\n\n    <?php\n\n    #[Entity]\n    #[InheritanceType('JOINED')]\n    #[DiscriminatorColumn(name: 'discr', type: 'string')]\n    #[DiscriminatorMap(['person' => 'Person', 'employee' => 'Employee'])]\n    class Person\n    {\n        // ...\n    }\n\nNow take a look at the SQL which is generated to create the table,\nyou'll notice some differences:\n\n.. code-block:: sql\n\n    CREATE TABLE Person (\n        id INT AUTO_INCREMENT NOT NULL,\n        name VARCHAR(50) NOT NULL,\n        discr VARCHAR(255) NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    CREATE TABLE Employee (\n        id INT NOT NULL,\n        department VARCHAR(50) NOT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n    ALTER TABLE Employee ADD FOREIGN KEY (id) REFERENCES Person(id) ON DELETE CASCADE\n\n\n-  The data is split between two tables\n-  A foreign key exists between the two tables\n\nNow if were to insert the same ``Employee`` as we did in the\n``SINGLE_TABLE`` example and run the same example query it will\ngenerate different SQL joining the ``Person`` information\nautomatically for you:\n\n.. code-block:: sql\n\n    SELECT p0_.id AS id0, p0_.name AS name1, e1_.department AS department2,\n           p0_.discr AS discr3\n    FROM Employee e1_ INNER JOIN Person p0_ ON e1_.id = p0_.id\n    WHERE p0_.name = ?\n\n\nThe Query class\n---------------\n\nAn instance of the ``Doctrine\\ORM\\Query`` class represents a DQL\nquery. You create a Query instance by calling\n``EntityManager#createQuery($dql)``, passing the DQL query string.\nAlternatively you can create an empty ``Query`` instance and invoke\n``Query#setDQL($dql)`` afterwards. Here are some examples:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n\n    // example1: passing a DQL string\n    $q = $em->createQuery('select u from MyProject\\Model\\User u');\n\n    // example2: using setDQL\n    $q = $em->createQuery();\n    $q->setDQL('select u from MyProject\\Model\\User u');\n\nQuery Result Formats (Hydration Modes)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe way in which the SQL result set of a DQL SELECT query is transformed\nto PHP is determined by the so-called \"hydration mode\".\n\n``getResult()``\n^^^^^^^^^^^^^^^\n\nRetrieves a collection of objects. The result is either a plain collection of objects (pure) or an array\nwhere the objects are nested in the result rows (mixed):\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\AbstractQuery;\n\n    $query = $em->createQuery('SELECT u FROM User u');\n    $users = $query->getResult();\n    // same as:\n    $users = $query->getResult(AbstractQuery::HYDRATE_OBJECT);\n\n- Objects fetched in a FROM clause are returned as a Set, that means every\n  object is only ever included in the resulting array once. This is the case\n  even when using JOIN or GROUP BY in ways that return the same row for an\n  object multiple times. If the hydrator sees the same object multiple times,\n  then it makes sure it is only returned once.\n\n- If an object is already in memory from a previous query of any kind, then\n  then the previous object is used, even if the database may contain more\n  recent data. This even happens if the previous object is still an unloaded proxy.\n\n``getArrayResult()``\n^^^^^^^^^^^^^^^^^^^^\n\nRetrieves an array graph (a nested array) for read-only purposes.\n\n.. note::\n\n    An array graph can differ from the corresponding object\n    graph in certain scenarios due to the difference of the identity\n    semantics between arrays and objects.\n\n.. code-block:: php\n\n    <?php\n    $users = $query->getArrayResult();\n    // same as:\n    $users = $query->getResult(AbstractQuery::HYDRATE_ARRAY);\n\n``getScalarResult()``\n^^^^^^^^^^^^^^^^^^^^^\n\nRetrieves a flat/rectangular result set of scalar values that can contain duplicate data. The\npure/mixed distinction does not apply.\n\n.. code-block:: php\n\n    <?php\n    $users = $query->getScalarResult();\n    // same as:\n    $users = $query->getResult(AbstractQuery::HYDRATE_SCALAR);\n\nFields from classes are prefixed by the DQL alias in the result.\nA query of the kind `SELECT u.name ...` returns a key `u_name` in the result rows.\n\n``getSingleScalarResult()``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieves a single scalar value from the result returned by the database. If the result contains\nmore than a single scalar value, a ``NonUniqueResultException`` is thrown. The pure/mixed distinction does not apply.\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT COUNT(u.id) FROM User u');\n    $numUsers = $query->getSingleScalarResult();\n    // same as:\n    $numUsers = $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR);\n\n``getSingleColumnResult()``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieves an array from a one-dimensional array of scalar values:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT a.id FROM User u');\n    $ids = $query->getSingleColumnResult();\n    // same as:\n    $ids = $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN);\n\n``getSingleResult()``\n^^^^^^^^^^^^^^^^^^^^^\n\nRetrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``\nis thrown. If the result contains no objects, a ``NoResultException`` is thrown. The pure/mixed distinction does not apply.\n\n``getOneOrNullResult()``\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``\nis thrown. If no object is found, ``null`` will be returned.\n\nCustom Hydration Modes\n^^^^^^^^^^^^^^^^^^^^^^\n\nYou can easily add your own custom hydration modes by first\ncreating a class which extends ``AbstractHydrator``:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\Hydrators;\n\n    use Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\n\n    class CustomHydrator extends AbstractHydrator\n    {\n        protected function _hydrateAll()\n        {\n            return $this->_stmt->fetchAllAssociative();\n        }\n    }\n\nNext you just need to add the class to the ORM configuration:\n\n.. code-block:: php\n\n    <?php\n    $em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\\Hydrators\\CustomHydrator');\n\nNow the hydrator is ready to be used in your queries:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM CmsUser u');\n    $results = $query->getResult('CustomHydrator');\n\nPure and Mixed Results\n~~~~~~~~~~~~~~~~~~~~~~\n\nThe nature of a result returned by a DQL SELECT query retrieved\nthrough ``Query#getResult()`` or ``Query#getArrayResult()`` can be\nof 2 forms: **pure** and **mixed**. In the previous simple\nexamples, you already saw a \"pure\" query result, with only objects.\nBy default, the result type is **pure** but\n**as soon as scalar values, such as aggregate values or other scalar values that do not belong to an entity, appear in the SELECT part of the DQL query, the result becomes mixed**.\nA mixed result has a different structure than a pure result in\norder to accommodate for the scalar values.\n\nA pure result usually looks like this:\n\n.. code-block:: php\n\n    $dql = \"SELECT u FROM User u\";\n\n    array\n        [0] => Object\n        [1] => Object\n        [2] => Object\n        ...\n\nA mixed result on the other hand has the following general\nstructure:\n\n.. code-block:: php\n\n    $dql = \"SELECT u, 'some scalar string', count(g.id) AS num FROM User u JOIN u.groups g GROUP BY u.id\";\n\n    array\n        [0]\n            [0] => Object\n            [1] => \"some scalar string\"\n            ['num'] => 42\n            // ... more scalar values, either indexed numerically or with a name\n        [1]\n            [0] => Object\n            [1] => \"some scalar string\"\n            ['num'] => 42\n            // ... more scalar values, either indexed numerically or with a name\n\nTo better understand mixed results, consider the following DQL\nquery:\n\n.. code-block:: sql\n\n    SELECT u, UPPER(u.name) nameUpper FROM MyProject\\Model\\User u\n\nThis query makes use of the ``UPPER`` DQL function that returns a\nscalar value and because there is now a scalar value in the SELECT\nclause, we get a mixed result.\n\nConventions for mixed results are as follows:\n\n\n-  The object fetched in the FROM clause is always positioned with the key '0'.\n-  Every scalar without a name is numbered in the order given in the query, starting with 1.\n-  Every aliased scalar is given with its alias-name as the key. The case of the name is kept.\n-  If several objects are fetched from the FROM clause they alternate every row.\n\n\nHere is how the result could look like:\n\n.. code-block:: php\n\n    array\n        array\n            [0] => User (Object)\n            ['nameUpper'] => \"ROMAN\"\n        array\n            [0] => User (Object)\n            ['nameUpper'] => \"JONATHAN\"\n        ...\n\nAnd here is how you would access it in PHP code:\n\n.. code-block:: php\n\n    <?php\n    foreach ($results as $row) {\n        echo \"Name: \" . $row[0]->getName();\n        echo \"Name UPPER: \" . $row['nameUpper'];\n    }\n\nFetching Multiple FROM Entities\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you fetch multiple entities that are listed in the FROM clause then the hydration\nwill return the rows iterating the different top-level entities.\n\n.. code-block:: php\n\n    $dql = \"SELECT u, g FROM User u, Group g\";\n\n    array\n        [0] => Object (User)\n        [1] => Object (Group)\n        [2] => Object (User)\n        [3] => Object (Group)\n\nIterating Large Result Sets\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are situations when a query you want to execute returns a\nvery large result-set that needs to be processed. All the\npreviously described hydration modes completely load a result-set\ninto memory which might not be feasible with large result sets. See\nthe :doc:`Batch Processing </reference/batch-processing>` section on\ndetails how to iterate large result sets.\n\nFunctions\n~~~~~~~~~\n\nThe following methods exist on the ``AbstractQuery`` which both\n``Query`` and ``NativeQuery`` extend from.\n\nParameters\n^^^^^^^^^^\n\nPrepared Statements that use numerical or named wildcards require\nadditional parameters to be executable against the database. To\npass parameters to the query the following methods can be used:\n\n\n-  ``AbstractQuery::setParameter($param, $value)`` - Set the\n   numerical or named wildcard to the given value.\n-  ``AbstractQuery::setParameters(array $params)`` - Set an array\n   of parameter key-value pairs.\n-  ``AbstractQuery::getParameter($param)``\n-  ``AbstractQuery::getParameters()``\n\nBoth named and positional parameters are passed to these methods without their ? or : prefix.\n\nCache related API\n^^^^^^^^^^^^^^^^^\n\nYou can cache query results based either on all variables that\ndefine the result (SQL, Hydration Mode, Parameters and Hints) or on\nuser-defined cache keys. However by default query results are not\ncached at all. You have to enable the result cache on a per query\nbasis. The following example shows a complete workflow using the\nResult Cache API:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery('SELECT u FROM MyProject\\Model\\User u WHERE u.id = ?1');\n    $query->setParameter(1, 12);\n\n    $query->setResultCacheDriver(new ApcCache());\n\n    $query->enableResultCache(3600);\n\n    $result = $query->getResult(); // cache miss\n\n    $query->expireResultCache(true);\n    $result = $query->getResult(); // forced expire, cache miss\n\n    $query->setResultCacheId('my_query_result');\n    $result = $query->getResult(); // saved in given result cache id.\n\n    // or call enableResultCache() with all parameters:\n    $query->enableResultCache(3600, 'my_query_result');\n    $result = $query->getResult(); // cache hit!\n\n    // Introspection\n    $queryCacheProfile = $query->getQueryCacheProfile();\n    $cacheDriver = $query->getResultCacheDriver();\n    $lifetime = $query->getLifetime();\n    $key = $query->getCacheKey();\n\n.. note::\n\n    You can set the Result Cache Driver globally on the\n    ``Doctrine\\ORM\\Configuration`` instance so that it is passed to\n    every ``Query`` and ``NativeQuery`` instance.\n\n\nQuery Hints\n^^^^^^^^^^^\n\nYou can pass hints to the query parser and hydrators by using the\n``AbstractQuery::setHint($name, $value)`` method. Currently there\nexist mostly internal query hints that are not be consumed in\nuserland. However the following few hints are to be used in\nuserland:\n\n\n-  ``Query::HINT_FORCE_PARTIAL_LOAD`` - Allows to hydrate objects\n   although not all their columns are fetched. This query hint can be\n   used to handle memory consumption problems with large result-sets\n   that contain char or binary data. Doctrine has no way of implicitly\n   reloading this data. Partially loaded objects have to be passed to\n   ``EntityManager::refresh()`` if they are to be reloaded fully from\n   the database. This query hint is deprecated and will be removed\n   in the future (\\ `Details <https://github.com/doctrine/orm/issues/8471>`_)\n-  ``Query::HINT_REFRESH`` - This query is used internally by\n   ``EntityManager::refresh()`` and can be used in userland as well.\n   If you specify this hint and a query returns the data for an entity\n   that is already managed by the UnitOfWork, the fields of the\n   existing entity will be refreshed. In normal operation a result-set\n   that loads data of an already existing entity is discarded in favor\n   of the already existing entity.\n-  ``Query::HINT_CUSTOM_TREE_WALKERS`` - An array of additional\n   ``Doctrine\\ORM\\Query\\TreeWalker`` instances that are attached to\n   the DQL query parsing process.\n\nQuery Cache (DQL Query Only)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nParsing a DQL query and converting it into a SQL query against the\nunderlying database platform obviously has some overhead in\ncontrast to directly executing Native SQL queries. That is why\nthere is a dedicated Query Cache for caching the DQL parser\nresults. In combination with the use of wildcards you can reduce\nthe number of parsed queries in production to zero.\n\nThe Query Cache Driver is passed from the\n``Doctrine\\ORM\\Configuration`` instance to each\n``Doctrine\\ORM\\Query`` instance by default and is also enabled by\ndefault. This also means you don't regularly need to fiddle with\nthe parameters of the Query Cache, however if you do there are\nseveral methods to interact with it:\n\n\n-  ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache\n   instance\n-  ``Query::setQueryCacheLifeTime($seconds)`` - Set lifetime\n   of the query caching.\n-  ``Query::expireQueryCache($bool)`` - Enforce the expiring of the\n   query cache if set to true.\n-  ``Query::getExpireQueryCache()``\n-  ``Query::getQueryCacheDriver()``\n-  ``Query::getQueryCacheLifeTime()``\n\nFirst and Max Result Items (DQL Query Only)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can limit the number of results returned from a DQL query as\nwell as specify the starting offset, Doctrine then uses a strategy\nof manipulating the select query to return only the requested\nnumber of results:\n\n\n-  ``Query::setMaxResults($maxResults)``\n-  ``Query::setFirstResult($offset)``\n\n.. note::\n\n    If your query contains a fetch-joined collection\n    specifying the result limit methods are not working as you would\n    expect. Set Max Results restricts the number of database result\n    rows, however in the case of fetch-joined collections one root\n    entity might appear in many rows, effectively hydrating less than\n    the specified number of results.\n\n.. _dql-temporarily-change-fetch-mode:\n\nTemporarily change fetch mode in DQL\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhile normally all your associations are marked as lazy or extra lazy you will have cases where you are using DQL and don't want to\nfetch join a second, third or fourth level of entities into your result, because of the increased cost of the SQL JOIN. You\ncan mark a many-to-one or one-to-one association as fetched temporarily to batch fetch these entities using a WHERE .. IN query.\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT u FROM MyProject\\User u\");\n    $query->setFetchMode(\"MyProject\\User\", \"address\", \\Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_EAGER);\n    $query->execute();\n\nGiven that there are 10 users and corresponding addresses in the database the executed queries will look something like:\n\n.. code-block:: sql\n\n    SELECT * FROM users;\n    SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);\n\n.. note::\n    Changing the fetch mode during a query mostly makes sense for one-to-one and many-to-one relations. In that case,\n    all the necessary IDs are available after the root entity (``user`` in the above example) has been loaded. So, one\n    query per association can be executed to fetch all the referred-to entities (``address``).\n\n    For one-to-many relations, changing the fetch mode to eager will cause to execute one query **for every root entity\n    loaded**. This gives no improvement over the ``lazy`` fetch mode which will also initialize the associations on\n    a one-by-one basis once they are accessed.\n\n\n.. _dql_ebnf_grammar:\n\nEBNF\n----\n\nThe following context-free grammar, written in an EBNF variant,\ndescribes the Doctrine Query Language. You can consult this grammar\nwhenever you are unsure about what is possible with DQL or what the\ncorrect syntax for a particular query should be.\n\nDocument syntax:\n~~~~~~~~~~~~~~~~\n\n\n-  non-terminals begin with an upper case character\n-  terminals begin with a lower case character\n-  parentheses (...) are used for grouping\n-  square brackets [...] are used for defining an optional part,\n   e.g. zero or one time\n-  curly brackets {...} are used for repetition, e.g. zero or more\n   times\n-  double quotation marks \"...\" define a terminal string\n-  a vertical bar \\| represents an alternative\n\nTerminals\n~~~~~~~~~\n\n\n-  identifier (name, email, ...) must match ``[a-z_][a-z0-9_]*``\n-  fully_qualified_name (Doctrine\\Tests\\Models\\CMS\\CmsUser) matches PHP's fully qualified class names\n-  string ('foo', 'bar''s house', '%ninja%', ...)\n-  char ('/', '\\\\', ' ', ...)\n-  integer (-1, 0, 1, 34, ...)\n-  float (-0.23, 0.007, 1.245342E+8, ...)\n-  boolean (false, true)\n\nQuery Language\n~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement\n\nStatements\n~~~~~~~~~~\n\n.. code-block:: php\n\n    SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n    UpdateStatement ::= UpdateClause [WhereClause]\n    DeleteStatement ::= DeleteClause [WhereClause]\n\nIdentifiers\n~~~~~~~~~~~\n\n.. code-block:: php\n\n    /* Alias Identification usage (the \"u\" of \"u.name\") */\n    IdentificationVariable ::= identifier\n\n    /* Alias Identification declaration (the \"u\" of \"FROM User u\") */\n    AliasIdentificationVariable :: = identifier\n\n    /* identifier that must be a class name (the \"User\" of \"FROM User u\"), possibly as a fully qualified class name */\n    AbstractSchemaName ::= fully_qualified_name | identifier\n\n    /* Alias ResultVariable declaration (the \"total\" of \"COUNT(*) AS total\") */\n    AliasResultVariable = identifier\n\n    /* ResultVariable identifier usage of mapped field aliases (the \"total\" of \"COUNT(*) AS total\") */\n    ResultVariable = identifier\n\n    /* identifier that must be a field (the \"name\" of \"u.name\") */\n    /* This is responsible to know if the field exists in Object, no matter if it's a relation or a simple field */\n    FieldIdentificationVariable ::= identifier\n\n    /* identifier that must be a collection-valued association field (to-many) (the \"Phonenumbers\" of \"u.Phonenumbers\") */\n    CollectionValuedAssociationField ::= FieldIdentificationVariable\n\n    /* identifier that must be a single-valued association field (to-one) (the \"Group\" of \"u.Group\") */\n    SingleValuedAssociationField ::= FieldIdentificationVariable\n\n    /* identifier that must be an embedded class state field */\n    EmbeddedClassStateField ::= FieldIdentificationVariable\n\n    /* identifier that must be a simple state field (name, email, ...) (the \"name\" of \"u.name\") */\n    /* The difference between this and FieldIdentificationVariable is only semantical, because it points to a single field (not mapping to a relation) */\n    SimpleStateField ::= FieldIdentificationVariable\n\nPath Expressions\n~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    /* \"u.Group\" or \"u.Phonenumbers\" declarations */\n    JoinAssociationPathExpression             ::= IdentificationVariable \".\" (CollectionValuedAssociationField | SingleValuedAssociationField)\n\n    /* \"u.Group\" or \"u.Phonenumbers\" usages */\n    AssociationPathExpression                 ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression\n\n    /* \"u.name\" or \"u.Group\" */\n    SingleValuedPathExpression                ::= StateFieldPathExpression | SingleValuedAssociationPathExpression\n\n    /* \"u.name\" or \"u.Group.name\" */\n    StateFieldPathExpression                  ::= IdentificationVariable \".\" StateField\n\n    /* \"u.Group\" */\n    SingleValuedAssociationPathExpression     ::= IdentificationVariable \".\" SingleValuedAssociationField\n\n    /* \"u.Group.Permissions\" */\n    CollectionValuedPathExpression            ::= IdentificationVariable \".\" CollectionValuedAssociationField\n\n    /* \"name\" */\n    StateField                                ::= {EmbeddedClassStateField \".\"}* SimpleStateField\n\nClauses\n~~~~~~~\n\n.. code-block:: php\n\n    SelectClause        ::= \"SELECT\" [\"DISTINCT\"] SelectExpression {\",\" SelectExpression}*\n    SimpleSelectClause  ::= \"SELECT\" [\"DISTINCT\"] SimpleSelectExpression\n    UpdateClause        ::= \"UPDATE\" AbstractSchemaName [\"AS\"] AliasIdentificationVariable \"SET\" UpdateItem {\",\" UpdateItem}*\n    DeleteClause        ::= \"DELETE\" [\"FROM\"] AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n    FromClause          ::= \"FROM\" IdentificationVariableDeclaration {\",\" IdentificationVariableDeclaration}*\n    SubselectFromClause ::= \"FROM\" SubselectIdentificationVariableDeclaration {\",\" SubselectIdentificationVariableDeclaration}*\n    WhereClause         ::= \"WHERE\" ConditionalExpression\n    HavingClause        ::= \"HAVING\" ConditionalExpression\n    GroupByClause       ::= \"GROUP\" \"BY\" GroupByItem {\",\" GroupByItem}*\n    OrderByClause       ::= \"ORDER\" \"BY\" OrderByItem {\",\" OrderByItem}*\n    Subselect           ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n\nItems\n~~~~~\n\n.. code-block:: php\n\n    UpdateItem  ::= SingleValuedPathExpression \"=\" NewValue\n    OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable | FunctionDeclaration) [\"ASC\" | \"DESC\"]\n    GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression\n    NewValue    ::= SimpleArithmeticExpression | \"NULL\"\n\nFrom, Join and Index by\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    IdentificationVariableDeclaration          ::= RangeVariableDeclaration [IndexBy] {Join}*\n    SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration\n    RangeVariableDeclaration                   ::= AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n    JoinAssociationDeclaration                 ::= JoinAssociationPathExpression [\"AS\"] AliasIdentificationVariable [IndexBy]\n    Join                                       ::= [\"LEFT\" [\"OUTER\"] | \"INNER\"] \"JOIN\" (JoinAssociationDeclaration [\"WITH\" ConditionalExpression] | RangeVariableDeclaration [(\"ON\" | \"WITH\") ConditionalExpression])\n    IndexBy                                    ::= \"INDEX\" \"BY\" SingleValuedPathExpression\n\n.. note::\n    Using the ``WITH`` keyword for the ``ConditionalExpression`` of a\n    ``RangeVariableDeclaration`` is deprecated and will be removed in\n    ORM 4.0. Use the ``ON`` keyword instead.\n\nSelect Expressions\n~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    SelectExpression              ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | \"(\" Subselect \")\" | CaseExpression | NewObjectExpression) [[\"AS\"] [\"HIDDEN\"] AliasResultVariable]\n    SimpleSelectExpression        ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | \"(\" Subselect \")\" | ScalarExpression) [[\"AS\"] AliasResultVariable]\n    PartialObjectExpression       ::= \"PARTIAL\" IdentificationVariable \".\" PartialFieldSet\n    PartialFieldSet               ::= \"{\" SimpleStateField {\",\" SimpleStateField}* \"}\"\n    NewObjectExpression           ::= \"NEW\" AbstractSchemaName \"(\" NewObjectArg {\",\" NewObjectArg}* \")\"\n    NewObjectArg                  ::= (ScalarExpression | \"(\" Subselect \")\" | NewObjectExpression | EntityAsDtoArgumentExpression) [\"AS\" AliasResultVariable]\n    EntityAsDtoArgumentExpression ::= IdentificationVariable\n\nConditional Expressions\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    ConditionalExpression       ::= ConditionalTerm {\"OR\" ConditionalTerm}*\n    ConditionalTerm             ::= ConditionalFactor {\"AND\" ConditionalFactor}*\n    ConditionalFactor           ::= [\"NOT\"] ConditionalPrimary\n    ConditionalPrimary          ::= SimpleConditionalExpression | \"(\" ConditionalExpression \")\"\n    SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression |\n                                    InExpression | NullComparisonExpression | ExistsExpression |\n                                    EmptyCollectionComparisonExpression | CollectionMemberExpression |\n                                    InstanceOfExpression\n\n\nCollection Expressions\n~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression \"IS\" [\"NOT\"] \"EMPTY\"\n    CollectionMemberExpression          ::= EntityExpression [\"NOT\"] \"MEMBER\" [\"OF\"] CollectionValuedPathExpression\n\nLiteral Values\n~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    Literal     ::= string | char | integer | float | boolean\n    InParameter ::= ArithmeticExpression | InputParameter\n\nInput Parameter\n~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    InputParameter      ::= PositionalParameter | NamedParameter\n    PositionalParameter ::= \"?\" integer\n    NamedParameter      ::= \":\" string\n\nArithmetic Expressions\n~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    ArithmeticExpression       ::= SimpleArithmeticExpression | \"(\" Subselect \")\"\n    SimpleArithmeticExpression ::= ArithmeticTerm {(\"+\" | \"-\") ArithmeticTerm}*\n    ArithmeticTerm             ::= ArithmeticFactor {(\"*\" | \"/\") ArithmeticFactor}*\n    ArithmeticFactor           ::= [(\"+\" | \"-\")] ArithmeticPrimary\n    ArithmeticPrimary          ::= SingleValuedPathExpression | Literal | \"(\" SimpleArithmeticExpression \")\"\n                                   | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings\n                                   | FunctionsReturningDatetime | IdentificationVariable | ResultVariable\n                                   | InputParameter | CaseExpression\n\nScalar and Type Expressions\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    ScalarExpression       ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression\n    StringExpression       ::= StringPrimary | ResultVariable | \"(\" Subselect \")\"\n    StringPrimary          ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression\n    BooleanExpression      ::= BooleanPrimary | \"(\" Subselect \")\"\n    BooleanPrimary         ::= StateFieldPathExpression | boolean | InputParameter\n    EntityExpression       ::= SingleValuedAssociationPathExpression | SimpleEntityExpression\n    SimpleEntityExpression ::= IdentificationVariable | InputParameter\n    DatetimeExpression     ::= DatetimePrimary | \"(\" Subselect \")\"\n    DatetimePrimary        ::= StateFieldPathExpression | InputParameter | FunctionsReturningDatetime | AggregateExpression\n\n.. note::\n\n    Parts of CASE expressions are not yet implemented.\n\nAggregate Expressions\n~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    AggregateExpression ::= (\"AVG\" | \"MAX\" | \"MIN\" | \"SUM\" | \"COUNT\") \"(\" [\"DISTINCT\"] SimpleArithmeticExpression \")\"\n\nCase Expressions\n~~~~~~~~~~~~~~~~\n\n.. code-block:: php\n\n    CaseExpression        ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression\n    GeneralCaseExpression ::= \"CASE\" WhenClause {WhenClause}* \"ELSE\" ScalarExpression \"END\"\n    WhenClause            ::= \"WHEN\" ConditionalExpression \"THEN\" ScalarExpression\n    SimpleCaseExpression  ::= \"CASE\" CaseOperand SimpleWhenClause {SimpleWhenClause}* \"ELSE\" ScalarExpression \"END\"\n    CaseOperand           ::= StateFieldPathExpression | TypeDiscriminator\n    SimpleWhenClause      ::= \"WHEN\" ScalarExpression \"THEN\" ScalarExpression\n    CoalesceExpression    ::= \"COALESCE\" \"(\" ScalarExpression {\",\" ScalarExpression}* \")\"\n    NullifExpression      ::= \"NULLIF\" \"(\" ScalarExpression \",\" ScalarExpression \")\"\n\nOther Expressions\n~~~~~~~~~~~~~~~~~\n\nQUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS\n\n.. code-block:: php\n\n    QuantifiedExpression     ::= (\"ALL\" | \"ANY\" | \"SOME\") \"(\" Subselect \")\"\n    BetweenExpression        ::= ArithmeticExpression [\"NOT\"] \"BETWEEN\" ArithmeticExpression \"AND\" ArithmeticExpression\n    ComparisonExpression     ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )\n    InExpression             ::= ArithmeticExpression [\"NOT\"] \"IN\" \"(\" (InParameter {\",\" InParameter}* | Subselect) \")\"\n    InstanceOfExpression     ::= IdentificationVariable [\"NOT\"] \"INSTANCE\" [\"OF\"] (InstanceOfParameter | \"(\" InstanceOfParameter {\",\" InstanceOfParameter}* \")\")\n    InstanceOfParameter      ::= AbstractSchemaName | InputParameter\n    LikeExpression           ::= StringExpression [\"NOT\"] \"LIKE\" StringPrimary [\"ESCAPE\" char]\n    NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) \"IS\" [\"NOT\"] \"NULL\"\n    ExistsExpression         ::= [\"NOT\"] \"EXISTS\" \"(\" Subselect \")\"\n    ComparisonOperator       ::= \"=\" | \"<\" | \"<=\" | \"<>\" | \">\" | \">=\" | \"!=\"\n\nFunctions\n~~~~~~~~~\n\n.. code-block:: php\n\n    FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDateTime\n\n    FunctionsReturningNumerics ::=\n            \"LENGTH\" \"(\" StringPrimary \")\" |\n            \"LOCATE\" \"(\" StringPrimary \",\" StringPrimary [\",\" SimpleArithmeticExpression]\")\" |\n            \"ABS\" \"(\" SimpleArithmeticExpression \")\" |\n            \"SQRT\" \"(\" SimpleArithmeticExpression \")\" |\n            \"MOD\" \"(\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\" |\n            \"SIZE\" \"(\" CollectionValuedPathExpression \")\" |\n            \"DATE_DIFF\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\" |\n            \"BIT_AND\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\" |\n            \"BIT_OR\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n\n    FunctionsReturningDateTime ::=\n            \"CURRENT_DATE\" |\n            \"CURRENT_TIME\" |\n            \"CURRENT_TIMESTAMP\" |\n            \"DATE_ADD\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \",\" StringPrimary \")\" |\n            \"DATE_SUB\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \",\" StringPrimary \")\"\n\n    FunctionsReturningStrings ::=\n            \"CONCAT\" \"(\" StringPrimary \",\" StringPrimary \")\" |\n            \"SUBSTRING\" \"(\" StringPrimary \",\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\" |\n            \"TRIM\" \"(\" [[\"LEADING\" | \"TRAILING\" | \"BOTH\"] [char] \"FROM\"] StringPrimary \")\" |\n            \"LOWER\" \"(\" StringPrimary \")\" |\n            \"UPPER\" \"(\" StringPrimary \")\" |\n            \"IDENTITY\" \"(\" SingleValuedAssociationPathExpression {\",\" string} \")\"\n"
  },
  {
    "path": "docs/en/reference/events.rst",
    "content": "Events\n======\n\nDoctrine ORM features a lightweight event system that is part of the\nCommon package. Doctrine uses it to dispatch system events, mainly\n:ref:`lifecycle events <reference-events-lifecycle-events>`.\nYou can also use it for your own custom events.\n\nThe Event System\n----------------\n\nThe event system is controlled by the ``EventManager``. It is the\ncentral point of Doctrine's event listener system. Listeners are\nregistered on the manager and events are dispatched through the\nmanager.\n\n.. code-block:: php\n\n    <?php\n    $evm = new EventManager();\n\nNow we can add some event listeners to the ``$evm``. Let's create a\n``TestEvent`` class to play around with.\n\n.. code-block:: php\n\n    <?php\n    class TestEvent\n    {\n        const preFoo = 'preFoo';\n        const postFoo = 'postFoo';\n\n        private $_evm;\n\n        public $preFooInvoked = false;\n        public $postFooInvoked = false;\n\n        public function __construct($evm)\n        {\n            $evm->addEventListener(array(self::preFoo, self::postFoo), $this);\n        }\n\n        public function preFoo(EventArgs $e)\n        {\n            $this->preFooInvoked = true;\n        }\n\n        public function postFoo(EventArgs $e)\n        {\n            $this->postFooInvoked = true;\n        }\n    }\n\n    // Create a new instance\n    $test = new TestEvent($evm);\n\nEvents can be dispatched by using the ``dispatchEvent()`` method.\n\n.. code-block:: php\n\n    <?php\n    $evm->dispatchEvent(TestEvent::preFoo);\n    $evm->dispatchEvent(TestEvent::postFoo);\n\nYou can easily remove a listener with the ``removeEventListener()``\nmethod.\n\n.. code-block:: php\n\n    <?php\n    $evm->removeEventListener(array(self::preFoo, self::postFoo), $this);\n\nThe Doctrine ORM event system also has a simple concept of event\nsubscribers. We can define a simple ``TestEventSubscriber`` class\nwhich implements the ``\\Doctrine\\Common\\EventSubscriber`` interface\nand implements a ``getSubscribedEvents()`` method which returns an\narray of events it should be subscribed to.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Common\\EventSubscriber;\n\n    class TestEventSubscriber implements EventSubscriber\n    {\n        public $preFooInvoked = false;\n\n        public function preFoo()\n        {\n            $this->preFooInvoked = true;\n        }\n\n        public function getSubscribedEvents()\n        {\n            return array(TestEvent::preFoo);\n        }\n    }\n\n    $eventSubscriber = new TestEventSubscriber();\n    $evm->addEventSubscriber($eventSubscriber);\n\n.. note::\n\n    The array to return in the ``getSubscribedEvents`` method is a simple array\n    with the values being the event names. The subscriber must have a method\n    that is named exactly like the event.\n\nNow when you dispatch an event, any event subscribers will be\nnotified for that event.\n\n.. code-block:: php\n\n    <?php\n    $evm->dispatchEvent(TestEvent::preFoo);\n\nNow you can test the ``$eventSubscriber`` instance to see if the\n``preFoo()`` method was invoked.\n\n.. code-block:: php\n\n    <?php\n    if ($eventSubscriber->preFooInvoked) {\n        echo 'pre foo invoked!';\n    }\n\nRegistering Event Handlers\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are two ways to set up an event handler:\n\n* For *all events* you can create a Lifecycle Event Listener or Subscriber class and register\nit by calling ``$eventManager->addEventListener()`` or ``eventManager->addEventSubscriber()``,\nsee\n:ref:`Listening and subscribing to Lifecycle Events <listening-and-subscribing-to-lifecycle-events>`\n* For *some events* (see table below), you can create a *Lifecycle Callback* method in the\nentity, see :ref:`Lifecycle Callbacks <lifecycle-callbacks>`.\n\n.. _reference-events-lifecycle-events:\n\nEvents Overview\n---------------\n\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| Event                                                            | Dispatched by         | Lifecycle | Passed                              |\n|                                                                  |                       | Callback  | Argument                            |\n+==================================================================+=======================+===========+=====================================+\n| :ref:`preRemove <reference-events-pre-remove>`                   | ``$em->remove()``     | Yes       | `PreRemoveEventArgs`_               |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`postRemove <reference-events-post-update-remove-persist>`  | ``$em->flush()``      | Yes       | `PostRemoveEventArgs`_              |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`prePersist <reference-events-pre-persist>`                 | ``$em->persist()``    | Yes       | `PrePersistEventArgs`_              |\n|                                                                  | on *initial* persist  |           |                                     |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`postPersist <reference-events-post-update-remove-persist>` | ``$em->flush()``      | Yes       | `PostPersistEventArgs`_             |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`preUpdate <reference-events-pre-update>`                   | ``$em->flush()``      | Yes       | `PreUpdateEventArgs`_               |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`postUpdate <reference-events-post-update-remove-persist>`  | ``$em->flush()``      | Yes       | `PostUpdateEventArgs`_              |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`postLoad <reference-events-post-load>`                     | Loading from database | Yes       | `PostLoadEventArgs`_                |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`loadClassMetadata <reference-events-load-class-metadata>`  | Loading of mapping    | No        | `LoadClassMetadataEventArgs`_       |\n|                                                                  | metadata              |           |                                     |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| ``onClassMetadataNotFound``                                      | ``MappingException``  | No        | `OnClassMetadataNotFoundEventArgs`_ |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`preFlush <reference-events-pre-flush>`                     | ``$em->flush()``      | Yes       | `PreFlushEventArgs`_                |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`onFlush <reference-events-on-flush>`                       | ``$em->flush()``      | No        | `OnFlushEventArgs`_                 |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`postFlush <reference-events-post-flush>`                   | ``$em->flush()``      | No        | `PostFlushEventArgs`_               |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n| :ref:`onClear <reference-events-on-clear>`                       | ``$em->clear()``      | No        | `OnClearEventArgs`_                 |\n+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+\n\n.. warning::\n\n    Making changes to entities and calling ``EntityManager::flush()`` from within\n    event handlers dispatched by ``EntityManager::flush()`` itself is strongly\n    discouraged, and might be deprecated and eventually prevented in the future.\n\n    The reason is that it causes re-entrance into ``UnitOfWork::commit()`` while a commit\n    is currently being processed. The ``UnitOfWork`` was never designed to support this,\n    and its behavior in this situation is not covered by any tests.\n\n    This may lead to entity or collection updates being missed, applied only in parts and\n    changes being lost at the end of the commit phase.\n\nNaming convention\n~~~~~~~~~~~~~~~~~\n\nEvents being used with the Doctrine ORM EventManager are best named\nwith camelcase and the value of the corresponding constant should\nbe the name of the constant itself, even with spelling. This has\nseveral reasons:\n\n\n-  It is easy to read.\n-  Simplicity.\n-  Each method within an EventSubscriber is named after the\n   corresponding constant's value. If the constant's name and value differ\n   it contradicts the intention of using the constant and makes your code\n   harder to maintain.\n\nAn example for a correct notation can be found in the example\n``TestEvent`` above.\n\n.. _lifecycle-callbacks:\n\nLifecycle Callbacks\n-------------------\n\nLifecycle Callbacks are defined on an entity class. They allow you to\ntrigger callbacks whenever an instance of that entity class experiences\na relevant lifecycle event. More than one callback can be defined for each\nlifecycle event. Lifecycle Callbacks are best used for simple operations\nspecific to a particular entity class's lifecycle.\n\n\n.. note::\n\n    Lifecycle Callbacks are not supported for :doc:`Embeddables </tutorials/embeddables>`.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\DBAL\\Types\\Types;\n        use Doctrine\\ORM\\Event\\PrePersistEventArgs;\n        use Doctrine\\ORM\\Mapping\\Entity;\n        use Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\n        use Doctrine\\ORM\\Mapping\\PrePersist;\n        use Doctrine\\ORM\\Mapping\\PreUpdate;\n\n        #[Entity]\n        #[HasLifecycleCallbacks]\n        class User\n        {\n            // ...\n\n            #[Column(type: Types::STRING, length: 255)]\n            public $value;\n\n            #[PrePersist]\n            public function doStuffOnPrePersist(PrePersistEventArgs $eventArgs)\n            {\n                $this->createdAt = date('Y-m-d H:i:s');\n            }\n\n            #[PrePersist]\n            public function doOtherStuffOnPrePersist()\n            {\n                $this->value = 'changed from prePersist callback!';\n            }\n\n            #[PreUpdate]\n            public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)\n            {\n                $this->value = 'changed from preUpdate callback!';\n            }\n        }\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n            <entity name=\"User\">\n                <!-- ... -->\n                <lifecycle-callbacks>\n                    <lifecycle-callback type=\"prePersist\" method=\"doStuffOnPrePersist\"/>\n                    <lifecycle-callback type=\"prePersist\" method=\"doOtherStuffOnPrePersist\"/>\n                    <lifecycle-callback type=\"preUpdate\" method=\"doStuffOnPreUpdate\"/>\n                </lifecycle-callbacks>\n            </entity>\n        </doctrine-mapping>\n\nLifecycle Callbacks Event Argument\n----------------------------------\n\nThe triggered event is also given to the lifecycle-callback.\n\nWith the additional argument you have access to the\n``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.\n\n.. code-block:: php\n\n    <?php\n    // ...\n\n    class User\n    {\n        public function preUpdate(PreUpdateEventArgs $event)\n        {\n            if ($event->hasChangedField('username')) {\n                // Do something when the username is changed.\n            }\n        }\n    }\n\n.. _listening-and-subscribing-to-lifecycle-events:\n\nListening and subscribing to Lifecycle Events\n---------------------------------------------\n\nLifecycle event listeners are much more powerful than the simple\nlifecycle callbacks that are defined on the entity classes. They\nsit at a level above the entities and allow you to implement re-usable\nbehaviors across different entity classes.\n\nNote that they require much more detailed knowledge about the inner\nworkings of the ``EntityManager`` and ``UnitOfWork`` classes. Please\nread the :ref:`Implementing Event Listeners <reference-events-implementing-listeners>` section\ncarefully if you are trying to write your own listener.\n\nFor event subscribers, there are no surprises. They declare the\nlifecycle events in their ``getSubscribedEvents`` method and provide\npublic methods that expect the relevant arguments.\n\nA lifecycle event listener looks like the following:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n    class MyEventListener\n    {\n        public function preUpdate(PreUpdateEventArgs $args)\n        {\n            $entity = $args->getObject();\n            $entityManager = $args->getObjectManager();\n\n            // perhaps you only want to act on some \"Product\" entity\n            if ($entity instanceof Product) {\n                // do something with the Product\n            }\n        }\n    }\n\nA lifecycle event subscriber may look like this:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PostUpdateEventArgs;\n    use Doctrine\\ORM\\Events;\n    use Doctrine\\EventSubscriber;\n\n    class MyEventSubscriber implements EventSubscriber\n    {\n        public function getSubscribedEvents()\n        {\n            return array(\n                Events::postUpdate,\n            );\n        }\n\n        public function postUpdate(PostUpdateEventArgs $args)\n        {\n            $entity = $args->getObject();\n            $entityManager = $args->getObjectManager();\n\n            // perhaps you only want to act on some \"Product\" entity\n            if ($entity instanceof Product) {\n                // do something with the Product\n            }\n        }\n\n.. note::\n\n    Lifecycle events are triggered for all entities. It is the responsibility\n    of the listeners and subscribers to check if the entity is of a type\n    it wants to handle.\n\nTo register an event listener or subscriber, you have to hook it into the\nEventManager that is passed to the EntityManager factory:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Events;\n\n    $eventManager = new EventManager();\n    $eventManager->addEventListener([Events::preUpdate], new MyEventListener());\n    $eventManager->addEventSubscriber(new MyEventSubscriber());\n\n    $entityManager = new EntityManager($connection, $config, $eventManager);\n\nYou can also retrieve the event manager instance after the\nEntityManager was created:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Events;\n\n    $entityManager->getEventManager()->addEventListener([Events::preUpdate], new MyEventListener());\n    $entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber());\n\n.. _reference-events-implementing-listeners:\n\nImplementing Event Listeners\n----------------------------\n\nThis section explains what is and what is not allowed during\nspecific lifecycle events of the ``UnitOfWork`` class. Although you get\npassed the ``EntityManager`` instance in all of these events, you have\nto follow these restrictions very carefully since operations in the\nwrong event may produce lots of different errors, such as inconsistent\ndata and lost updates/persists/removes.\n\nFor the described events that are also lifecycle callback events\nthe restrictions apply as well, with the additional restriction\nthat (prior to version 2.4) you do not have access to the\n``EntityManager`` or ``UnitOfWork`` APIs inside these events.\n\n.. _reference-events-pre-persist:\n\nprePersist\n~~~~~~~~~~\n\nThere are two ways for the ``prePersist`` event to be triggered:\n\n- One is when you call ``EntityManager::persist()``. The\n  event is also called for all :ref:`cascaded associations <transitive-persistence>`.\n- The other is inside the ``flush()`` method when changes to associations are computed and\n  this association is marked as :ref:`cascade: persist <transitive-persistence>`. Any new entity found\n  during this operation is also persisted and ``prePersist`` called\n  on it. This is called :ref:`persistence by reachability <persistence-by-reachability>`.\n\nIn both cases you get passed a ``PrePersistEventArgs`` instance\nwhich has access to the entity and the entity manager.\n\nThis event is only triggered on *initial* persist of an entity\n(i.e. it does not trigger on future updates).\n\nThe following restrictions apply to ``prePersist``:\n\n-  If you are using a PrePersist Identity Generator such as\n   sequences the ID value will *NOT* be available within any\n   PrePersist events.\n-  Doctrine will not recognize changes made to relations in a prePersist\n   event. This includes modifications to\n   collections such as additions, removals or replacement.\n\n.. _reference-events-pre-remove:\n\npreRemove\n~~~~~~~~~\n\nThe ``preRemove`` event is called on every entity immediately when it is passed\nto the ``EntityManager::remove()`` method. It is cascaded for all\nassociations that are marked as :ref:`cascade: remove <transitive-persistence>`\n\nIt is not called for a DQL ``DELETE`` statement.\n\nThere are no restrictions to what methods can be called inside the\n``preRemove`` event, except when the remove method itself was\ncalled during a flush operation.\n\n.. _reference-events-pre-flush:\n\npreFlush\n~~~~~~~~\n\n``preFlush`` is called inside ``EntityManager::flush()`` before\nanything else. ``EntityManager::flush()`` must not be called inside\nits listeners, since it would fire the ``preFlush`` event again, which would\nresult in an infinite loop.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreFlushEventArgs;\n\n    class PreFlushExampleListener\n    {\n        public function preFlush(PreFlushEventArgs $args)\n        {\n            // ...\n        }\n    }\n\n.. _reference-events-on-flush:\n\nonFlush\n~~~~~~~\n\n``onFlush`` is a very powerful event. It is called inside\n``EntityManager::flush()`` after the changes to all the managed\nentities and their associations have been computed. This means, the\n``onFlush`` event has access to the sets of:\n\n-  Entities scheduled for insert\n-  Entities scheduled for update\n-  Entities scheduled for removal\n-  Collections scheduled for update\n-  Collections scheduled for removal\n\nTo make use of the ``onFlush`` event you have to be familiar with the\ninternal :ref:`UnitOfWork <unit-of-work>` API, which grants you access to the previously\nmentioned sets. See this example:\n\n.. code-block:: php\n\n    <?php\n    class FlushExampleListener\n    {\n        public function onFlush(OnFlushEventArgs $eventArgs)\n        {\n            $em = $eventArgs->getObjectManager();\n            $uow = $em->getUnitOfWork();\n\n            foreach ($uow->getScheduledEntityInsertions() as $entity) {\n\n            }\n\n            foreach ($uow->getScheduledEntityUpdates() as $entity) {\n\n            }\n\n            foreach ($uow->getScheduledEntityDeletions() as $entity) {\n\n            }\n\n            foreach ($uow->getScheduledCollectionDeletions() as $col) {\n\n            }\n\n            foreach ($uow->getScheduledCollectionUpdates() as $col) {\n\n            }\n        }\n    }\n\nThe following restrictions apply to the ``onFlush`` event:\n\n-  If you create and persist a new entity in ``onFlush``, then\n   calling ``EntityManager::persist()`` is not enough.\n   You have to execute an additional call to\n   ``$unitOfWork->computeChangeSet($classMetadata, $entity)``.\n-  Changing primitive fields or associations requires you to\n   explicitly trigger a re-computation of the changeset of the\n   affected entity. This can be done by calling\n   ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.\n\n.. _reference-events-post-flush:\n\npostFlush\n~~~~~~~~~\n\n``postFlush`` is called at the end of ``EntityManager::flush()``.\n``EntityManager::flush()`` can **NOT** be called safely inside its listeners.\nThis event is not a lifecycle callback.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PostFlushEventArgs;\n\n    class PostFlushExampleListener\n    {\n        public function postFlush(PostFlushEventArgs $args)\n        {\n            // ...\n        }\n    }\n\n.. _reference-events-pre-update:\n\npreUpdate\n~~~~~~~~~\n\nPreUpdate is called inside the ``EntityManager::flush()`` method,\nright before an SQL ``UPDATE`` statement. This event is not\ntriggered when the computed changeset is empty, nor for a DQL\n   ``UPDATE`` statement.\n\nChanges to associations of the updated entity are never allowed in\nthis event, since Doctrine cannot guarantee to correctly handle\nreferential integrity at this point of the flush operation. This\nevent has a powerful feature however, it is executed with a\n`PreUpdateEventArgs`_ instance, which contains a reference to the\ncomputed change-set of this entity.\n\nThis means you have access to all the fields that have changed for\nthis entity with their old and new value. The following methods are\navailable on the ``PreUpdateEventArgs``:\n\n-  ``getEntity()`` to get access to the actual entity.\n-  ``getEntityChangeSet()`` to get a copy of the changeset array.\n   Changes to this returned array do not affect updating.\n-  ``hasChangedField($fieldName)`` to check if the given field name\n   of the current entity changed.\n-  ``getOldValue($fieldName)`` and ``getNewValue($fieldName)`` to\n   access the values of a field.\n-  ``setNewValue($fieldName, $value)`` to change the value of a\n   field to be updated.\n\nA simple example for this event looks like:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n    class NeverAliceOnlyBobListener\n    {\n        public function preUpdate(PreUpdateEventArgs $eventArgs)\n        {\n            if ($eventArgs->getEntity() instanceof User) {\n                if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {\n                    $eventArgs->setNewValue('name', 'Bob');\n                    // The following will only work if `status` is already present in the computed changeset.\n                    // Otherwise it will throw an InvalidArgumentException:\n                    $eventArgs->setNewValue('status', 'active');\n                }\n            }\n        }\n    }\n\nYou could also use this listener to implement validation of all the\nfields that have changed. This is more efficient than using a\nlifecycle callback when there are expensive validations to call:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n    class ValidCreditCardListener\n    {\n        public function preUpdate(PreUpdateEventArgs $eventArgs)\n        {\n            if ($eventArgs->getEntity() instanceof Account) {\n                if ($eventArgs->hasChangedField('creditCard')) {\n                    $this->validateCreditCard($eventArgs->getNewValue('creditCard'));\n                }\n            }\n        }\n\n        private function validateCreditCard($no)\n        {\n            // throw an exception to interrupt flush event. Transaction will be rolled back.\n        }\n    }\n\nRestrictions for this event:\n\n-  Changes to associations of the passed entities are not\n   recognized by the flush operation anymore.\n-  Changes to fields of the passed entities are not recognized by\n   the flush operation anymore, use the computed change-set passed to\n   the event to modify primitive field values, e.g. use\n   ``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.\n-  Any calls to ``EntityManager::persist()`` or\n   ``EntityManager::remove()``, even in combination with the ``UnitOfWork``\n   API are strongly discouraged and don't work as expected outside the\n   flush operation.\n\n.. _reference-events-post-update-remove-persist:\n\npostUpdate, postRemove, postPersist\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThese three ``post*`` events are called inside ``EntityManager::flush()``.\nChanges in here are not relevant to the persistence in the\ndatabase, but you can use these events to alter non-persistable items,\nlike non-mapped fields, logging or even associated classes that are\nnot directly mapped by Doctrine.\n\n-  The ``postUpdate`` event occurs after the database\n   update operations to entity data, but before the database transaction\n   has been committed. It is not called for a DQL ``UPDATE`` statement.\n-  The ``postPersist`` event occurs for an entity after the entity has\n   been made persistent. It will be invoked after all database insert\n   operations for new entities have been performed, but before the database\n   transaction has been committed. Generated primary key values will be\n   available for all entities at the time this event is triggered.\n-  The ``postRemove`` event occurs for an entity after the\n   entity has been deleted. It will be invoked after all database\n   delete operations for entity rows have been executed, but before the\n   database transaction has been committed. This event is not called for\n   a DQL ``DELETE`` statement.\n\n.. note::\n\n    At the time ``postPersist`` is called, there may still be collection and/or\n    \"extra\" updates pending. The database may not yet be completely in\n    sync with the entity states in memory, not even for the new entities. Similarly,\n    also at the time ``postUpdate`` and ``postRemove`` are called, in-memory collections\n    may still be in a \"dirty\" state or still contain removed entities.\n\n.. warning::\n\n    The ``postRemove`` event or any events triggered after an entity removal\n    can receive an uninitializable proxy in case you have configured an entity to\n    cascade remove relations. In this case, you should load yourself the proxy in\n    the associated ``pre*`` event.\n\n.. _reference-events-post-load:\n\npostLoad\n~~~~~~~~\n\nThe postLoad event occurs after the entity has been loaded into the current\n``EntityManager`` from the database or after ``refresh()`` has been applied to it.\n\n.. warning::\n\n    When using ``Doctrine\\ORM\\AbstractQuery::toIterable()``, ``postLoad``\n    events will be executed immediately after objects are being hydrated, and therefore\n    associations are not guaranteed to be initialized. It is not safe to combine\n    usage of ``Doctrine\\ORM\\AbstractQuery::toIterable()`` and ``postLoad`` event\n    handlers.\n\n.. _reference-events-on-clear:\n\nonClear\n~~~~~~~~\n\nThe ``onClear`` event occurs when the ``EntityManager::clear()`` operation is invoked,\nafter all references to entities have been removed from the unit of work.\nThis event is not a lifecycle callback.\n\nEntity listeners\n----------------\n\nAn entity listener is a lifecycle listener class used for an entity.\n\n- The entity listener's mapping may be applied to an entity class or mapped superclass.\n- An entity listener is defined by mapping the entity class with the corresponding mapping.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        namespace MyProject\\Entity;\n        use App\\EventListener\\UserListener;\n\n        #[Entity]\n        #[EntityListeners([UserListener::class])]\n        class User\n        {\n            // ....\n        }\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"MyProject\\Entity\\User\">\n                <entity-listeners>\n                    <entity-listener class=\"UserListener\"/>\n                </entity-listeners>\n                <!-- .... -->\n            </entity>\n        </doctrine-mapping>\n\n.. _reference-entity-listeners:\n\nEntity listeners class\n~~~~~~~~~~~~~~~~~~~~~~\n\nAn ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor.\n\n- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity\n- An entity listener method receives two arguments, the entity instance and the lifecycle event.\n- The callback method can be defined by naming convention or specifying a method mapping.\n- When a listener mapping is not given the parser will use the naming convention to look for a matching method,\n  e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event.\n- When a listener mapping is given the parser will not look for any methods using the naming convention.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n    class UserListener\n    {\n        public function preUpdate(User $user, PreUpdateEventArgs $event)\n        {\n            // Do something on pre update.\n        }\n    }\n\nTo define a specific event listener method (one that does not follow the naming convention)\nyou need to map the listener method using the event type mapping:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        use Doctrine\\ORM\\Event\\PostLoadEventArgs;\n        use Doctrine\\ORM\\Event\\PostPersistEventArgs;\n        use Doctrine\\ORM\\Event\\PostRemoveEventArgs;\n        use Doctrine\\ORM\\Event\\PostUpdateEventArgs;\n        use Doctrine\\ORM\\Event\\PreFlushEventArgs;\n        use Doctrine\\ORM\\Event\\PrePersistEventArgs;\n        use Doctrine\\ORM\\Event\\PreRemoveEventArgs;\n        use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n        class UserListener\n        {\n            #[PrePersist]\n            public function prePersistHandler(User $user, PrePersistEventArgs $event): void { // ... }\n\n            #[PostPersist]\n            public function postPersistHandler(User $user, PostPersistEventArgs $event): void { // ... }\n\n            #[PreUpdate]\n            public function preUpdateHandler(User $user, PreUpdateEventArgs $event): void { // ... }\n\n            #[PostUpdate]\n            public function postUpdateHandler(User $user, PostUpdateEventArgs $event): void { // ... }\n\n            #[PostRemove]\n            public function postRemoveHandler(User $user, PostRemoveEventArgs $event): void { // ... }\n\n            #[PreRemove]\n            public function preRemoveHandler(User $user, PreRemoveEventArgs $event): void { // ... }\n\n            #[PreFlush]\n            public function preFlushHandler(User $user, PreFlushEventArgs $event): void { // ... }\n\n            #[PostLoad]\n            public function postLoadHandler(User $user, PostLoadEventArgs $event): void { // ... }\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"MyProject\\Entity\\User\">\n                 <entity-listeners>\n                    <entity-listener class=\"UserListener\">\n                        <lifecycle-callback type=\"preFlush\"      method=\"preFlushHandler\"/>\n                        <lifecycle-callback type=\"postLoad\"      method=\"postLoadHandler\"/>\n\n                        <lifecycle-callback type=\"postPersist\"   method=\"postPersistHandler\"/>\n                        <lifecycle-callback type=\"prePersist\"    method=\"prePersistHandler\"/>\n\n                        <lifecycle-callback type=\"postUpdate\"    method=\"postUpdateHandler\"/>\n                        <lifecycle-callback type=\"preUpdate\"     method=\"preUpdateHandler\"/>\n\n                        <lifecycle-callback type=\"postRemove\"    method=\"postRemoveHandler\"/>\n                        <lifecycle-callback type=\"preRemove\"     method=\"preRemoveHandler\"/>\n                    </entity-listener>\n                </entity-listeners>\n                <!-- .... -->\n            </entity>\n        </doctrine-mapping>\n\n.. note::\n\n    The order of execution of multiple methods for the same event (e.g. multiple @PrePersist) is not guaranteed.\n\n\nEntity listeners resolver\n~~~~~~~~~~~~~~~~~~~~~~~~~\nDoctrine invokes the listener resolver to get the listener instance.\n\n- A resolver allows you register a specific entity listener instance.\n- You can also implement your own resolver by extending ``Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver`` or implementing ``Doctrine\\ORM\\Mapping\\EntityListenerResolver``\n\nSpecifying an entity listener instance :\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\PreUpdateEventArgs;\n\n    // User.php\n\n    #[Entity]\n    #[EntityListeners([\"UserListener\"])\n    class User\n    {\n        // ....\n    }\n\n    // UserListener.php\n    class UserListener\n    {\n        public function __construct(MyService $service)\n        {\n            $this->service = $service;\n        }\n\n        public function preUpdate(User $user, PreUpdateEventArgs $event)\n        {\n            $this->service->doSomething($user);\n        }\n    }\n\n    // register a entity listener.\n    $listener = $container->get('user_listener');\n    $em->getConfiguration()->getEntityListenerResolver()->register($listener);\n\nImplementing your own resolver:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver;\n\n    class MyEntityListenerResolver extends DefaultEntityListenerResolver\n    {\n        public function __construct($container)\n        {\n            $this->container = $container;\n        }\n\n        public function resolve($className)\n        {\n            // resolve the service id by the given class name;\n            $id = 'user_listener';\n\n            return $this->container->get($id);\n        }\n    }\n\n    // Configure the listener resolver only before instantiating the EntityManager\n    $configurations->setEntityListenerResolver(new MyEntityListenerResolver);\n    $entityManager = new EntityManager(.., $configurations, ..);\n\n.. _reference-events-load-class-metadata:\n\nLoad ClassMetadata Event\n------------------------\n\n``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the\nmapping metadata for a class has been loaded from a mapping source\n(attributes/xml) in to a ``Doctrine\\ORM\\Mapping\\ClassMetadata`` instance.\nYou can hook in to this process and manipulate the instance.\nThis event is not a lifecycle callback.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\n\n    $test = new TestEventListener();\n    $evm = $em->getEventManager();\n    $evm->addEventListener(Doctrine\\ORM\\Events::loadClassMetadata, $test);\n\n    class TestEventListener\n    {\n        public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)\n        {\n            $classMetadata = $eventArgs->getClassMetadata();\n            $fieldMapping = array(\n                'fieldName' => 'about',\n                'type' => 'string',\n                'length' => 255\n            );\n            $classMetadata->mapField($fieldMapping);\n        }\n    }\n\nIf not class metadata can be found, an ``onClassMetadataNotFound`` event is dispatched.\nManipulating the given event args instance\nallows providing fallback metadata even when no actual metadata exists\nor could be found. This event is not a lifecycle callback.\n\nSchemaTool Events\n-----------------\n\nIt is possible to access the schema metadata during schema changes that are happening in ``Doctrine\\ORM\\Tools\\SchemaTool``.\nThere are two different events where you can hook in.\n\npostGenerateSchemaTable\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThis event is fired for each ``Doctrine\\DBAL\\Schema\\Table`` instance, after one was created and built up with the current class metadata\nof an entity. It is possible to access to the current state of ``Doctrine\\DBAL\\Schema\\Schema``, the current table schema\ninstance and class metadata.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Tools\\ToolEvents;\n    use Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs;\n\n    $test = new TestEventListener();\n    $evm = $em->getEventManager();\n    $evm->addEventListener(ToolEvents::postGenerateSchemaTable, $test);\n\n    class TestEventListener\n    {\n        public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)\n        {\n            $classMetadata = $eventArgs->getClassMetadata();\n            $schema = $eventArgs->getSchema();\n            $table = $eventArgs->getClassTable();\n        }\n    }\n\npostGenerateSchema\n~~~~~~~~~~~~~~~~~~\n\nThis event is fired after the schema instance was successfully built and before SQL queries are generated from the\nschema information of ``Doctrine\\DBAL\\Schema\\Schema``. It allows to access the full object representation of the database schema\nand the EntityManager.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Tools\\ToolEvents;\n    use Doctrine\\ORM\\Tools\\Event\\GenerateSchemaEventArgs;\n\n    $test = new TestEventListener();\n    $evm = $em->getEventManager();\n    $evm->addEventListener(ToolEvents::postGenerateSchema, $test);\n\n    class TestEventListener\n    {\n        public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs)\n        {\n            $schema = $eventArgs->getSchema();\n            $em = $eventArgs->getEntityManager();\n        }\n    }\n\n.. _PrePersistEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PrePersistEventArgs.php\n.. _PreRemoveEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PreRemoveEventArgs.php\n.. _PreUpdateEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PreUpdateEventArgs.php\n.. _PostPersistEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PostPersistEventArgs.php\n.. _PostRemoveEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PostRemoveEventArgs.php\n.. _PostUpdateEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PostUpdateEventArgs.php\n.. _PostLoadEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PostLoadEventArgs.php\n.. _PreFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PreFlushEventArgs.php\n.. _PostFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/PostFlushEventArgs.php\n.. _OnFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/OnFlushEventArgs.php\n.. _OnClearEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/OnClearEventArgs.php\n.. _LoadClassMetadataEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/LoadClassMetadataEventArgs.php\n.. _OnClassMetadataNotFoundEventArgs: https://github.com/doctrine/orm/blob/HEAD/src/Event/OnClassMetadataNotFoundEventArgs.php\n"
  },
  {
    "path": "docs/en/reference/faq.rst",
    "content": "Frequently Asked Questions\n==========================\n\n.. note::\n\n    This FAQ is a work in progress. We will add lots of questions and not answer them right away just to remember\n    what is often asked. If you stumble across an unanswered question please write a mail to the mailing-list or\n    join the #doctrine channel on Freenode IRC.\n\nDatabase Schema\n---------------\n\nHow do I set the charset and collation for MySQL tables?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn your mapping configuration, the column definition (for example, the\n``#[Column]`` attribute) has an ``options`` parameter where you can specify\nthe ``charset`` and ``collation``. The default values are ``utf8`` and\n``utf8_unicode_ci``, respectively.\n\nMapping\n-------\n\nWhy do I get exceptions about unique constraint failures during ``$em->flush()``?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDoctrine does not check if you are re-adding entities with a primary key that already exists\nor adding entities to a collection twice. You have to check for both conditions yourself\nin the code before calling ``$em->flush()`` if you know that unique constraint failures\ncan occur.\n\nIn `Symfony2 <https://www.symfony.com>`_ for example there is a Unique Entity Validator\nto achieve this task.\n\nFor collections you can check with ``$collection->contains($entity)`` if an entity is already\npart of this collection. For a FETCH=LAZY collection this will initialize the collection,\nhowever for FETCH=EXTRA_LAZY this method will use SQL to determine if this entity is already\npart of the collection.\n\nAssociations\n------------\n\nWhat is wrong when I get an InvalidArgumentException \"A new entity was found through the relationship..\"?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis exception is thrown during ``EntityManager#flush()`` when there exists an object in the identity map\nthat contains a reference to an object that Doctrine does not know about. Say for example you grab\na \"User\"-entity from the database with a specific id and set a completely new object into one of the associations\nof the User object. If you then call ``EntityManager#flush()`` without letting Doctrine know about\nthis new object using ``EntityManager#persist($newObject)`` you will see this exception.\n\nYou can solve this exception by:\n\n* Calling ``EntityManager#persist($newObject)`` on the new object\n* Using cascade=persist on the association that contains the new object\n\nHow can I filter an association?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou should use DQL queries to query for the filtered set of entities.\n\nI call clear() on a One-To-Many collection but the entities are not deleted\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is an expected behavior that has to do with the inverse/owning side handling of Doctrine.\nBy definition a One-To-Many association is on the inverse side, that means changes to it\nwill not be recognized by Doctrine.\n\nIf you want to perform the equivalent of the clear operation you have to iterate the\ncollection and set the owning side many-to-one reference to NULL as well to detach all entities\nfrom the collection. This will trigger the appropriate UPDATE statements on the database.\n\nHow can I add columns to a many-to-many table?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe many-to-many association is only supporting foreign keys in the table definition\nTo work with many-to-many tables containing extra columns you have to use the\nforeign keys as primary keys feature of Doctrine ORM.\n\nSee :doc:`the tutorial on composite primary keys for more information <../tutorials/composite-primary-keys>`.\n\n\nHow can i paginate fetch-joined collections?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you are issuing a DQL statement that fetches a collection as well you cannot easily iterate\nover this collection using a LIMIT statement (or vendor equivalent).\n\nDoctrine does not offer a solution for this out of the box but there are several extensions\nthat do:\n\n* `DoctrineExtensions <https://github.com/beberlei/DoctrineExtensions>`_\n* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_\n\nWhy does pagination not work correctly with fetch joins?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPagination in Doctrine uses a LIMIT clause (or vendor equivalent) to restrict the results.\nHowever when fetch-joining this is not returning the correct number of results since joining\nwith a one-to-many or many-to-many association multiplies the number of rows by the number\nof associated entities.\n\nSee the previous question for a solution to this task.\n\nInheritance\n-----------\n\nCan I use Inheritance with Doctrine ORM?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYes, you can use Single- or Joined-Table Inheritance in ORM.\n\nSee the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for\nthe details.\n\nWhy does Doctrine not create proxy objects for my inheritance hierarchy?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you set a many-to-one or one-to-one association target-entity to any parent class of\nan inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.\nTo find this out it has to execute a SQL query to look this information up in the database.\n\nEntityGenerator\n---------------\n\nWhy does the EntityGenerator not do X?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation\nis not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator\nis supposed to kick-start you, but not towards 100%.\n\nWhy does the EntityGenerator not generate inheritance correctly?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nJust from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy.\nThis is why the generation of inherited entities does not fully work. You have to adjust some additional\ncode to get this one working correctly.\n\nPerformance\n-----------\n\nWhy is an extra SQL query executed every time I fetch an entity with a one-to-one relation?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf Doctrine detects that you are fetching an inverse side one-to-one association\nit has to execute an additional query to load this object, because it cannot know\nif there is no such object (setting null) or if it should set a proxy and which id this proxy has.\n\nTo solve this problem currently a query has to be executed to find out this information.\n\nDoctrine Query Language\n-----------------------\n\nWhat is DQL?\n~~~~~~~~~~~~\n\nDQL stands for Doctrine Query Language, a query language that very much looks like SQL\nbut has some important benefits when using Doctrine:\n\n-  It uses class names and fields instead of tables and columns, separating concerns between backend and your object model.\n-  It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them.\n-  It adds some functionality that is related to object management and transforms them into SQL.\n\nIt also has some drawbacks of course:\n\n-  The syntax is slightly different to SQL so you have to learn and remember the differences.\n-  To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly.\n-  For some DQL constructs subselects are used which are known to be slow in MySQL.\n\nCan I sort by a function (for example ORDER BY RAND()) in DQL?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNo, it is not supported to sort by function in DQL. If you need this functionality you should either\nuse a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow\nstarting with 1000 rows.\n\nIs it better to write DQL or to generate it with the query builder?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe purpose of the ``QueryBuilder`` is to generate DQL dynamically,\nwhich is useful when you have optional filters, conditional joins, etc.\n\nBut the ``QueryBuilder`` is not an alternative to DQL, it actually generates DQL\nqueries at runtime, which are then interpreted by Doctrine. This means that\nusing the ``QueryBuilder`` to build and run a query is actually always slower\nthan only running the corresponding DQL query.\n\nSo if you only need to generate a query and bind parameters to it,\nyou should use plain DQL, as this is a simpler and much more readable solution.\nYou should only use the ``QueryBuilder`` when you can't achieve what you want to do with a DQL query.\n\nA Query fails, how can I debug it?\n----------------------------------\n\nFirst, if you are using the QueryBuilder you can use\n``$queryBuilder->getDQL()`` to get the DQL string of this query. The\ncorresponding SQL you can get from the Query instance by calling\n``$query->getSQL()``.\n\n.. code-block:: php\n\n    <?php\n    $dql = \"SELECT u FROM User u\";\n    $query = $entityManager->createQuery($dql);\n    var_dump($query->getSQL());\n\n    $qb = $entityManager->createQueryBuilder();\n    $qb->select('u')->from('User', 'u');\n    var_dump($qb->getDQL());\n"
  },
  {
    "path": "docs/en/reference/filters.rst",
    "content": "Filters\n=======\n\nDoctrine ORM features a filter system that allows the developer to add SQL to\nthe conditional clauses of queries, regardless the place where the SQL is\ngenerated (e.g. from a DQL query, or by loading associated entities).\n\nThe filter functionality works on SQL level. Whether a SQL query is generated\nin a Persister, during lazy loading, in extra lazy collections or from DQL.\nEach time the system iterates over all the enabled filters, adding a new SQL\npart as a filter returns.\n\nBy adding SQL to the conditional clauses of queries, the filter system filters\nout rows belonging to the entities at the level of the SQL result set. This\nmeans that the filtered entities are never hydrated (which can be expensive).\n\n\nExample filter class\n--------------------\nThroughout this document the example ``MyLocaleFilter`` class will be used to\nillustrate how the filter feature works. A filter class must extend the base\n``Doctrine\\ORM\\Query\\Filter\\SQLFilter`` class and implement the ``addFilterConstraint``\nmethod. The method receives the ``ClassMetadata`` of the filtered entity and the\ntable alias of the SQL table of the entity.\n\n.. note::\n\n    In the case of joined or single table inheritance, you always get passed the ClassMetadata of the\n    inheritance root. This is necessary to avoid edge cases that would break the SQL when applying the filters.\n\nFor the filter to correctly function, the following rules must be followed. Failure to do so will lead to unexpected results from the query cache.\n  1. Parameters for the query should be set on the filter object by ``SQLFilter#setParameter()`` before the filter is used by the ORM ( i.e. do not set parameters inside ``SQLFilter#addFilterConstraint()`` function ).\n  2. The filter must be deterministic. Don't change the values based on external inputs.\n\nThe ``SQLFilter#getParameter()`` function takes care of the proper quoting of parameters.\n\n.. code-block:: php\n\n    <?php\n    namespace Example;\n    use Doctrine\\ORM\\Mapping\\ClassMetadata,\n        Doctrine\\ORM\\Query\\Filter\\SQLFilter;\n\n    class MyLocaleFilter extends SQLFilter\n    {\n        public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string\n        {\n            // Check if the entity implements the LocalAware interface\n            if (!$targetEntity->reflClass->implementsInterface('LocaleAware')) {\n                return \"\";\n            }\n\n            return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); // getParameter applies quoting automatically\n        }\n    }\n\nIf the parameter is an array and should be quoted as a list of values for an IN query\nthis is possible with the alternative ``SQLFilter#setParameterList()`` and\n``SQLFilter#getParameterList()`` functions.\n\nConfiguration\n-------------\nFilter classes are added to the configuration as following:\n\n.. code-block:: php\n\n    <?php\n    $config->addFilter(\"locale\", \"\\Doctrine\\Tests\\ORM\\Functional\\MyLocaleFilter\");\n\n\nThe ``Configuration#addFilter()`` method takes a name for the filter and the name of the\nclass responsible for the actual filtering.\n\n\nDisabling/Enabling Filters and Setting Parameters\n---------------------------------------------------\nFilters can be disabled and enabled via the ``FilterCollection`` which is\nstored in the ``EntityManager``. The ``FilterCollection#enable($name)`` method\nwill retrieve the filter object. You can set the filter parameters on that\nobject.\n\n.. code-block:: php\n\n    <?php\n    $filter = $em->getFilters()->enable(\"locale\");\n    $filter->setParameter('locale', 'en');\n\n    // Disable it\n    $filter = $em->getFilters()->disable(\"locale\");\n\n.. warning::\n    Disabling and enabling filters has no effect on managed entities. If you\n    want to refresh or reload an object after having modified a filter or the\n    FilterCollection, then you should clear the EntityManager and re-fetch your\n    entities, having the new rules for filtering applied.\n\n\nSuspending/Restoring Filters\n----------------------------\nWhen a filter is disabled, the instance is fully deleted and all the filter\nparameters previously set are lost. Then, if you enable it again, a new filter\nis created without the previous filter parameters. If you want to keep a filter\n(in order to use it later) but temporary disable it, you'll need to use the\n``FilterCollection#suspend($name)`` and ``FilterCollection#restore($name)``\nmethods instead.\n\n.. code-block:: php\n\n    <?php\n    $filter = $em->getFilters()->enable(\"locale\");\n    $filter->setParameter('locale', 'en');\n\n    // Temporary suspend the filter\n    $filter = $em->getFilters()->suspend(\"locale\");\n\n    // Do things\n\n    // Then restore it, the locale parameter will still be set\n    $filter = $em->getFilters()->restore(\"locale\");\n\n.. warning::\n    If you enable a previously disabled filter, doctrine will create a new\n    one without keeping any of the previously parameter set with\n    ``SQLFilter#setParameter()`` or ``SQLFilter#getParameterList()``. If you\n    want to restore the previously disabled filter instead, you must use the\n    ``FilterCollection#restore($name)`` method.\n"
  },
  {
    "path": "docs/en/reference/improving-performance.rst",
    "content": "Improving Performance\n=====================\n\nBytecode Cache\n--------------\n\nIt is highly recommended to make use of a bytecode cache like OPcache.\nA bytecode cache removes the need for parsing PHP code on every\nrequest and can greatly improve performance.\n\n    \"If you care about performance and don't use a bytecode\n    cache then you don't really care about performance. Please get one\n    and start using it.\"\n\n    *Stas Malyshev, Core Contributor to PHP and Zend Employee*\n\n\nMetadata and Query caches\n-------------------------\n\nAs already mentioned earlier in the chapter about configuring\nDoctrine, it is strongly discouraged to use Doctrine without a\nMetadata and Query cache.\n\nOperating Doctrine without these caches means\nDoctrine will need to load your mapping information on every single\nrequest and has to parse each DQL query on every single request.\nThis is a waste of resources.\n\nThe preferred cache adapter for metadata and query caches is a PHP file\ncache like Symfony's\n`PHP files adapter <https://symfony.com/doc/current/components/cache/adapters/php_files_adapter.html>`_.\nThis kind of cache serializes cache items and writes them to a file.\nThis allows for opcode caching to be used and provides high performance in most scenarios.\n\nSee :ref:`types-of-caches`\n\nAlternative Query Result Formats\n--------------------------------\n\nMake effective use of the available alternative query result\nformats like nested array graphs or pure scalar results, especially\nin scenarios where data is loaded for read-only purposes.\n\nRead-Only Entities\n------------------\n\nYou can mark entities as read only. For details, see :ref:`attrref_entity`\n\nThis means that the entity marked as read only is never considered for updates.\nDuring flush on the EntityManager these entities are skipped even if properties\nchanged.\n\nRead-Only allows to persist new entities of a kind and remove existing ones,\nthey are just not considered for updates.\n\nYou can also explicitly mark individual entities read only directly on the\nUnitOfWork via a call to ``markReadOnly()``:\n\n.. code-block:: php\n\n   $user = $entityManager->find(User::class, $id);\n   $entityManager->getUnitOfWork()->markReadOnly($user);\n\nOr you can set all objects that are the result of a query hydration to be\nmarked as read only with the following query hint:\n\n.. code-block:: php\n\n   $query = $entityManager->createQuery('SELECT u FROM App\\\\Entity\\\\User u');\n   $query->setHint(Query::HINT_READ_ONLY, true);\n\n   $users = $query->getResult();\n\nExtra-Lazy Collections\n----------------------\n\nIf entities hold references to large collections you will get performance and memory problems initializing them.\nTo solve this issue you can use the EXTRA_LAZY fetch-mode feature for collections. See the :doc:`tutorial <../tutorials/extra-lazy-associations>`\nfor more information on how this fetch mode works.\n\nTemporarily change fetch mode in DQL\n------------------------------------\n\nSee :ref:`dql-temporarily-change-fetch-mode`\n\n\nApply Best Practices\n--------------------\n\nA lot of the points mentioned in the Best Practices chapter will\nalso positively affect the performance of Doctrine.\n\nSee :doc:`Best Practices </reference/best-practices>`\n\nChange Tracking policies\n------------------------\n\nSee: :doc:`Change Tracking Policies <change-tracking-policies>`\n"
  },
  {
    "path": "docs/en/reference/inheritance-mapping.rst",
    "content": "Inheritance Mapping\n===================\n\nThis chapter explains the available options for mapping class\nhierarchies.\n\nMapped Superclasses\n-------------------\n\nA mapped superclass is an abstract or concrete class that provides\npersistent entity state and mapping information for its subclasses,\nbut which is not itself an entity. Typically, the purpose of such a\nmapped superclass is to define state and mapping information that\nis common to multiple entity classes.\n\nMapped superclasses, just as regular, non-mapped classes, can\nappear in the middle of an otherwise mapped inheritance hierarchy\n(through Single Table Inheritance or Class Table Inheritance). They\nare not query-able, and do not require an ``#[Id]`` property.\n\nNo database table will be created for a mapped superclass itself,\nonly for entity classes inheriting from it. That  implies that a\nmapped superclass cannot be the ``targetEntity`` in associations.\n\nIn other words, a mapped superclass can use unidirectional One-To-One\nand Many-To-One associations where it is the owning side.\nMany-To-Many associations are only possible if the mapped\nsuperclass is only used in exactly one entity at the moment. For further\nsupport of inheritance, the single or joined table inheritance features\nhave to be used.\n\n.. note::\n\n    One-To-Many associations are not generally possible on a mapped\n    superclass, since they require the \"many\" side to hold the foreign\n    key.\n\n    It is, however, possible to use the :doc:`ResolveTargetEntityListener </cookbook/resolve-target-entity-listener>`\n    to replace references to a mapped superclass with an entity class at runtime.\n    As long as there is only one entity subclass inheriting from the mapped\n    superclass and all references to the mapped superclass are resolved to that\n    entity class at runtime, the mapped superclass *can* use One-To-Many associations\n    and be named as the ``targetEntity`` on the owning sides.\n\n.. warning::\n\n    At least when using attributes or annotations to specify your mapping,\n    it *seems* as if you could inherit from a base class that is neither\n    an entity nor a mapped superclass, but has properties with mapping configuration\n    on them that would also be used in the inheriting class.\n\n    This, however, is due to how the corresponding mapping\n    drivers work and what the PHP reflection API reports for inherited fields.\n\n    Such a configuration is explicitly not supported. To give just one example,\n    it will break for ``private`` properties.\n\n.. note::\n\n    You may be tempted to use traits to mix mapped fields or relationships\n    into your entity classes to circumvent some of the limitations of\n    mapped superclasses. Before doing that, please read the section on traits\n    in the :doc:`Limitations and Known Issues </reference/limitations-and-known-issues>` chapter.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Mapping\\Column;\n    use Doctrine\\ORM\\Mapping\\JoinColumn;\n    use Doctrine\\ORM\\Mapping\\OneToOne;\n    use Doctrine\\ORM\\Mapping\\Id;\n    use Doctrine\\ORM\\Mapping\\MappedSuperclass;\n    use Doctrine\\ORM\\Mapping\\Entity;\n\n    #[MappedSuperclass]\n    class Person\n    {\n        #[Column(type: 'integer')]\n        protected int $mapped1;\n        #[Column(type: 'string')]\n        protected string $mapped2;\n        #[OneToOne(targetEntity: Toothbrush::class)]\n        #[JoinColumn(name: 'toothbrush_id', referencedColumnName: 'id')]\n        protected Toothbrush|null $toothbrush = null;\n\n        // ... more fields and methods\n    }\n\n    #[Entity]\n    class Employee extends Person\n    {\n        #[Id, Column(type: 'integer')]\n        private int|null $id = null;\n        #[Column(type: 'string')]\n        private string $name;\n\n        // ... more fields and methods\n    }\n\n    #[Entity]\n    class Toothbrush\n    {\n        #[Id, Column(type: 'integer')]\n        private int|null $id = null;\n\n        // ... more fields and methods\n    }\n\nThe DDL for the corresponding database schema would look something\nlike this (this is for SQLite):\n\n.. code-block:: sql\n\n    CREATE TABLE Employee (mapped1 INTEGER NOT NULL, mapped2 TEXT NOT NULL, id INTEGER NOT NULL, name TEXT NOT NULL, toothbrush_id INTEGER DEFAULT NULL, PRIMARY KEY(id))\n\nAs you can see from this DDL snippet, there is only a single table\nfor the entity subclass. All the mappings from the mapped\nsuperclass were inherited to the subclass as if they had been\ndefined on that class directly.\n\nEntity Inheritance\n------------------\n\nAs soon as one entity class inherits from another entity class, either\ndirectly, with a mapped superclass or other unmapped (also called\n\"transient\") classes in between, these entities form an inheritance\nhierarchy. The topmost entity class in this hierarchy is called the\nroot entity, and the hierarchy includes all entities that are\ndescendants of this root entity.\n\nOn the root entity class, ``#[InheritanceType]``,\n``#[DiscriminatorColumn]`` and ``#[DiscriminatorMap]`` must be specified.\n\n``#[InheritanceType]`` specifies one of the two available inheritance\nmapping strategies that are explained in the following sections.\n\n``#[DiscriminatorColumn]`` designates the so-called discriminator column.\nThis is an extra column in the table that keeps information about which\ntype from the hierarchy applies for a particular database row.\n\n``#[DiscriminatorMap]`` declares the possible values for the discriminator\ncolumn and maps them to class names in the hierarchy. This discriminator map\nhas to declare all non-abstract entity classes that exist in that particular\ninheritance hierarchy. That includes the root as well as any intermediate\nentity classes, given they are not abstract.\n\nThe names of the classes in the discriminator map do not need to be fully\nqualified if the classes are contained in the same namespace as the entity\nclass on which the discriminator map is applied.\n\nIf no discriminator map is provided, then the map is generated\nautomatically. The automatically generated discriminator map contains the\nlowercase short name of each class as key.\n\n.. note::\n\n    Automatically generating the discriminator map is very expensive\n    computation-wise. The mapping driver has to provide all classes\n    for which mapping configuration exists, and those have to be\n    loaded and checked against  the current inheritance hierarchy\n    to see if they are part of it. The resulting map, however, can be kept\n    in the metadata cache.\n\nPerformance impact on to-one associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere is a general performance consideration when using entity inheritance:\nIf the target-entity of a many-to-one or one-to-one association is part of\nan inheritance hierarchy, it is preferable for performance reasons that it\nbe a leaf entity (ie. have no subclasses).\n\nOtherwise, the ORM is currently unable to tell beforehand which entity class\nwill have to be used, and so no appropriate proxy instance can be created.\nThat means the referred-to entities will *always* be loaded eagerly, which\nmight even propagate to relationships of these entities (in the case of\nself-referencing associations).\n\nSingle Table Inheritance\n------------------------\n\n`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_\nis an inheritance mapping strategy where all classes of a hierarchy are\nmapped to a single database table.\n\nExample:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        namespace MyProject\\Model;\n\n        #[Entity]\n        #[InheritanceType('SINGLE_TABLE')]\n        #[DiscriminatorColumn(name: 'discr', type: 'string')]\n        #[DiscriminatorMap(['person' => Person::class, 'employee' => Employee::class])]\n        class Person\n        {\n            // ...\n        }\n\n        #[Entity]\n        class Employee extends Person\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"MyProject\\Model\\Person\" inheritance-type=\"SINGLE_TABLE\">\n            <discriminator-column name=\"discr\" type=\"string\" />\n            <discriminator-map>\n                <discriminator-mapping value=\"person\" class=\"MyProject\\Model\\Person\"/>\n                <discriminator-mapping value=\"employee\" class=\"MyProject\\Model\\Employee\"/>\n            </discriminator-map>\n          </entity>\n        </doctrine-mapping>\n\n        <doctrine-mapping>\n            <entity name=\"MyProject\\Model\\Employee\">\n            </entity>\n        </doctrine-mapping>\n\nIn this example, the ``#[DiscriminatorMap]`` specifies that in the\ndiscriminator column, a value of \"person\" identifies a row as being of type\n``Person`` and employee\" identifies a row as being of type ``Employee``.\n\nDesign-time considerations\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis mapping approach works well when the type hierarchy is fairly\nsimple and stable. Adding a new type to the hierarchy and adding\nfields to existing supertypes simply involves adding new columns to\nthe table, though in large deployments this may have an adverse\nimpact on the index and column layout inside the database.\n\nPerformance impact\n~~~~~~~~~~~~~~~~~~\n\nThis strategy is very efficient for querying across all types in\nthe hierarchy or for specific types. No table joins are required,\nonly a ``WHERE`` clause listing the type identifiers. In particular,\nrelationships involving types that employ this mapping strategy are\nvery performing.\n\nSQL Schema considerations\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor Single-Table-Inheritance to work in scenarios where you are\nusing either a legacy database schema or a self-written database\nschema you have to make sure that all columns that are not in the\nroot entity but in any of the different sub-entities has to allow\nnull values. Columns that have ``NOT NULL`` constraints have to be on\nthe root entity of the single-table inheritance hierarchy.\n\nClass Table Inheritance\n-----------------------\n\n`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_\nis an inheritance mapping strategy where each class in a hierarchy\nis mapped to several tables: its own table and the tables of all\nparent classes. The table of a child class is linked to the table\nof a parent class through a foreign key constraint.\n\nThe discriminator column is placed in the topmost table of the hierarchy,\nbecause this is the easiest way to achieve polymorphic queries with Class\nTable Inheritance.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    namespace MyProject\\Model;\n\n    #[Entity]\n    #[InheritanceType('JOINED')]\n    #[DiscriminatorColumn(name: 'discr', type: 'string')]\n    #[DiscriminatorMap(['person' => Person::class, 'employee' => Employee::class])]\n    class Person\n    {\n        // ...\n    }\n\n    #[Entity]\n    class Employee extends Person\n    {\n        // ...\n    }\n\nAs before, the ``#[DiscriminatorMap]`` specifies that in the\ndiscriminator column, a value of \"person\" identifies a row as being of type\n``Person`` and \"employee\" identifies a row as being of type ``Employee``.\n\n.. note::\n\n    When you do not use the SchemaTool to generate the\n    required SQL you should know that deleting a class table\n    inheritance makes use of the foreign key property\n    ``ON DELETE CASCADE`` in all database implementations. A failure to\n    implement this yourself will lead to dead rows in the database.\n\n\nDesign-time considerations\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIntroducing a new type to the hierarchy, at any level, simply\ninvolves interjecting a new table into the schema. Subtypes of that\ntype will automatically join with that new type at runtime.\nSimilarly, modifying any entity type in the hierarchy by adding,\nmodifying or removing fields affects only the immediate table\nmapped to that type. This mapping strategy provides the greatest\nflexibility at design time, since changes to any type are always\nlimited to that type's dedicated table.\n\nPerformance impact\n~~~~~~~~~~~~~~~~~~\n\nThis strategy inherently requires multiple JOIN operations to\nperform just about any query which can have a negative impact on\nperformance, especially with large tables and/or large hierarchies.\nWhen partial objects are allowed, either globally or on the\nspecific query, then querying for any type will not cause the\ntables of subtypes to be ``OUTER JOIN``ed which can increase\nperformance but the resulting partial objects will not fully load\nthemselves on access of any subtype fields, so accessing fields of\nsubtypes after such a query is not safe.\n\nThere is also another important performance consideration that it is *not possible*\nto query for the base entity without any ``LEFT JOIN``s to the sub-types.\n\nSQL Schema considerations\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor each entity in the Class-Table Inheritance hierarchy all the\nmapped fields have to be columns on the table of this entity.\nAdditionally each child table has to have an id column that matches\nthe id column definition on the root table (except for any sequence\nor auto-increment details). Furthermore each child table has to\nhave a foreign key pointing from the id column to the root table id\ncolumn and cascading on delete.\n\n.. _inheritence_mapping_overrides:\n\nOverrides\n---------\n\nOverrides can only be applied to entities that extend a mapped superclass or\nuse traits. They are used to override a mapping for an entity field or\nrelationship defined in that mapped superclass or trait.\n\nIt is not supported to use overrides in entity inheritance scenarios.\n\n.. note::\n\n    When using traits, make sure not to miss the warnings given in the\n    :doc:`Limitations and Known Issues </reference/limitations-and-known-issues>` chapter.\n\n\nAssociation Override\n~~~~~~~~~~~~~~~~~~~~\nOverride a mapping for an entity relationship.\n\nCould be used by an entity that extends a mapped superclass\nto override a relationship mapping defined by the mapped superclass.\n\nExample:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        // user mapping\n        namespace MyProject\\Model;\n\n        #[MappedSuperclass]\n        class User\n        {\n            // other fields mapping\n\n            /** @var Collection<int, Group> */\n            #[JoinTable(name: 'users_groups')]\n            #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n            #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n            #[ManyToMany(targetEntity: 'Group', inversedBy: 'users')]\n            protected Collection $groups;\n\n            #[ManyToOne(targetEntity: 'Address')]\n            #[JoinColumn(name: 'address_id', referencedColumnName: 'id')]\n            protected Address|null $address = null;\n        }\n\n        // admin mapping\n        namespace MyProject\\Model;\n\n        #[Entity]\n        #[AssociationOverrides([\n            new AssociationOverride(\n                name: 'groups',\n                joinTable: new JoinTable(\n                    name: 'users_admingroups',\n                ),\n                joinColumns: [new JoinColumn(name: 'adminuser_id')],\n                inverseJoinColumns: [new JoinColumn(name: 'admingroup_id')]\n            ),\n            new AssociationOverride(\n                name: 'address',\n                joinColumns: [new JoinColumn(name: 'adminaddress_id', referencedColumnName: 'id')]\n            )\n        ])]\n        class Admin extends User\n        {\n        }\n\n    .. code-block:: xml\n\n        <!-- user mapping -->\n        <doctrine-mapping>\n          <mapped-superclass name=\"MyProject\\Model\\User\">\n                <!-- other fields mapping -->\n                <many-to-many field=\"groups\" target-entity=\"Group\" inversed-by=\"users\">\n                    <cascade>\n                        <cascade-persist/>\n                        <cascade-detach/>\n                    </cascade>\n                    <join-table name=\"users_groups\">\n                        <join-columns>\n                            <join-column name=\"user_id\" referenced-column-name=\"id\" />\n                        </join-columns>\n                        <inverse-join-columns>\n                            <join-column name=\"group_id\" referenced-column-name=\"id\" />\n                        </inverse-join-columns>\n                    </join-table>\n                </many-to-many>\n            </mapped-superclass>\n        </doctrine-mapping>\n\n        <!-- admin mapping -->\n        <doctrine-mapping>\n            <entity name=\"MyProject\\Model\\Admin\">\n                <association-overrides>\n                    <association-override name=\"groups\">\n                        <join-table name=\"users_admingroups\">\n                            <join-columns>\n                                <join-column name=\"adminuser_id\"/>\n                            </join-columns>\n                            <inverse-join-columns>\n                                <join-column name=\"admingroup_id\"/>\n                            </inverse-join-columns>\n                        </join-table>\n                    </association-override>\n                    <association-override name=\"address\">\n                        <join-columns>\n                            <join-column name=\"adminaddress_id\" referenced-column-name=\"id\"/>\n                        </join-columns>\n                    </association-override>\n                </association-overrides>\n            </entity>\n        </doctrine-mapping>\n\nThings to note:\n\n-  The \"association override\" specifies the overrides based on the property\n name.\n-  This feature is available for all kind of associations (OneToOne, OneToMany, ManyToOne, ManyToMany).\n-  The association type *cannot* be changed.\n-  The override could redefine the ``joinTables`` or ``joinColumns`` depending on the association type.\n-  The override could redefine ``inversedBy`` to reference more than one extended entity.\n-  The override could redefine fetch to modify the fetch strategy of the extended entity.\n\nAttribute Override\n~~~~~~~~~~~~~~~~~~~~\nOverride the mapping of a field.\n\nCould be used by an entity that extends a mapped superclass to override a field mapping defined by the mapped superclass.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        // user mapping\n        namespace MyProject\\Model;\n\n        #[MappedSuperclass]\n        class User\n        {\n            #[Id, GeneratedValue, Column(type: 'integer', name: 'user_id', length: 150)]\n            protected int|null $id = null;\n\n            #[Column(name: 'user_name', nullable: true, unique: false, length: 250)]\n            protected string $name;\n\n            // other fields mapping\n        }\n\n        // guest mapping\n        namespace MyProject\\Model;\n        #[Entity]\n        #[AttributeOverrides([\n            new AttributeOverride(\n                name: 'id',\n                column: new Column(\n                    name: 'guest_id',\n                    type: 'integer',\n                    length: 140\n                )\n            ),\n            new AttributeOverride(\n                name: 'name',\n                column: new Column(\n                    name: 'guest_name',\n                    nullable: false,\n                    unique: true,\n                    length: 240\n                )\n            )\n        ])]\n        class Guest extends User\n        {\n        }\n\n    .. code-block:: xml\n\n        <!-- user mapping -->\n        <doctrine-mapping>\n          <mapped-superclass name=\"MyProject\\Model\\User\">\n                <id name=\"id\" type=\"integer\" column=\"user_id\" length=\"150\">\n                    <generator strategy=\"AUTO\"/>\n                </id>\n                <field name=\"name\" column=\"user_name\" type=\"string\" length=\"250\" nullable=\"true\" unique=\"false\" />\n                <many-to-one field=\"address\" target-entity=\"Address\">\n                    <cascade>\n                        <cascade-persist/>\n                    </cascade>\n                    <join-column name=\"address_id\" referenced-column-name=\"id\"/>\n                </many-to-one>\n                <!-- other fields mapping -->\n            </mapped-superclass>\n        </doctrine-mapping>\n\n        <!-- admin mapping -->\n        <doctrine-mapping>\n            <entity name=\"MyProject\\Model\\Guest\">\n                <attribute-overrides>\n                    <attribute-override name=\"id\">\n                        <field column=\"guest_id\" length=\"140\"/>\n                    </attribute-override>\n                    <attribute-override name=\"name\">\n                        <field column=\"guest_name\" type=\"string\" length=\"240\" nullable=\"false\" unique=\"true\" />\n                    </attribute-override>\n                </attribute-overrides>\n            </entity>\n        </doctrine-mapping>\n\nThings to note:\n\n-  The \"attribute override\" specifies the overrides based on the property name.\n-  The column type *cannot* be changed. If the column type is not equal, you get a ``MappingException``.\n-  The override can redefine all the attributes except the type.\n\nQuery the Type\n--------------\n\nIt may happen that the entities of a special type should be queried. Because there\nis no direct access to the discriminator column, Doctrine provides the\n``INSTANCE OF`` construct.\n\nThe following example shows how to use ``INSTANCE OF``. There is a three level hierarchy\nwith a base entity ``NaturalPerson`` which is extended by ``Staff`` which in turn\nis extended by ``Technician``.\n\nQuerying for the staffs without getting any technicians can be achieved by this DQL:\n\n.. code-block:: php\n\n    <?php\n    $query = $em->createQuery(\"SELECT staff FROM MyProject\\Model\\Staff staff WHERE staff NOT INSTANCE OF MyProject\\Model\\Technician\");\n    $staffs = $query->getResult();\n"
  },
  {
    "path": "docs/en/reference/installation.rst",
    "content": ":orphan:\n\nInstallation\n============\n\nThe installation chapter has moved to :doc:`Installation and Configuration </reference/configuration>`.\n"
  },
  {
    "path": "docs/en/reference/limitations-and-known-issues.rst",
    "content": "Limitations and Known Issues\n============================\n\nWe try to make using Doctrine ORM a very pleasant experience.\nTherefore we think it is very important to be honest about the\ncurrent limitations to our users. Much like every other piece of\nsoftware the ORM is not perfect and far from feature complete.\nThis section should give you an overview of current limitations of\nDoctrine ORM as well as critical known issues that you should know\nabout.\n\nCurrent Limitations\n-------------------\n\nThere is a set of limitations that exist currently which might be\nsolved in the future. Any of this limitations now stated has at\nleast one ticket in the Tracker and is discussed for future\nreleases.\n\nJoin-Columns with non-primary keys\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt is not possible to use join columns pointing to non-primary keys. Doctrine will think these are the primary\nkeys and create lazy-loading proxies with the data, which can lead to unexpected results. Doctrine can for performance\nreasons not validate the correctness of this settings at runtime but only through the Validate Schema command.\n\nMapping Arrays to a Join Table\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRelated to the previous limitation with \"Foreign Keys as\nIdentifier\" you might be interested in mapping the same table\nstructure as given above to an array. However this is not yet\npossible either. See the following example:\n\n.. code-block:: sql\n\n    CREATE TABLE product (\n        id INTEGER,\n        name VARCHAR,\n        PRIMARY KEY(id)\n    );\n    \n    CREATE TABLE product_attributes (\n        product_id INTEGER,\n        attribute_name VARCHAR,\n        attribute_value VARCHAR,\n        PRIMARY KEY (product_id, attribute_name)\n    );\n\nThis schema should be mapped to a Product Entity as follows:\n\n.. code-block:: php\n\n    class Product\n    {\n        private $id;\n        private $name;\n        private $attributes = array();\n    }\n\nWhere the ``attribute_name`` column contains the key and\n``attribute_value`` contains the value of each array element in\n``$attributes``.\n\nThe feature request for persistence of primitive value arrays\n`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.\n\nCustom Persisters\n~~~~~~~~~~~~~~~~~\n\nA Persister in Doctrine is an object that is responsible for the\nhydration and write operations of an entity against the database.\nCurrently there is no way to overwrite the persister implementation\nfor a given entity, however there are several use-cases that can\nbenefit from custom persister implementations:\n\n-  `Add Upsert Support <https://github.com/doctrine/orm/issues/5178>`_\n-  `Evaluate possible ways in which stored-procedures can be used <https://github.com/doctrine/orm/issues/4946>`_\n\nPersist Keys of Collections\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPHP Arrays are ordered hash-maps and so should be the\n``Doctrine\\Common\\Collections\\Collection`` interface. We plan to\nevaluate a feature that optionally persists and hydrates the keys\nof a Collection instance.\n\n`Ticket DDC-213 <https://github.com/doctrine/orm/issues/2817>`_\n\nMapping many tables to one entity\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt is not possible to map several equally looking tables onto one\nentity. For example if you have a production and an archive table\nof a certain business concept then you cannot have both tables map\nto the same entity.\n\nBehaviors\n~~~~~~~~~\n\nDoctrine ORM will **never** include a behavior system like Doctrine 1\nin the core library. We don't think behaviors add more value than\nthey cost pain and debugging hell. Please see the many different\nblog posts we have written on this topics:\n\n-  `Doctrine2 \"Behaviors\" in a Nutshell <https://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_\n-  `A re-usable Versionable behavior for Doctrine2 <https://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_\n-  `Write your own ORM on top of Doctrine2 <https://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_\n-  `Doctrine ORM Behavioral Extensions <https://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_\n\nDoctrine ORM has enough hooks and extension points so that **you** can\nadd whatever you want on top of it. None of this will ever become\ncore functionality of Doctrine2 however, you will have to rely on\nthird party extensions for magical behaviors.\n\nNested Set\n~~~~~~~~~~\n\nNestedSet was offered as a behavior in Doctrine 1 and will not be\nincluded in the core of Doctrine ORM. However there are already two\nextensions out there that offer support for Nested Set with\nORM:\n\n-  `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_\n-  `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_\n\nUsing Traits in Entity Classes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe use of traits in entity or mapped superclasses, at least when they\ninclude mapping configuration or mapped fields, is currently not\nendorsed by the Doctrine project. The reasons for this are as follows.\n\nTraits were added in PHP 5.4 more than 10 years ago, but at the same time\nmore than two years after the initial Doctrine 2 release and the time where\ncore components were designed.\n\nIn fact, this documentation mentions traits only in the context of\n:doc:`overriding field association mappings in subclasses </tutorials/override-field-association-mappings-in-subclasses>`.\nCoverage of traits in test cases is practically nonexistent.\n\nThus, you should at least be aware that when using traits in your entity and\nmapped superclasses, you will be in uncharted terrain.\n\n.. warning::\n\n    There be dragons.\n\nFrom a more technical point of view, traits basically work at the language level\nas if the code contained in them had been copied into the class where the trait\nis used, and even private fields are accessible by the using class. In addition to\nthat, some precedence and conflict resolution rules apply.\n\nWhen it comes to loading mapping configuration, the annotation and attribute drivers\nrely on PHP reflection to inspect class properties including their docblocks.\nAs long as the results are consistent with what a solution *without* traits would\nhave produced, this is probably fine.\n\nHowever, to mention known limitations, it is currently not possible to use \"class\"\nlevel `annotations <https://github.com/doctrine/orm/pull/1517>`_ or\n`attributes <https://github.com/doctrine/orm/issues/8868>`_ on traits, and attempts to\nimprove parser support for traits as `here <https://github.com/doctrine/annotations/pull/102>`_\nor `there <https://github.com/doctrine/annotations/pull/63>`_ have been abandoned\ndue to complexity.\n\nXML mapping configuration probably needs to completely re-configure or otherwise\ncopy-and-paste configuration for fields used from traits.\n\nMapping multiple private fields of the same name\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen two classes, say a mapped superclass and an entity inheriting from it,\nboth contain a ``private`` field of the same name, this will lead to a ``MappingException``.\n\nSince the fields are ``private``, both are technically separate and can contain\ndifferent values at the same time. However, the ``ClassMetadata`` configuration used\ninternally by the ORM currently refers to fields by their name only, without taking the\nclass containing the field into consideration. This makes it impossible to keep separate\nmapping configuration for both fields.\n\nApart from that, in the case of having multiple ``private`` fields of the same name within\nthe class hierarchy an entity or mapped superclass, the Collection filtering API cannot determine\nthe right field to look at. Even if only one of these fields is actually mapped, the ``ArrayCollection``\nwill not be able to tell, since it does not have access to any metadata.\n\nThus, to avoid problems in this regard, it is best to avoid having multiple ``private`` fields of the\nsame name in class hierarchies containing entity and mapped superclasses.\n\nKnown Issues\n------------\n\nThe Known Issues section describes critical/blocker bugs and other\nissues that are either complicated to fix, not fixable due to\nbackwards compatibility issues or where no simple fix exists (yet).\nWe don't plan to add every bug in the tracker there, just those\nissues that can potentially cause nightmares or pain of any sort.\n\nSee bugs, improvement and feature requests on `Github issues <https://github.com/doctrine/orm/issues>`_.\n\nIdentifier Quoting and Legacy Databases\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor compatibility reasons between all the supported vendors and\nedge case problems Doctrine ORM does **NOT** do automatic identifier\nquoting. This can lead to problems when trying to get\nlegacy-databases to work with Doctrine ORM.\n\n\n-  You can quote column-names as described in the\n   :doc:`Basic-Mapping <basic-mapping>` section.\n-  You cannot quote join column names.\n-  You cannot use non [a-zA-Z0-9\\_]+ characters, they will break\n   several SQL statements.\n\nHaving problems with these kind of column names? Many databases\nsupport all CRUD operations on views that semantically map to\ncertain tables. You can create views for all your problematic\ntables and column names to avoid the legacy quoting nightmare.\n\nMicrosoft SQL Server and Doctrine \"datetime\"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDoctrine assumes that you use ``DateTime2`` data-types. If your legacy database contains DateTime\ndatatypes then you have to add your own data-type (see Basic Mapping for an example).\n\nMySQL with MyISAM tables\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nDoctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one\nof the tables involved uses the storage engine MyISAM. You must use InnoDB or\nother storage engines that support transactions if you need integrity.\n"
  },
  {
    "path": "docs/en/reference/metadata-drivers.rst",
    "content": "Metadata Drivers\n================\n\nThe heart of an object relational mapper is the mapping information\nthat glues everything together. It instructs the EntityManager how\nit should behave when dealing with the different entities.\n\nCore Metadata Drivers\n---------------------\n\nDoctrine provides a few different ways for you to specify your\nmetadata:\n\n\n-  **XML files** (XmlDriver)\n-  **Attributes** (AttributeDriver)\n-  **PHP Code in files or static functions** (PhpDriver)\n\nSomething important to note about the above drivers is they are all\nan intermediate step to the same end result. The mapping\ninformation is populated to ``Doctrine\\ORM\\Mapping\\ClassMetadata``\ninstances. So in the end, Doctrine only ever has to work with the\nAPI of the ``ClassMetadata`` class to get mapping information for\nan entity.\n\n.. note::\n\n    The populated ``ClassMetadata`` instances are also cached\n    so in a production environment the parsing and populating only ever\n    happens once. You can configure the metadata cache implementation\n    using the ``setMetadataCacheImpl()`` method on the\n    ``Doctrine\\ORM\\Configuration`` class:\n\n    .. code-block:: php\n\n        <?php\n        $em->getConfiguration()->setMetadataCacheImpl(new ApcuCache());\n\n\nAll the drivers are in the ``Doctrine\\ORM\\Mapping\\Driver`` namespace:\n\n.. code-block:: php\n\n    <?php\n    $driver = new \\Doctrine\\ORM\\Mapping\\Driver\\XmlDriver('/path/to/mapping/files');\n    $em->getConfiguration()->setMetadataDriverImpl($driver);\n\nImplementing Metadata Drivers\n-----------------------------\n\nIn addition to the included metadata drivers you can very easily\nimplement your own. All you need to do is define a class which\nimplements the ``MappingDriver`` interface:\n\n.. code-block:: php\n\n    <?php\n\n    declare(strict_types=1);\n\n    namespace Doctrine\\Persistence\\Mapping\\Driver;\n\n    use Doctrine\\Persistence\\Mapping\\ClassMetadata;\n\n    /**\n     * Contract for metadata drivers.\n     */\n    interface MappingDriver\n    {\n        /**\n         * Loads the metadata for the specified class into the provided container.\n         *\n         * @param class-string<T> $className\n         * @param ClassMetadata<T> $metadata\n         *\n         * @return void\n         *\n         * @template T of object\n         */\n        public function loadMetadataForClass(string $className, ClassMetadata $metadata);\n\n        /**\n         * Gets the names of all mapped classes known to this driver.\n         *\n         * @return list<class-string> The names of all mapped classes known to this driver.\n         */\n        public function getAllClassNames();\n\n        /**\n         * Returns whether the class with the specified name should have its metadata loaded.\n         * This is only the case if it is either mapped as an Entity or a MappedSuperclass.\n         *\n         * @param class-string $className\n         *\n         * @return bool\n         */\n        public function isTransient(string $className);\n    }\n\nIf you want to write a metadata driver to parse information from\nsome file format we've made your life a little easier by providing\nthe ``FileDriver`` implementation for you to extend from:\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\Persistence\\Mapping\\ClassMetadata;\n    use Doctrine\\Persistence\\Mapping\\Driver\\FileDriver;\n\n    class MyMetadataDriver extends FileDriver\n    {\n        /**\n         * {@inheritDoc}\n         */\n        protected $_fileExtension = '.dcm.ext';\n\n        /**\n         * {@inheritDoc}\n         */\n        public function loadMetadataForClass($className, ClassMetadata $metadata)\n        {\n            $data = $this->_loadMappingFile($file);\n\n            // populate ClassMetadata instance from $data\n        }\n\n        /**\n         * {@inheritDoc}\n         */\n        protected function _loadMappingFile($file)\n        {\n            // parse contents of $file and return php data structure\n        }\n    }\n\n.. note::\n\n    When using the ``FileDriver`` it requires that you only have one\n    entity defined per file and the file named after the class described\n    inside where namespace separators are replaced by periods. So if you\n    have an entity named ``Entities\\User`` and you wanted to write a\n    mapping file for your driver above you would need to name the file\n    ``Entities.User.dcm.ext`` for it to be recognized.\n\n\nNow you can use your ``MyMetadataDriver`` implementation by setting\nit with the ``setMetadataDriverImpl()`` method:\n\n.. code-block:: php\n\n    <?php\n    $driver = new MyMetadataDriver('/path/to/mapping/files');\n    $em->getConfiguration()->setMetadataDriverImpl($driver);\n\nClassMetadata\n-------------\n\nThe last piece you need to know and understand about metadata in\nDoctrine ORM is the API of the ``ClassMetadata`` classes. You need to\nbe familiar with them in order to implement your own drivers but\nmore importantly to retrieve mapping information for a certain\nentity when needed.\n\nYou have all the methods you need to manually specify the mapping\ninformation instead of using some mapping file to populate it from.\n\nYou can read more about the API of the ``ClassMetadata`` classes in\nthe PHP Mapping chapter.\n\nGetting ClassMetadata Instances\n-------------------------------\n\nIf you want to get the ``ClassMetadata`` instance for an entity in\nyour project to programmatically use some mapping information to\ngenerate some HTML or something similar you can retrieve it through\nthe ``ClassMetadataFactory``:\n\n.. code-block:: php\n\n    <?php\n    $cmf = $em->getMetadataFactory();\n    $class = $cmf->getMetadataFor('MyEntityName');\n\nNow you can learn about the entity and use the data stored in the\n``ClassMetadata`` instance to get all mapped fields for example and\niterate over them:\n\n.. code-block:: php\n\n    <?php\n    foreach ($class->fieldMappings as $fieldMapping) {\n        echo $fieldMapping['fieldName'] . \"\\n\";\n    }\n"
  },
  {
    "path": "docs/en/reference/namingstrategy.rst",
    "content": "Implementing a NamingStrategy\n==============================\n\nUsing a naming strategy you can provide rules for generating database identifiers,\ncolumn or table names. This feature helps\nreduce the verbosity of the mapping document, eliminating repetitive noise (eg: ``TABLE_``).\n\n.. warning\n\n    The naming strategy is always overridden by entity mapping such as the `Table` attribute.\n\nConfiguring a naming strategy\n-----------------------------\nThe default strategy used by Doctrine is quite minimal.\n\nBy default the ``Doctrine\\ORM\\Mapping\\DefaultNamingStrategy``\nuses the simple class name and the attribute names to generate tables and columns.\n\nYou can specify a different strategy by calling ``Doctrine\\ORM\\Configuration#setNamingStrategy()``:\n\n.. code-block:: php\n\n    <?php\n    $namingStrategy = new MyNamingStrategy();\n    $configuration->setNamingStrategy($namingStrategy);\n\nUnderscore naming strategy\n---------------------------\n\n``\\Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy`` is a built-in strategy.\n\n.. code-block:: php\n\n    <?php\n    $namingStrategy = new \\Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy(CASE_UPPER);\n    $configuration->setNamingStrategy($namingStrategy);\n\nFor SomeEntityName the strategy will generate the table SOME_ENTITY_NAME with the\n``CASE_UPPER`` option, or some_entity_name with the ``CASE_LOWER`` option.\n\nNaming strategy interface\n-------------------------\nThe interface ``Doctrine\\ORM\\Mapping\\NamingStrategy`` allows you to specify\na naming strategy for database tables and columns.\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Return a table name for an entity class\n     *\n     * @param string $className The fully-qualified class name\n     * @return string A table name\n     */\n    function classToTableName($className);\n\n    /**\n     * Return a column name for a property\n     *\n     * @param string $propertyName A property\n     * @return string A column name\n     */\n    function propertyToColumnName($propertyName);\n\n    /**\n     * Return the default reference column name\n     *\n     * @return string A column name\n     */\n    function referenceColumnName();\n\n    /**\n     * Return a join column name for a property\n     *\n     * @param string $propertyName A property\n     * @return string A join column name\n     */\n    function joinColumnName($propertyName, $className = null);\n\n    /**\n     * Return a join table name\n     *\n     * @param string $sourceEntity The source entity\n     * @param string $targetEntity The target entity\n     * @param string $propertyName A property\n     * @return string A join table name\n     */\n    function joinTableName($sourceEntity, $targetEntity, $propertyName = null);\n\n    /**\n     * Return the foreign key column name for the given parameters\n     *\n     * @param string $entityName A entity\n     * @param string $referencedColumnName A property\n     * @return string A join column name\n     */\n    function joinKeyColumnName($entityName, $referencedColumnName = null);\n\nImplementing a naming strategy\n-------------------------------\nIf you have database naming standards, like all table names should be prefixed\nby the application prefix, all column names should be lower case, you can easily\nachieve such standards by implementing a naming strategy.\n\nYou need to create a class which implements ``Doctrine\\ORM\\Mapping\\NamingStrategy``.\n\n\n.. code-block:: php\n\n    <?php\n    class MyAppNamingStrategy implements NamingStrategy\n    {\n        public function classToTableName(string $className): string\n        {\n            return 'MyApp_' . substr($className, strrpos($className, '\\\\') + 1);\n        }\n        public function propertyToColumnName(string $propertyName): string\n        {\n            return $propertyName;\n        }\n        public function referenceColumnName(): string\n        {\n            return 'id';\n        }\n        public function joinColumnName(string $propertyName, ?string $className = null): string\n        {\n            return $propertyName . '_' . $this->referenceColumnName();\n        }\n        public function joinTableName(string $sourceEntity, string $targetEntity, string $propertyName): string\n        {\n            return strtolower($this->classToTableName($sourceEntity) . '_' .\n                    $this->classToTableName($targetEntity));\n        }\n        public function joinKeyColumnName(string $entityName, ?string $referencedColumnName): string\n        {\n            return strtolower($this->classToTableName($entityName) . '_' .\n                    ($referencedColumnName ?: $this->referenceColumnName()));\n        }\n    }\n"
  },
  {
    "path": "docs/en/reference/native-sql.rst",
    "content": "Native SQL\n==========\n\nWith ``NativeQuery`` you can execute native SELECT SQL statements\nand map the results to Doctrine entities or any other result format\nsupported by Doctrine.\n\nIn order to make this mapping possible, you need to describe\nto Doctrine what columns in the result map to which entity property.\nThis description is represented by a ``ResultSetMapping`` object.\n\nWith this feature you can map arbitrary SQL code to objects, such as highly\nvendor-optimized SQL or stored-procedures.\n\nWriting ``ResultSetMapping`` from scratch is complex, but there is a convenience\nwrapper around it called a ``ResultSetMappingBuilder``. It can generate\nthe mappings for you based on Entities and even generates the ``SELECT``\nclause based on this information for you.\n\n.. note::\n\n    If you want to execute DELETE, UPDATE or INSERT statements\n    the Native SQL API cannot be used and will probably throw errors.\n    Use ``EntityManager#getConnection()`` to access the native database\n    connection and call the ``executeUpdate()`` method for these\n    queries.\n\nThe NativeQuery class\n---------------------\n\nTo create a ``NativeQuery`` you use the method\n``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in\nthe signature of this method, it expects 2 ingredients: The SQL you want to\nexecute and the ``ResultSetMapping`` that describes how the results will be\nmapped.\n\nOnce you obtained an instance of a ``NativeQuery``, you can bind parameters to\nit with the same API that ``Query`` has and execute it.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Query\\ResultSetMapping;\n\n    $rsm = new ResultSetMapping();\n    // build rsm here\n\n    $query = $entityManager->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);\n    $query->setParameter(1, 'romanb');\n\n    $users = $query->getResult();\n\nResultSetMappingBuilder\n-----------------------\n\nAn easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object.\nThis has several benefits:\n\n- The builder takes care of automatically updating your ``ResultSetMapping``\n  when the fields or associations change on the metadata of an entity.\n- You can generate the required ``SELECT`` expression for a builder\n  by converting it to a string.\n- The API is much simpler than the usual ``ResultSetMapping`` API.\n\nOne downside is that the builder API does not yet support entities\nwith inheritance hierarchies.\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\n\n    $sql = \"SELECT u.id, u.name, a.id AS address_id, a.street, a.city \" .\n           \"FROM users u INNER JOIN address a ON u.address_id = a.id\";\n\n    $rsm = new ResultSetMappingBuilder($entityManager);\n    $rsm->addRootEntityFromClassMetadata('MyProject\\User', 'u');\n    $rsm->addJoinedEntityFromClassMetadata('MyProject\\Address', 'a', 'u', 'address', array('id' => 'address_id'));\n\nThe builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.\n\nThe ``SELECT`` clause can be generated\nfrom a ``ResultSetMappingBuilder``. You can either cast the builder\nobject to ``(string)`` and the DQL aliases are used as SQL table aliases\nor use the ``generateSelectClause($tableAliases)`` method and pass\na mapping from DQL alias (key) to SQL alias (value)\n\n.. code-block:: php\n\n    <?php\n\n    $selectClause = $rsm->generateSelectClause(array(\n        'u' => 't1',\n        'g' => 't2'\n    ));\n    $sql = \"SELECT \" . $selectClause . \" FROM users t1 JOIN groups t2 ON t1.group_id = t2.id\";\n\n\nThe ResultSetMapping\n--------------------\n\nUnderstanding the ``ResultSetMapping`` is the key to using a\n``NativeQuery``. A Doctrine result can contain the following\ncomponents:\n\n\n-  Entity results. These represent root result elements.\n-  Joined entity results. These represent joined entities in\n   associations of root entity results.\n-  Field results. These represent a column in the result set that\n   maps to a field of an entity. A field result always belongs to an\n   entity result or joined entity result.\n-  Scalar results. These represent scalar values in the result set\n   that will appear in each result row. Adding scalar results to a\n   ResultSetMapping can also cause the overall result to become\n   **mixed** (see DQL - Doctrine Query Language) if the same\n   ResultSetMapping also contains entity results.\n-  Meta results. These represent columns that contain\n   meta-information, such as foreign keys and discriminator columns.\n   When querying for objects (``getResult()``), all meta columns of\n   root entities or joined entities must be present in the SQL query\n   and mapped accordingly using ``ResultSetMapping#addMetaResult``.\n\n.. note::\n\n    It might not surprise you that Doctrine uses\n    ``ResultSetMapping`` internally when you create DQL queries. As\n    the query gets parsed and transformed to SQL, Doctrine fills a\n    ``ResultSetMapping`` that describes how the results should be\n    processed by the hydration routines.\n\n\nWe will now look at each of the result types that can appear in a\nResultSetMapping in detail.\n\nEntity results\n~~~~~~~~~~~~~~\n\nAn entity result describes an entity type that appears as a root\nelement in the transformed result. You add an entity result through\n``ResultSetMapping#addEntityResult()``. Let's take a look at the\nmethod signature in detail:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Adds an entity result to this ResultSetMapping.\n     *\n     * @param string $class The class name of the entity.\n     * @param string $alias The alias for the class. The alias must be unique among all entity\n     *                      results or joined entity results within this ResultSetMapping.\n     */\n    public function addEntityResult($class, $alias)\n\nThe first parameter is the fully qualified name of the entity\nclass. The second parameter is some arbitrary alias for this entity\nresult that must be unique within a ``ResultSetMapping``. You use\nthis alias to attach field results to the entity result. It is very\nsimilar to an identification variable that you use in DQL to alias\nclasses or relationships.\n\nAn entity result alone is not enough to form a valid\n``ResultSetMapping``. An entity result or joined entity result\nalways needs a set of field results, which we will look at soon.\n\nJoined entity results\n~~~~~~~~~~~~~~~~~~~~~\n\nA joined entity result describes an entity type that appears as a\njoined relationship element in the transformed result, attached to\na (root) entity result. You add a joined entity result through\n``ResultSetMapping#addJoinedEntityResult()``. Let's take a look at\nthe method signature in detail:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Adds a joined entity result.\n     *\n     * @param string $class The class name of the joined entity.\n     * @param string $alias The unique alias to use for the joined entity.\n     * @param string $parentAlias The alias of the entity result that is the parent of this joined result.\n     * @param object $relation The association field that connects the parent entity result with the joined entity result.\n     */\n    public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)\n\nThe first parameter is the class name of the joined entity. The\nsecond parameter is an arbitrary alias for the joined entity that\nmust be unique within the ``ResultSetMapping``. You use this alias\nto attach field results to the entity result. The third parameter\nis the alias of the entity result that is the parent type of the\njoined relationship. The fourth and last parameter is the name of\nthe field on the parent entity result that should contain the\njoined entity result.\n\nField results\n~~~~~~~~~~~~~\n\nA field result describes the mapping of a single column in a SQL\nresult set to a field in an entity. As such, field results are\ninherently bound to entity results. You add a field result through\n``ResultSetMapping#addFieldResult()``. Again, let's examine the\nmethod signature in detail:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Adds a field result that is part of an entity result or joined entity result.\n     *\n     * @param string $alias The alias of the entity result or joined entity result.\n     * @param string $columnName The name of the column in the SQL result set.\n     * @param string $fieldName The name of the field on the (joined) entity.\n     */\n    public function addFieldResult($alias, $columnName, $fieldName)\n\nThe first parameter is the alias of the entity result to which the\nfield result will belong. The second parameter is the name of the\ncolumn in the SQL result set. Note that this name is case\nsensitive, i.e. if you use a native query against Oracle it must be\nall uppercase. The third parameter is the name of the field on the\nentity result identified by ``$alias`` into which the value of the\ncolumn should be set.\n\nScalar results\n~~~~~~~~~~~~~~\n\nA scalar result describes the mapping of a single column in a SQL\nresult set to a scalar value in the Doctrine result. Scalar results\nare typically used for aggregate values but any column in the SQL\nresult set can be mapped as a scalar value. To add a scalar result\nuse ``ResultSetMapping#addScalarResult()``. The method signature in\ndetail:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Adds a scalar result mapping.\n     *\n     * @param string $columnName The name of the column in the SQL result set.\n     * @param string $alias The result alias with which the scalar result should be placed in the result structure.\n     */\n    public function addScalarResult($columnName, $alias)\n\nThe first parameter is the name of the column in the SQL result set\nand the second parameter is the result alias under which the value\nof the column will be placed in the transformed Doctrine result.\n\nSpecial case: DTOs\n...................\n\nYou can also use ``ResultSetMapping`` to map the results of a native SQL\nquery to a DTO (Data Transfer Object). This is done by adding scalar\nresults for each argument of the DTO's constructor, then filling the\n``newObjectMappings`` property of the ``ResultSetMapping`` with\ninformation about where to map each scalar result:\n\n.. code-block:: php\n\n   <?php\n\n    $rsm = new ResultSetMapping();\n    $rsm->addScalarResult('name', 1, 'string');\n    $rsm->addScalarResult('email', 2, 'string');\n    $rsm->addScalarResult('city', 3, 'string');\n    $rsm->newObjectMappings['name']  = [\n        'className' => CmsUserDTO::class,\n        'objIndex'  => 0, // a result can contain many DTOs, this is the index of the DTO to map to\n        'argIndex'  => 0, // each scalar result can be mapped to a different argument of the DTO constructor\n    ];\n    $rsm->newObjectMappings['email'] = [\n        'className' => CmsUserDTO::class,\n        'objIndex'  => 0,\n        'argIndex'  => 1,\n    ];\n    $rsm->newObjectMappings['city']  = [\n        'className' => CmsUserDTO::class,\n        'objIndex'  => 0,\n        'argIndex'  => 2,\n    ];\n\n\nMeta results\n~~~~~~~~~~~~\n\nA meta result describes a single column in a SQL result set that\nis either a foreign key or a discriminator column. These columns\nare essential for Doctrine to properly construct objects out of SQL\nresult sets. To add a column as a meta result use\n``ResultSetMapping#addMetaResult()``. The method signature in\ndetail:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Adds a meta column (foreign key or discriminator column) to the result set.\n     *\n     * @param string  $alias\n     * @param string  $columnAlias\n     * @param string  $columnName\n     * @param boolean $isIdentifierColumn\n     */\n    public function addMetaResult($alias, $columnAlias, $columnName, $isIdentifierColumn = false)\n\nThe first parameter is the alias of the entity result to which the\nmeta column belongs. A meta result column (foreign key or\ndiscriminator column) always belongs to an entity result. The\nsecond parameter is the column alias/name of the column in the SQL\nresult set and the third parameter is the column name used in the\nmapping.\nThe fourth parameter should be set to true in case the primary key\nof the entity is the foreign key you're adding.\n\nDiscriminator Column\n~~~~~~~~~~~~~~~~~~~~\n\nWhen joining an inheritance tree you have to give Doctrine a hint\nwhich meta-column is the discriminator column of this tree.\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Sets a discriminator column for an entity result or joined entity result.\n     * The discriminator column will be used to determine the concrete class name to\n     * instantiate.\n     *\n     * @param string $alias The alias of the entity result or joined entity result the discriminator\n     *                      column should be used for.\n     * @param string $discrColumn The name of the discriminator column in the SQL result set.\n     */\n    public function setDiscriminatorColumn($alias, $discrColumn)\n\nExamples\n~~~~~~~~\n\nUnderstanding a ResultSetMapping is probably easiest through\nlooking at some examples.\n\nFirst a basic example that describes the mapping of a single\nentity.\n\n.. code-block:: php\n\n    <?php\n    // Equivalent DQL query: \"select u from User u where u.name=?1\"\n    // User owns no associations.\n    $rsm = new ResultSetMapping;\n    $rsm->addEntityResult('User', 'u');\n    $rsm->addFieldResult('u', 'id', 'id');\n    $rsm->addFieldResult('u', 'name', 'name');\n\n    $query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);\n    $query->setParameter(1, 'romanb');\n\n    $users = $query->getResult();\n\nThe result would look like this:\n\n.. code-block:: php\n\n    array(\n        [0] => User (Object)\n    )\n\nNote that this would be a partial object if the entity has more\nfields than just id and name. In the example above the column and\nfield names are identical but that is not necessary, of course.\nAlso note that the query string passed to createNativeQuery is\n**real native SQL**. Doctrine does not touch this SQL in any way.\n\nIn the previous basic example, a User had no relations and the\ntable the class is mapped to owns no foreign keys. The next example\nassumes User has a unidirectional or bidirectional one-to-one\nassociation to a CmsAddress, where the User is the owning side and\nthus owns the foreign key.\n\n.. code-block:: php\n\n    <?php\n    // Equivalent DQL query: \"select u from User u where u.name=?1\"\n    // User owns an association to an Address but the Address is not loaded in the query.\n    $rsm = new ResultSetMapping;\n    $rsm->addEntityResult('User', 'u');\n    $rsm->addFieldResult('u', 'id', 'id');\n    $rsm->addFieldResult('u', 'name', 'name');\n    $rsm->addMetaResult('u', 'address_id', 'address_id');\n\n    $query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);\n    $query->setParameter(1, 'romanb');\n\n    $users = $query->getResult();\n\nForeign keys are used by Doctrine for lazy-loading purposes when\nquerying for objects. In the previous example, each user object in\nthe result will have a proxy (a \"ghost\") in place of the address\nthat contains the address\\_id. When the ghost proxy is accessed, it\nloads itself based on this key.\n\nConsequently, associations that are *fetch-joined* do not require\nthe foreign keys to be present in the SQL result set, only\nassociations that are lazy.\n\n.. code-block:: php\n\n    <?php\n    // Equivalent DQL query: \"select u from User u join u.address a WHERE u.name = ?1\"\n    // User owns association to an Address and the Address is loaded in the query.\n    $rsm = new ResultSetMapping;\n    $rsm->addEntityResult('User', 'u');\n    $rsm->addFieldResult('u', 'id', 'id');\n    $rsm->addFieldResult('u', 'name', 'name');\n    $rsm->addJoinedEntityResult('Address' , 'a', 'u', 'address');\n    $rsm->addFieldResult('a', 'address_id', 'id');\n    $rsm->addFieldResult('a', 'street', 'street');\n    $rsm->addFieldResult('a', 'city', 'city');\n\n    $sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .\n           'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';\n    $query = $this->_em->createNativeQuery($sql, $rsm);\n    $query->setParameter(1, 'romanb');\n\n    $users = $query->getResult();\n\nIn this case the nested entity ``Address`` is registered with the\n``ResultSetMapping#addJoinedEntityResult`` method, which notifies\nDoctrine that this entity is not hydrated at the root level, but as\na joined entity somewhere inside the object graph. In this case we\nspecify the alias 'u' as third parameter and ``address`` as fourth\nparameter, which means the ``Address`` is hydrated into the\n``User::$address`` property.\n\nIf a fetched entity is part of a mapped hierarchy that requires a\ndiscriminator column, this column must be present in the result set\nas a meta column so that Doctrine can create the appropriate\nconcrete type. This is shown in the following example where we\nassume that there are one or more subclasses that extend User and\neither Class Table Inheritance or Single Table Inheritance is used\nto map the hierarchy (both use a discriminator column).\n\n.. code-block:: php\n\n    <?php\n    // Equivalent DQL query: \"select u from User u where u.name=?1\"\n    // User is a mapped base class for other classes. User owns no associations.\n    $rsm = new ResultSetMapping;\n    $rsm->addEntityResult('User', 'u');\n    $rsm->addFieldResult('u', 'id', 'id');\n    $rsm->addFieldResult('u', 'name', 'name');\n    $rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column\n    $rsm->setDiscriminatorColumn('u', 'discr');\n\n    $query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);\n    $query->setParameter(1, 'romanb');\n\n    $users = $query->getResult();\n\nNote that in the case of Class Table Inheritance, an example as\nabove would result in partial objects if any objects in the result\nare actually a subtype of User. When using DQL, Doctrine\nautomatically includes the necessary joins for this mapping\nstrategy but with native SQL it is your responsibility.\n"
  },
  {
    "path": "docs/en/reference/partial-hydration.rst",
    "content": "Partial Hydration\n=================\n\nPartial hydration of entities is allowed in the array hydrator, when\nonly a subset of the fields of an entity are loaded from the database\nand the nested results are still created based on the entity relationship structure.\n\n.. code-block:: php\n\n    <?php\n    $users = $em->createQuery(\"SELECT PARTIAL u.{id,name}, partial a.{id,street} FROM MyApp\\Domain\\User u JOIN u.addresses a\")\n                ->getArrayResult();\n\nThis is a useful optimization when you are not interested in all fields of an entity\nfor performance reasons, for example in use-cases for exporting or rendering lots of data.\n"
  },
  {
    "path": "docs/en/reference/partial-objects.rst",
    "content": "Partial Objects\n===============\n\nA partial object is an object whose state is not fully initialized\nafter being reconstituted from the database and that is\ndisconnected from the rest of its data. The following section will\ndescribe why partial objects are problematic and what the approach\nof Doctrine to this problem is.\n\n.. note::\n\n    The partial object problem in general does not apply to\n    methods or queries where you do not retrieve the query result as\n    objects. Examples are: ``Query#getArrayResult()``,\n    ``Query#getScalarResult()``, ``Query#getSingleScalarResult()``,\n    etc.\n\n.. warning::\n\n    Use of partial objects is tricky. Fields that are not retrieved\n    from the database will not be updated by the UnitOfWork even if they\n    get changed in your objects. You can only promote a partial object\n    to a fully-loaded object by calling ``EntityManager#refresh()``\n    or a DQL query with the refresh flag.\n\n\nWhat is the problem?\n--------------------\n\nIn short, partial objects are problematic because they are usually\nobjects with broken invariants. As such, code that uses these\npartial objects tends to be very fragile and either needs to \"know\"\nwhich fields or methods can be safely accessed or add checks around\nevery field access or method invocation. The same holds true for\nthe internals, i.e. the method implementations, of such objects.\nYou usually simply assume the state you need in the method is\navailable, after all you properly constructed this object before\nyou pushed it into the database, right? These blind assumptions can\nquickly lead to null reference errors when working with such\npartial objects.\n\nIt gets worse with the scenario of an optional association (0..1 to\n1). When the associated field is NULL, you don't know whether this\nobject does not have an associated object or whether it was simply\nnot loaded when the owning object was loaded from the database.\n\nThese are reasons why many ORMs do not allow partial objects at all\nand instead you always have to load an object with all its fields\n(associations being proxied). One secure way to allow partial\nobjects is if the programming language/platform allows the ORM tool\nto hook deeply into the object and instrument it in such a way that\nindividual fields (not only associations) can be loaded lazily on\nfirst access. This is possible in Java, for example, through\nbytecode instrumentation. In PHP though this is not possible, so\nthere is no way to have \"secure\" partial objects in an ORM with\ntransparent persistence.\n\nDoctrine, by default, does not allow partial objects. That means,\nany query that only selects partial object data and wants to\nretrieve the result as objects (i.e. ``Query#getResult()``) will\nraise an exception telling you that partial objects are dangerous.\nIf you want to force a query to return you partial objects,\npossibly as a performance tweak, you can use the ``partial``\nkeyword as follows:\n\n.. code-block:: php\n\n    <?php\n    $q = $em->createQuery(\"select partial u.{id,name} from MyApp\\Domain\\User u\");\n\nYou can also get a partial reference instead of a proxy reference by\ncalling:\n\n.. code-block:: php\n\n    <?php\n    $reference = $em->getPartialReference('MyApp\\Domain\\User', 1);\n\nPartial references are objects with only the identifiers set as they\nare passed to the second argument of the ``getPartialReference()`` method.\nAll other fields are null.\n\nWhen should I force partial objects?\n------------------------------------\n\nMainly for optimization purposes, but be careful of premature\noptimization as partial objects lead to potentially more fragile\ncode.\n"
  },
  {
    "path": "docs/en/reference/php-mapping.rst",
    "content": "PHP Mapping\n===========\n\nDoctrine ORM also allows you to provide the ORM metadata in the form of plain\nPHP code using the ``ClassMetadata`` API. You can write the code in inside of a\nstatic function named ``loadMetadata($class)`` on the entity class itself.\n\nStatic Function\n---------------\n\nIn addition to other drivers using configuration languages you can also\nprogramatically specify your mapping information inside of a static function\ndefined on the entity class itself.\n\nThis is useful for cases where you want to keep your entity and mapping\ninformation together but don't want to use attributes. For this you just\nneed to use the ``StaticPHPDriver``:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver;\n\n    $driver = new StaticPHPDriver('/path/to/entities');\n    $em->getConfiguration()->setMetadataDriverImpl($driver);\n\nNow you just need to define a static function named\n``loadMetadata($metadata)`` on your entity:\n\n.. code-block:: php\n\n    <?php\n    namespace Entities;\n\n    use Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n    class User\n    {\n        // ...\n\n        public static function loadMetadata(ClassMetadata $metadata)\n        {\n            $metadata->mapField(array(\n               'id' => true,\n               'fieldName' => 'id',\n               'type' => 'integer'\n            ));\n\n            $metadata->mapField(array(\n               'fieldName' => 'username',\n               'type' => 'string'\n            ));\n        }\n    }\n\nClassMetadataBuilder\n--------------------\n\nTo ease the use of the ClassMetadata API (which is very raw) there is a ``ClassMetadataBuilder`` that you can use.\n\n.. code-block:: php\n\n    <?php\n    namespace Entities;\n\n    use Doctrine\\ORM\\Mapping\\ClassMetadata;\n    use Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder;\n\n    class User\n    {\n        // ...\n\n        public static function loadMetadata(ClassMetadata $metadata)\n        {\n            $builder = new ClassMetadataBuilder($metadata);\n            $builder->createField('id', 'integer')->isPrimaryKey()->generatedValue()->build();\n            $builder->addField('username', 'string');\n        }\n    }\n\nThe API of the ClassMetadataBuilder has the following methods with a fluent interface:\n\n-   ``addField($name, $type, array $mapping)``\n-   ``setMappedSuperclass()``\n-   ``setReadOnly()``\n-   ``setCustomRepositoryClass($className)``\n-   ``setTable($name)``\n-   ``addIndex(array $columns, $indexName)``\n-   ``addUniqueConstraint(array $columns, $constraintName)``\n-   ``setJoinedTableInheritance()``\n-   ``setSingleTableInheritance()``\n-   ``setDiscriminatorColumn($name, $type = 'string', $length = 255, $columnDefinition = null, $enumType = null, $options = [])``\n-   ``addDiscriminatorMapClass($name, $class)``\n-   ``setChangeTrackingPolicyDeferredExplicit()``\n-   ``addLifecycleEvent($methodName, $event)``\n-   ``addManyToOne($name, $targetEntity, $inversedBy = null)``\n-   ``addInverseOneToOne($name, $targetEntity, $mappedBy)``\n-   ``addOwningOneToOne($name, $targetEntity, $inversedBy = null)``\n-   ``addOwningManyToMany($name, $targetEntity, $inversedBy = null)``\n-   ``addInverseManyToMany($name, $targetEntity, $mappedBy)``\n-   ``addOneToMany($name, $targetEntity, $mappedBy)``\n\nIt also has several methods that create builders (which are necessary for advanced mappings):\n\n-   ``createField($name, $type)`` returns a ``FieldBuilder`` instance\n-   ``createManyToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance\n-   ``createOneToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance\n-   ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance\n-   ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance\n\nClassMetadata API\n-----------------\n\nThe ``ClassMetadata`` class is the data object for storing the mapping\nmetadata for a single entity. It contains all the getters and setters\nyou need populate and retrieve information for an entity.\n\nGeneral Setters\n~~~~~~~~~~~~~~~\n\n\n-  ``setTableName($tableName)``\n-  ``setPrimaryTable(array $primaryTableDefinition)``\n-  ``setCustomRepositoryClass($repositoryClassName)``\n-  ``setIdGeneratorType($generatorType)``\n-  ``setIdGenerator($generator)``\n-  ``setSequenceGeneratorDefinition(array $definition)``\n-  ``setChangeTrackingPolicy($policy)``\n-  ``setIdentifier(array $identifier)``\n\nInheritance Setters\n~~~~~~~~~~~~~~~~~~~\n\n\n-  ``setInheritanceType($type)``\n-  ``setSubclasses(array $subclasses)``\n-  ``setParentClasses(array $classNames)``\n-  ``setDiscriminatorColumn($columnDef)``\n-  ``setDiscriminatorMap(array $map)``\n\nField Mapping Setters\n~~~~~~~~~~~~~~~~~~~~~\n\n\n-  ``mapField(array $mapping)``\n-  ``mapOneToOne(array $mapping)``\n-  ``mapOneToMany(array $mapping)``\n-  ``mapManyToOne(array $mapping)``\n-  ``mapManyToMany(array $mapping)``\n\nLifecycle Callback Setters\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n-  ``addLifecycleCallback($callback, $event)``\n-  ``setLifecycleCallbacks(array $callbacks)``\n\nVersioning Setters\n~~~~~~~~~~~~~~~~~~\n\n\n-  ``setVersionMapping(array &$mapping)``\n-  ``setVersioned($bool)``\n-  ``setVersionField()``\n\nGeneral Getters\n~~~~~~~~~~~~~~~\n\n\n-  ``getTableName()``\n-  ``getSchemaName()``\n-  ``getTemporaryIdTableName()``\n\nIdentifier Getters\n~~~~~~~~~~~~~~~~~~\n\n\n-  ``getIdentifierColumnNames()``\n-  ``usesIdGenerator()``\n-  ``isIdentifier($fieldName)``\n-  ``isIdGeneratorIdentity()``\n-  ``isIdGeneratorSequence()``\n-  ``isIdGeneratorTable()``\n-  ``isIdentifierNatural()``\n-  ``getIdentifierFieldNames()``\n-  ``getSingleIdentifierFieldName()``\n-  ``getSingleIdentifierColumnName()``\n\nInheritance Getters\n~~~~~~~~~~~~~~~~~~~\n\n\n-  ``isInheritanceTypeNone()``\n-  ``isInheritanceTypeJoined()``\n-  ``isInheritanceTypeSingleTable()``\n-  ``isInheritedField($fieldName)``\n-  ``isInheritedAssociation($fieldName)``\n\nChange Tracking Getters\n~~~~~~~~~~~~~~~~~~~~~~~\n\n\n-  ``isChangeTrackingDeferredExplicit()``\n-  ``isChangeTrackingDeferredImplicit()``\n\nField & Association Getters\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n-  ``isUniqueField($fieldName)``\n-  ``isNullable($fieldName)``\n-  ``isIndexed($fieldName)``\n-  ``getColumnName($fieldName)``\n-  ``getFieldMapping($fieldName)``\n-  ``getAssociationMapping($fieldName)``\n-  ``getAssociationMappings()``\n-  ``getFieldName($columnName)``\n-  ``hasField($fieldName)``\n-  ``getColumnNames(array $fieldNames = null)``\n-  ``getTypeOfField($fieldName)``\n-  ``getTypeOfColumn($columnName)``\n-  ``hasAssociation($fieldName)``\n-  ``isSingleValuedAssociation($fieldName)``\n-  ``isCollectionValuedAssociation($fieldName)``\n\nLifecycle Callback Getters\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n-  ``hasLifecycleCallbacks($lifecycleEvent)``\n-  ``getLifecycleCallbacks($event)``\n\nRuntime reflection methods\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThese are methods related to runtime reflection for working with the\nentities themselves.\n\n\n-  ``getReflectionClass()``\n-  ``getReflectionProperties()``\n-  ``getReflectionProperty($name)``\n-  ``getSingleIdReflectionProperty()``\n-  ``getIdentifierValues($entity)``\n-  ``setIdentifierValues($entity, $id)``\n-  ``setFieldValue($entity, $field, $value)``\n-  ``getFieldValue($entity, $field)``\n"
  },
  {
    "path": "docs/en/reference/query-builder.rst",
    "content": "The QueryBuilder\n================\n\nA ``QueryBuilder`` provides an API that is designed for\nconditionally constructing a DQL query in several steps.\n\nIt provides a set of classes and methods that is able to\nprogrammatically build queries, and also provides a fluent API.\nThis means that you can change between one methodology to the other\nas you want, or just pick a preferred one.\n\n.. note::\n\n    The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.\n    You should still use plain DQL when you can, as it is simpler and more readable.\n    More about this in the :doc:`FAQ <faq>`.\n\nConstructing a new QueryBuilder object\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe same way you build a normal Query, you build a ``QueryBuilder``\nobject. Here is an example of how to build a ``QueryBuilder``\nobject:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n\n    // example1: creating a QueryBuilder instance\n    $qb = $em->createQueryBuilder();\n\nAn instance of QueryBuilder has several informative methods.  One\ngood example is to inspect what type of object the\n``QueryBuilder`` is.\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    // example2: retrieving type of QueryBuilder\n    echo $qb->getType(); // Prints: 0\n\nThere're currently 3 possible return values for ``getType()``:\n\n\n-  ``QueryBuilder::SELECT``, which returns value 0\n-  ``QueryBuilder::DELETE``, returning value 1\n-  ``QueryBuilder::UPDATE``, which returns value 2\n\nIt is possible to retrieve the associated ``EntityManager`` of the\ncurrent ``QueryBuilder``, its DQL and also a ``Query`` object when\nyou finish building your DQL.\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    // example3: retrieve the associated EntityManager\n    $em = $qb->getEntityManager();\n\n    // example4: retrieve the DQL string of what was defined in QueryBuilder\n    $dql = $qb->getDql();\n\n    // example5: retrieve the associated Query object with the processed DQL\n    $q = $qb->getQuery();\n\nInternally, ``QueryBuilder`` works with a DQL cache to increase\nperformance. Any changes that may affect the generated DQL actually\nmodifies the state of ``QueryBuilder`` to a stage we call\nSTATE\\_DIRTY. One ``QueryBuilder`` can be in two different states:\n\n\n-  ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been\n   altered since last retrieval or nothing were added since its\n   instantiation\n-  ``QueryBuilder::STATE_DIRTY``, means DQL query must (and will)\n   be processed on next retrieval\n\nWorking with QueryBuilder\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\nHigh level API methods\n^^^^^^^^^^^^^^^^^^^^^^\n\nThe most straightforward way to build a dynamic query with the ``QueryBuilder`` is by taking\nadvantage of Helper methods. For all base code, there is a set of\nuseful methods to simplify a programmer's life. To illustrate how\nto work with them, here is the same example 6 re-written using\n``QueryBuilder`` helper methods:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    $qb->select('u')\n       ->from('User', 'u')\n       ->where('u.id = ?1')\n       ->orderBy('u.name', 'ASC');\n\n``QueryBuilder`` helper methods are considered the standard way to\nuse the ``QueryBuilder``. The ``$qb->expr()->*`` methods can help you\nbuild conditional expressions dynamically. Here is a converted example 8 to\nsuggested way to build queries with dynamic conditions:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    $qb->select(array('u')) // string 'u' is converted to array internally\n       ->from('User', 'u')\n       ->where($qb->expr()->orX(\n           $qb->expr()->eq('u.id', '?1'),\n           $qb->expr()->like('u.nickname', '?2')\n       ))\n       ->orderBy('u.surname', 'ASC');\n\nHere is a complete list of helper methods available in ``QueryBuilder``:\n\n.. code-block:: php\n\n    <?php\n    class QueryBuilder\n    {\n        // Example - $qb->select('u')\n        // Example - $qb->select(array('u', 'p'))\n        // Example - $qb->select($qb->expr()->select('u', 'p'))\n        public function select($select = null);\n\n        // addSelect does not override previous calls to select\n        //\n        // Example - $qb->select('u');\n        //              ->addSelect('p.area_code');\n        public function addSelect($select = null);\n\n        // Example - $qb->delete('User', 'u')\n        public function delete($delete = null, $alias = null);\n\n        // Example - $qb->update('Group', 'g')\n        public function update($update = null, $alias = null);\n\n        // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold'))\n        // Example - $qb->set('u.numChilds', 'u.numChilds + ?1')\n        // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1'))\n        public function set($key, $value);\n\n        // Example - $qb->from('Phonenumber', 'p')\n        // Example - $qb->from('Phonenumber', 'p', 'p.id')\n        public function from($from, $alias, $indexBy = null);\n\n        // Example - $qb->join('u.Group', 'g', Expr\\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))\n        // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1')\n        // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')\n        public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null);\n\n        // Example - $qb->innerJoin('u.Group', 'g', Expr\\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))\n        // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1')\n        // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')\n        public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);\n\n        // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\\Join::WITH, $qb->expr()->eq('p.area_code', 55))\n        // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')\n        // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55', 'p.id')\n        public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);\n\n        // NOTE: ->where() overrides all previously set conditions\n        //\n        // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2'))\n        // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))\n        // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')\n        public function where($where);\n\n        // NOTE: ->andWhere() can be used directly, without any ->where() before\n        //\n        // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))\n        public function andWhere($where);\n\n        // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10));\n        public function orWhere($where);\n\n        // NOTE: -> groupBy() overrides all previously set grouping conditions\n        //\n        // Example - $qb->groupBy('u.id')\n        public function groupBy($groupBy);\n\n        // Example - $qb->addGroupBy('g.name')\n        public function addGroupBy($groupBy);\n\n        // NOTE: -> having() overrides all previously set having conditions\n        //\n        // Example - $qb->having('u.salary >= ?1')\n        // Example - $qb->having($qb->expr()->gte('u.salary', '?1'))\n        public function having($having);\n\n        // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0))\n        public function andHaving($having);\n\n        // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100'))\n        public function orHaving($having);\n\n        // NOTE: -> orderBy() overrides all previously set ordering conditions\n        //\n        // Example - $qb->orderBy('u.surname', 'DESC')\n        public function orderBy($sort, $order = null);\n\n        // Example - $qb->addOrderBy('u.firstName')\n        public function addOrderBy($sort, $order = null); // Default $order = 'ASC'\n    }\n\nBinding parameters to your query\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nDoctrine supports dynamic binding of parameters to your query,\nsimilar to preparing queries. You can use both strings and numbers\nas placeholders, although both have a slightly different syntax.\nAdditionally, you must make your choice: Mixing both styles is not\nallowed. Binding parameters can simply be achieved as follows:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    $qb->select('u')\n       ->from('User', 'u')\n       ->where('u.id = ?1')\n       ->orderBy('u.name', 'ASC')\n       ->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100\n\nYou are not forced to enumerate your placeholders as the\nalternative syntax is available:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    $qb->select('u')\n       ->from('User', 'u')\n       ->where('u.id = :identifier')\n       ->orderBy('u.name', 'ASC')\n       ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100\n\nNote that numeric placeholders start with a ? followed by a number\nwhile the named placeholders start with a : followed by a string.\n\nCalling ``setParameter()`` automatically infers which type you are setting as\nvalue. This works for integers, arrays of strings/integers, DateTime instances\nand for managed entities. If you want to set a type explicitly you can call\nthe third argument to ``setParameter()`` explicitly. It accepts either a DBAL\n``Doctrine\\DBAL\\ParameterType::*`` or a DBAL Type name for conversion.\n\n.. note::\n\n    Even though passing DateTime instance is allowed, it impacts performance \n    as by default there is an attempt to load metadata for object, and if it's not found, \n    type is inferred from the original value.\n    \n.. code-block:: php\n\n    <?php\n    \n    use Doctrine\\DBAL\\Types\\Types;\n    \n    // prevents attempt to load metadata for date time class, improving performance\n    $qb->setParameter('date', new \\DateTimeImmutable(), Types::DATETIME_IMMUTABLE)\n\nIf you've got several parameters to bind to your query, you can\nalso use setParameters() instead of setParameter() with the\nfollowing syntax:\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n    use Doctrine\\ORM\\Query\\Parameter;\n    \n    // $qb instanceof QueryBuilder\n\n    // Query here...\n    $qb->setParameters(new ArrayCollection([\n        new Parameter('1', 'value for ?1'),\n        new Parameter('2', 'value for ?2')\n    ]));\n\nGetting already bound parameters is easy - simply use the above\nmentioned syntax with \"getParameter()\" or \"getParameters()\":\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    // See example above\n    $params = $qb->getParameters();\n    // $params instanceof \\Doctrine\\Common\\Collections\\ArrayCollection\n\n    // Equivalent to\n    $param = $qb->getParameter(1);\n    // $param instanceof \\Doctrine\\ORM\\Query\\Parameter\n\nNote: If you try to get a parameter that was not bound yet,\ngetParameter() simply returns NULL.\n\nThe API of a Query Parameter is:\n\n.. code-block:: php\n\n    namespace Doctrine\\ORM\\Query;\n\n    class Parameter\n    {\n        public function getName();\n        public function getValue();\n        public function getType();\n        public function setValue($value, $type = null);\n    }\n\nLimiting the Result\n^^^^^^^^^^^^^^^^^^^\n\nTo limit a result the query builder has some methods in common with\nthe Query object which can be retrieved from ``EntityManager#createQuery()``.\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n    $offset = (int)$_GET['offset'];\n    $limit = (int)$_GET['limit'];\n\n    $qb->add('select', 'u')\n       ->add('from', 'User u')\n       ->add('orderBy', 'u.name ASC')\n       ->setFirstResult( $offset )\n       ->setMaxResults( $limit );\n\nExecuting a Query\n^^^^^^^^^^^^^^^^^\n\nThe QueryBuilder is only a builder object - it has no means of actually\nexecuting the Query. Additional functionality, such as enabling the result cache,\ncannot be set on the QueryBuilder itself. This is why you must always convert\na QueryBuilder instance into a Query object:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n    $query = $qb->getQuery();\n\n    // Enable the result cache\n    $query->enableResultCache(3600, 'my_custom_id');\n\n    // Execute Query\n    $result = $query->getResult();\n    $iterableResult = $query->toIterable();\n    $single = $query->getSingleResult();\n    $array = $query->getArrayResult();\n    $scalar = $query->getScalarResult();\n    $singleScalar = $query->getSingleScalarResult();\n\nThe Expr class\n^^^^^^^^^^^^^^\n\nTo workaround some of the issues that ``add()`` method may cause,\nDoctrine created a class that can be considered as a helper for\nbuilding expressions. This class is called ``Expr``, which provides a\nset of useful methods to help build expressions:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    // example8: QueryBuilder port of:\n    // \"SELECT u FROM User u WHERE u.id = ? OR u.nickname LIKE ? ORDER BY u.name ASC\" using Expr class\n    $qb->add('select', new Expr\\Select(array('u')))\n       ->add('from', new Expr\\From('User', 'u'))\n       ->add('where', $qb->expr()->orX(\n           $qb->expr()->eq('u.id', '?1'),\n           $qb->expr()->like('u.nickname', '?2')\n       ))\n       ->add('orderBy', new Expr\\OrderBy('u.name', 'ASC'));\n\nAlthough it still sounds complex, the ability to programmatically\ncreate conditions are the main feature of ``Expr``. Here it is a\ncomplete list of supported helper methods available:\n\n.. code-block:: php\n\n    <?php\n    class Expr\n    {\n        /** Conditional objects **/\n\n        // Example - $qb->expr()->andX($cond1 [, $condN])->add(...)->...\n        public function andX($x = null); // Returns Expr\\AndX instance\n\n        // Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->...\n        public function orX($x = null); // Returns Expr\\OrX instance\n\n\n        /** Comparison objects **/\n\n        // Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1\n        public function eq($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->neq('u.id', '?1') => u.id <> ?1\n        public function neq($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->lt('u.id', '?1') => u.id < ?1\n        public function lt($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->lte('u.id', '?1') => u.id <= ?1\n        public function lte($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->gt('u.id', '?1') => u.id > ?1\n        public function gt($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1\n        public function gte($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->isNull('u.id') => u.id IS NULL\n        public function isNull($x); // Returns string\n\n        // Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL\n        public function isNotNull($x); // Returns string\n\n        // Example - $qb->expr()->isMemberOf('?1', 'u.groups') => ?1 MEMBER OF u.groups\n        public function isMemberOf($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->isInstanceOf('u', Employee::class) => u INSTANCE OF Employee\n        public function isInstanceOf($x, $y); // Returns Expr\\Comparison instance\n\n\n        /** Arithmetic objects **/\n\n        // Example - $qb->expr()->prod('u.id', '2') => u.id * 2\n        public function prod($x, $y); // Returns Expr\\Math instance\n\n        // Example - $qb->expr()->diff('u.id', '2') => u.id - 2\n        public function diff($x, $y); // Returns Expr\\Math instance\n\n        // Example - $qb->expr()->sum('u.id', '2') => u.id + 2\n        public function sum($x, $y); // Returns Expr\\Math instance\n\n        // Example - $qb->expr()->quot('u.id', '2') => u.id / 2\n        public function quot($x, $y); // Returns Expr\\Math instance\n\n\n        /** Pseudo-function objects **/\n\n        // Example - $qb->expr()->exists($qb2->getDql())\n        public function exists($subquery); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->all($qb2->getDql())\n        public function all($subquery); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->some($qb2->getDql())\n        public function some($subquery); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->any($qb2->getDql())\n        public function any($subquery); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->not($qb->expr()->eq('u.id', '?1'))\n        public function not($restriction); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->in('u.id', array(1, 2, 3))\n        // Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception.\n        // Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above)\n        public function in($x, $y); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->notIn('u.id', '2')\n        public function notIn($x, $y); // Returns Expr\\Func instance\n\n        // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))\n        public function like($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->notLike('u.firstname', $qb->expr()->literal('Gui%'))\n        public function notLike($x, $y); // Returns Expr\\Comparison instance\n\n        // Example - $qb->expr()->between('u.id', '1', '10')\n        public function between($val, $x, $y); // Returns Expr\\Func\n\n\n        /** Function objects **/\n\n        // Example - $qb->expr()->trim('u.firstname')\n        public function trim($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname'))\n        public function concat($x, $y); // Returns Expr\\Func\n\n        // Example - $qb->expr()->substring('u.firstname', 0, 1)\n        public function substring($x, $from, $len); // Returns Expr\\Func\n\n        // Example - $qb->expr()->lower('u.firstname')\n        public function lower($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->upper('u.firstname')\n        public function upper($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->length('u.firstname')\n        public function length($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->avg('u.age')\n        public function avg($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->max('u.age')\n        public function max($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->min('u.age')\n        public function min($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->abs('u.currentBalance')\n        public function abs($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->sqrt('u.currentBalance')\n        public function sqrt($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->mod('u.currentBalance', '10')\n        public function mod($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->count('u.firstname')\n        public function count($x); // Returns Expr\\Func\n\n        // Example - $qb->expr()->countDistinct('u.surname')\n        public function countDistinct($x); // Returns Expr\\Func\n    }\n\nAdding a Criteria to a Query\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can also add a :ref:`filtering-collections` to a QueryBuilder by\nusing ``addCriteria``:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\Common\\Collections\\Criteria;\n    // ...\n\n    $criteria = Criteria::create()\n        ->orderBy(['firstName' => Criteria::ASC]);\n\n    // $qb instanceof QueryBuilder\n    $qb->addCriteria($criteria);\n    // then execute your query like normal\n\nAdding hints to a Query\n^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can also set query hints to a QueryBuilder by using ``setHint``:\n\n.. code-block:: php\n\n    <?php\n    // ...\n\n    // $qb instanceof QueryBuilder\n    $qb->setHint('hintName', 'hintValue');\n    // then execute your query like normal\n\nThe query hint can hold anything the usual query hints can hold\nexcept null. Those hints will be applied to the query when the\nquery is created.\n\nLow Level API\n^^^^^^^^^^^^^\n\nNow we will describe the low level method of creating queries.\nIt may be useful to work at this level for optimization purposes,\nbut most of the time it is preferred to work at a higher level of\nabstraction.\n\nAll helper methods in ``QueryBuilder`` actually rely on a single\none: ``add()``. This method is responsible of building every piece\nof DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and\n``$append`` (default=false)\n\n\n-  ``$dqlPartName``: Where the ``$dqlPart`` should be placed.\n   Possible values: select, from, where, groupBy, having, orderBy\n-  ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts\n   a string or any instance of ``Doctrine\\ORM\\Query\\Expr\\*``\n-  ``$append``: Optional flag (default=false) if the ``$dqlPart``\n   should override all previously defined items in ``$dqlPartName`` or\n   not (no effect on the ``where`` and ``having`` DQL query parts,\n   which always override all previously defined items)\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    // example6: how to define:\n    // \"SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC\"\n    // using QueryBuilder string support\n    $qb->add('select', 'u')\n       ->add('from', 'User u')\n       ->add('where', 'u.id = ?1')\n       ->add('orderBy', 'u.name ASC');\n\nExpr\\* classes\n^^^^^^^^^^^^^^\n\nWhen you call ``add()`` with string, it internally evaluates to an\ninstance of ``Doctrine\\ORM\\Query\\Expr\\Expr\\*`` class. Here is the\nsame query of example 6 written using\n``Doctrine\\ORM\\Query\\Expr\\Expr\\*`` classes:\n\n.. code-block:: php\n\n   <?php\n   // $qb instanceof QueryBuilder\n\n   // example7: how to define:\n   // \"SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC\"\n   // using QueryBuilder using Expr\\* instances\n   $qb->add('select', new Expr\\Select(array('u')))\n      ->add('from', new Expr\\From('User', 'u'))\n      ->add('where', new Expr\\Comparison('u.id', '=', '?1'))\n      ->add('orderBy', new Expr\\OrderBy('u.name', 'ASC'));\n\nBinding Parameters to Placeholders\n----------------------------------\n\nIt is often not necessary to know about the exact placeholder names when\nbuilding a query. You can use a helper method to bind a value to a placeholder\nand directly use that placeholder in your query as a return value:\n\n.. code-block:: php\n\n    <?php\n    // $qb instanceof QueryBuilder\n\n    $qb->select('u')\n        ->from('User', 'u')\n        ->where('u.email = ' .  $qb->createNamedParameter($userInputEmail))\n    ;\n    // SELECT u FROM User u WHERE email = :dcValue1\n"
  },
  {
    "path": "docs/en/reference/second-level-cache.rst",
    "content": "The Second Level Cache\n======================\n\n.. note::\n\n    The second level cache functionality is marked as experimental for now. It\n    is a very complex feature and we cannot guarantee yet that it works stable\n    in all cases.\n\nThe Second Level Cache is designed to reduce the amount of necessary database access.\nIt sits between your application and the database to avoid the number of database hits as much as possible.\n\nWhen turned on, entities will be first searched in cache and if they are not found,\na database query will be fired and then the entity result will be stored in a cache provider.\n\nThere are some flavors of caching available, but is better to cache read-only data.\n\nBe aware that caches are not aware of changes made to the persistent store by another application.\nThey can, however, be configured to regularly expire cached data.\n\n\nCaching Regions\n---------------\n\nSecond level cache does not store instances of an entity, instead it caches only entity identifier and values.\nEach entity class, collection association and query has its region, where values of each instance are stored.\n\nCaching Regions are specific region into the cache provider that might store entities, collection or queries.\nEach cache region resides in a specific cache namespace and has its own lifetime configuration.\n\nNotice that when caching collection and queries only identifiers are stored.\nThe entity values will be stored in its own region\n\nSomething like below for an entity region:\n\n.. code-block:: php\n\n    <?php\n    [\n      'region_name:entity_1_hash' => ['id' => 1, 'name' => 'FooBar', 'associationName' => null],\n      'region_name:entity_2_hash' => ['id' => 2, 'name' => 'Foo', 'associationName' => ['id' => 11]],\n      'region_name:entity_3_hash' => ['id' => 3, 'name' => 'Bar', 'associationName' => ['id' => 22]]\n    ];\n\n\nIf the entity holds a collection that also needs to be cached.\nA collection region could look something like:\n\n.. code-block:: php\n\n    <?php\n    [\n      'region_name:entity_1_coll_assoc_name_hash' => ['ownerId' => 1, 'list' => [1, 2, 3]],\n      'region_name:entity_2_coll_assoc_name_hash' => ['ownerId' => 2, 'list' => [2, 3]],\n      'region_name:entity_3_coll_assoc_name_hash' => ['ownerId' => 3, 'list' => [2, 4]]\n    ];\n\nA query region might be something like:\n\n.. code-block:: php\n\n    <?php\n    [\n      'region_name:query_1_hash' => ['list' => [1, 2, 3]],\n      'region_name:query_2_hash' => ['list' => [2, 3]],\n      'region_name:query_3_hash' => ['list' => [2, 4]]\n    ];\n\n\n.. note::\n\n    The following data structures represents now the cache will looks like, this is not actual cached data.\n\n\n.. _reference-second-level-cache-regions:\n\nCache Regions\n-------------\n\n``Doctrine\\ORM\\Cache\\Region\\DefaultRegion`` is the default implementation.\n A simplest cache region compatible with all doctrine-cache drivers but does not support locking.\n``Doctrine\\ORM\\Cache\\Region`` and ``Doctrine\\ORM\\Cache\\ConcurrentRegion``\ndefine contracts that should be implemented by a cache provider.\n\nIt allows you to provide your own cache implementation that might take advantage of specific cache driver.\n\nIf you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise.\n\n\nCache region\n~~~~~~~~~~~~\n\n``Doctrine\\ORM\\Cache\\Region`` defines a contract for accessing a particular\ncache region.\n\nConcurrent cache region\n~~~~~~~~~~~~~~~~~~~~~~~\n\nA ``Doctrine\\ORM\\Cache\\ConcurrentRegion`` is designed to store concurrently managed data region.\nBy default, Doctrine provides a very simple implementation based on file locks ``Doctrine\\ORM\\Cache\\Region\\FileLockRegion``.\n\nIf you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region.\n\n``Doctrine\\ORM\\Cache\\ConcurrentRegion`` defines a contract for concurrently managed data region.\n\nTimestamp region\n~~~~~~~~~~~~~~~~\n\n``Doctrine\\ORM\\Cache\\TimestampRegion``\n\nTracks the timestamps of the most recent updates to particular entity.\n\n.. _reference-second-level-cache-mode:\n\nCaching mode\n------------\n\n* ``READ_ONLY`` (DEFAULT)\n\n  * Can do reads, inserts and deletes, cannot perform updates or employ any locks.\n  * Useful for data that is read frequently but never updated.\n  * Best performer.\n  * It is Simple.\n\n* ``NONSTRICT_READ_WRITE``\n\n  * Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes.\n  * Good if the application needs to update data rarely.\n\n\n* ``READ_WRITE``\n\n  * Read Write cache employs locks before update/delete.\n  * Use if data needs to be updated.\n  * Slowest strategy.\n  * To use it the cache region implementation must support locking.\n\n\nBuilt-in cached persisters\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCached persisters are responsible to access cache regions.\n\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | Cache Usage           | Persister                                                                                |\n    +=======================+==========================================================================================+\n    | READ_ONLY             | ``Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister``                    |\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | READ_WRITE            | ``Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister``                   |\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | NONSTRICT_READ_WRITE  | ``Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister``          |\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | READ_ONLY             | ``Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister``            |\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | READ_WRITE            | ``Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister``           |\n    +-----------------------+------------------------------------------------------------------------------------------+\n    | NONSTRICT_READ_WRITE  | ``Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister``  |\n    +-----------------------+------------------------------------------------------------------------------------------+\n\nConfiguration\n-------------\nDoctrine allows you to specify configurations and some points of extension for the second-level-cache\n\n\nEnable Second Level Cache\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo enable the second-level-cache, you should provide a cache factory.\n``Doctrine\\ORM\\Cache\\DefaultCacheFactory`` is the default implementation.\n\n.. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\Cache\\RegionsConfiguration $cacheConfig */\n    /** @var \\Psr\\Cache\\CacheItemPoolInterface $cache */\n    /** @var \\Doctrine\\ORM\\Configuration $config */\n\n    $factory = new \\Doctrine\\ORM\\Cache\\DefaultCacheFactory($cacheConfig, $cache);\n\n    // Enable second-level-cache\n    $config->setSecondLevelCacheEnabled();\n\n    // Cache factory\n    $config->getSecondLevelCacheConfiguration()\n        ->setCacheFactory($factory);\n\n\nCache Factory\n~~~~~~~~~~~~~\n\nCache Factory is the main point of extension.\n\nIt allows you to provide a specific implementation of the following components:\n\n``QueryCache``\n    stores and retrieves query cache results.\n``CachedEntityPersister``\n    stores and retrieves entity results.\n``CachedCollectionPersister``\n    stores and retrieves query results.\n``EntityHydrator``\n    transforms entities into a cache entries and cache entries into entities\n``CollectionHydrator``\n    transforms collections into cache entries and cache entries into collections\n\nRegion Lifetime\n~~~~~~~~~~~~~~~\n\nTo specify a default lifetime for all regions or specify a different lifetime for a specific region.\n\n.. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\Configuration $config */\n    /** @var \\Doctrine\\ORM\\Cache\\CacheConfiguration $cacheConfig */\n    /** @var \\Doctrine\\ORM\\Cache\\RegionsConfiguration $regionConfig */\n    $cacheConfig  =  $config->getSecondLevelCacheConfiguration();\n    $regionConfig =  $cacheConfig->getRegionsConfiguration();\n\n    // Cache Region lifetime\n    $regionConfig->setLifetime('my_entity_region', 3600);   // Time to live for a specific region (in seconds)\n    $regionConfig->setDefaultLifetime(7200);                // Default time to live (in seconds)\n\n\nCache Log\n~~~~~~~~~\nBy providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts.\n\n``Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.\n\n .. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\Configuration $config */\n    $logger = new \\Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger();\n\n    // Cache logger\n    $config->setSecondLevelCacheEnabled(true);\n    $config->getSecondLevelCacheConfiguration()\n        ->setCacheLogger($logger);\n\n\n    // Collect cache statistics\n\n    // Get the number of entries successfully retrieved from a specific region.\n    $logger->getRegionHitCount('my_entity_region');\n\n    // Get the number of cached entries *not* found in a specific region.\n    $logger->getRegionMissCount('my_entity_region');\n\n    // Get the number of cacheable entries put in cache.\n    $logger->getRegionPutCount('my_entity_region');\n\n    // Get the total number of put in all regions.\n    $logger->getPutCount();\n\n    // Get the total number of entries successfully retrieved from all regions.\n    $logger->getHitCount();\n\n    // Get the total number of cached entries *not* found in all regions.\n    $logger->getMissCount();\n\nIf you want to get more information you should implement\n``Doctrine\\ORM\\Cache\\Logging\\CacheLogger`` and collect\nall the information you want.\n\nEntity cache definition\n-----------------------\n* Entity cache configuration allows you to define the caching strategy and region for an entity.\n\n  * ``usage`` specifies the caching strategy: ``READ_ONLY``,\n``NONSTRICT_READ_WRITE``, ``READ_WRITE``.\nSee :ref:`reference-second-level-cache-mode`.\n  * ``region`` is an optional value that specifies the name of the second\nlevel cache region.\n\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        #[Cache(usage: 'READ_ONLY', region: 'my_entity_region')]\n        class Country\n        {\n            #[Id]\n            #[GeneratedValue]\n            #[Column]\n            protected int|null $id = null;\n\n            #[Column(unique: true)]\n            protected string $name;\n\n            // other properties and methods\n        }\n\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"utf-8\"?>\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n          <entity name=\"Country\">\n            <cache usage=\"READ_ONLY\" region=\"my_entity_region\" />\n            <id name=\"id\" type=\"integer\" column=\"id\">\n              <generator strategy=\"IDENTITY\"/>\n            </id>\n            <field name=\"name\" type=\"string\" column=\"name\"/>\n          </entity>\n        </doctrine-mapping>\n\nAssociation cache definition\n----------------------------\nThe most common use case is to cache entities. But we can also cache relationships.\nIt caches the primary keys of association and cache each element will be cached into its region.\n\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        #[Cache(usage: 'NONSTRICT_READ_WRITE')]\n        class State\n        {\n            #[Id]\n            #[GeneratedValue]\n            #[Column]\n            protected int|null $id = null;\n\n            #[Column(unique: true)]\n            protected string $name;\n\n            #[Cache(usage: 'NONSTRICT_READ_WRITE')]\n            #[ManyToOne(targetEntity: Country::class)]\n            #[JoinColumn(name: 'country_id', referencedColumnName: 'id')]\n            protected Country|null $country = null;\n\n            /** @var Collection<int, City> */\n            #[Cache(usage: 'NONSTRICT_READ_WRITE')]\n            #[OneToMany(targetEntity: City::class, mappedBy: 'state')]\n            protected Collection $cities;\n\n            // other properties and methods\n        }\n\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"utf-8\"?>\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n          <entity name=\"State\">\n\n            <cache usage=\"NONSTRICT_READ_WRITE\" />\n\n            <id name=\"id\" type=\"integer\" column=\"id\">\n              <generator strategy=\"IDENTITY\"/>\n            </id>\n\n            <field name=\"name\" type=\"string\" column=\"name\"/>\n\n            <many-to-one field=\"country\" target-entity=\"Country\">\n              <cache usage=\"NONSTRICT_READ_WRITE\" />\n\n              <join-columns>\n                <join-column name=\"country_id\" referenced-column-name=\"id\"/>\n              </join-columns>\n            </many-to-one>\n\n            <one-to-many field=\"cities\" target-entity=\"City\" mapped-by=\"state\">\n              <cache usage=\"NONSTRICT_READ_WRITE\"/>\n            </one-to-many>\n          </entity>\n        </doctrine-mapping>\n\n.. note::\n\n    for this to work, the target entity must also be marked as cacheable.\n\nCache usage\n~~~~~~~~~~~\n\nBasic entity cache\n\n.. code-block:: php\n\n    <?php\n    $em->persist(new Country($name));\n    $em->flush();                         // Hit database to insert the row and put into cache\n\n    $em->clear();                         // Clear entity manager\n\n    $country1  = $em->find('Country', 1); // Retrieve item from cache\n\n    $country1->setName('New Name');\n\n    $em->flush();                         // Hit database to update the row and update cache\n\n    $em->clear();                         // Clear entity manager\n\n    $country2  = $em->find('Country', 1); // Retrieve item from cache\n                                          // Notice that $country1 and $country2 are not the same instance.\n\n\nAssociation cache\n\n.. code-block:: php\n\n    <?php\n    // Hit database to insert the row and put into cache\n    $em->persist(new State($name, $country));\n    $em->flush();\n\n    // Clear entity manager\n    $em->clear();\n\n    // Retrieve item from cache\n    $state = $em->find('State', 1);\n\n    // Hit database to update the row and update cache entry\n    $state->setName('New Name');\n    $em->persist($state);\n    $em->flush();\n\n    // Create a new collection item\n    $city = new City($name, $state);\n    $state->addCity($city);\n\n    // Hit database to insert new collection item,\n    // put entity and collection cache into cache.\n    $em->persist($city);\n    $em->persist($state);\n    $em->flush();\n\n    // Clear entity manager\n    $em->clear();\n\n    // Retrieve item from cache\n    $state = $em->find('State', 1);\n\n    // Retrieve association from cache\n    $country = $state->getCountry();\n\n    // Retrieve collection from cache\n    $cities = $state->getCities();\n\n    echo $country->getName();\n    echo $state->getName();\n\n    // Retrieve each collection item from cache\n    foreach ($cities as $city) {\n        echo $city->getName();\n    }\n\n.. note::\n\n    Notice that all entities should be marked as cacheable.\n\nUsing the query cache\n---------------------\n\nThe second level cache stores the entities, associations and collections.\nThe query cache stores the results of the query but as identifiers, entity values are actually stored in the 2nd level cache.\n\n.. note::\n\n    Query cache should always be used in conjunction with the second-level-cache for those entities which should be cached.\n\n.. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\EntityManager $em */\n\n    // Execute database query, store query cache and entity cache\n    $result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')\n        ->setCacheable(true)\n        ->getResult();\n\n    $em->clear();\n\n    // Check if query result is valid and load entities from cache\n    $result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')\n        ->setCacheable(true)\n        ->getResult();\n\nCache mode\n~~~~~~~~~~\n\nThe Cache Mode controls how a particular query interacts with the second-level cache:\n\n* ``Cache::MODE_GET`` - May read items from the cache, but will not add items.\n* ``Cache::MODE_PUT`` - Will never read items from the cache, but will add items to the cache as it reads them from the database.\n* ``Cache::MODE_NORMAL`` - May read items from the cache, and add items to the cache.\n* ``Cache::MODE_REFRESH`` - The query will never read items from the cache, but will refresh items to the cache as it reads them from the database.\n\n.. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\EntityManager $em */\n    // Will refresh the query cache and all entities the cache as it reads from the database.\n    $result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')\n        ->setCacheMode(\\Doctrine\\ORM\\Cache::MODE_GET)\n        ->setCacheable(true)\n        ->getResult();\n\n.. note::\n\n    The default query cache mode is ```Cache::MODE_NORMAL```\n\nDELETE / UPDATE queries\n~~~~~~~~~~~~~~~~~~~~~~~\n\nDQL UPDATE / DELETE statements are ported directly into a database and bypass\nthe second-level cache.\nEntities that are already cached will NOT be invalidated.\nHowever the cached data could be evicted using the cache API or a special query hint.\n\n\nExecute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``\n\n.. code-block:: php\n\n    <?php\n    // Execute and invalidate\n    $this->_em->createQuery(\"UPDATE Entity\\Country u SET u.name = 'unknown' WHERE u.id = 1\")\n        ->setHint(\\Doctrine\\ORM\\Query::HINT_CACHE_EVICT, true)\n        ->execute();\n\n\nExecute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API\n\n.. code-block:: php\n\n    <?php\n    // Execute\n    $this->_em->createQuery(\"UPDATE Entity\\Country u SET u.name = 'unknown' WHERE u.id = 1\")\n        ->execute();\n    // Invoke Cache API\n    $em->getCache()->evictEntityRegion('Entity\\Country');\n\n\nExecute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API\n\n.. code-block:: php\n\n    <?php\n    // Execute\n    $this->_em->createQuery(\"UPDATE Entity\\Country u SET u.name = 'unknown' WHERE u.id = 1\")\n        ->execute();\n    // Invoke Cache API\n    $em->getCache()->evictEntity('Entity\\Country', 1);\n\nUsing the repository query cache\n--------------------------------\n\nAs well as ``Query Cache`` all persister queries store only identifier values for an individual query.\nAll persisters use a single timestamp cache region to keep track of the last update for each persister,\nWhen a query is loaded from cache, the timestamp region is checked for the last update for that persister.\nUsing the last update timestamps as part of the query key invalidate the cache key when an update occurs.\n\n.. code-block:: php\n\n    <?php\n    // load from database and store cache query key hashing the query + parameters + last timestamp cache region..\n    $entities   = $em->getRepository('Entity\\Country')->findAll();\n\n    // load from query and entities from cache..\n    $entities   = $em->getRepository('Entity\\Country')->findAll();\n\n    // update the timestamp cache region for Country\n    $em->persist(new Country('zombieland'));\n    $em->flush();\n    $em->clear();\n\n    // Reload from database.\n    // At this point the query cache key is no longer valid, the select goes straight to the database\n    $entities   = $em->getRepository('Entity\\Country')->findAll();\n\nCache API\n---------\n\nCaches are not aware of changes made by another application.\nHowever, you can use the cache API to check / invalidate cache entries.\n\n.. code-block:: php\n\n    <?php\n    /** @var \\Doctrine\\ORM\\Cache $cache */\n    $cache = $em->getCache();\n\n    $cache->containsEntity('Entity\\State', 1)      // Check if the cache exists\n    $cache->evictEntity('Entity\\State', 1);        // Remove an entity from cache\n    $cache->evictEntityRegion('Entity\\State');     // Remove all entities from cache\n\n    $cache->containsCollection('Entity\\State', 'cities', 1);   // Check if the cache exists\n    $cache->evictCollection('Entity\\State', 'cities', 1);      // Remove an entity collection from cache\n    $cache->evictCollectionRegion('Entity\\State', 'cities');   // Remove all collections from cache\n\nLimitations\n-----------\n\nComposite primary key\n~~~~~~~~~~~~~~~~~~~~~\n\nComposite primary key are supported by second level cache,\nhowever when one of the keys is an association the cached entity should always be retrieved using the association identifier.\nFor performance reasons the cache API does not extract from composite primary key.\n\n.. code-block:: php\n\n    <?php\n\n    #[Entity]\n    class Reference\n    {\n        #[Id]\n        #[ManyToOne(targetEntity: Article::class, inversedBy: 'references')]\n        #[JoinColumn(name: 'source_id', referencedColumnName: 'article_id')]\n        private Article|null $source = null;\n\n        #[Id]\n        #[ManyToOne(targetEntity: Article::class, inversedBy: 'references')]\n        #[JoinColumn(name: 'target_id', referencedColumnName: 'article_id')]\n        private $target;\n    }\n\n    // Supported\n    /** @var Article $article */\n    $article = $em->find('Article', 1);\n\n    // Supported\n    /** @var Article $article */\n    $article = $em->find('Article', $article);\n\n    // Supported\n    $id        = ['source' => 1, 'target' => 2];\n    $reference = $em->find('Reference', $id);\n\n    // NOT Supported\n    $id        = ['source' => new Article(1), 'target' => new Article(2)];\n    $reference = $em->find('Reference', $id);\n\nDistributed environments\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nSome cache driver are not meant to be used in a distributed environment.\nLoad-balancer for distributing workloads across multiple computing resources\nshould be used in conjunction with distributed caching system such as memcached, redis, riak ...\n\nCaches should be used with care when using a load-balancer if you don't share the cache.\nWhile using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines.\n\n\nPaginator\n~~~~~~~~~\n\nCount queries generated by ``Doctrine\\ORM\\Tools\\Pagination\\Paginator`` are not cached by second-level cache.\nAlthough entities and query result are cached, count queries will hit the\ndatabase every time.\n"
  },
  {
    "path": "docs/en/reference/security.rst",
    "content": "Security\n========\n\nThe Doctrine library is operating very close to your database and as such needs\nto handle and make assumptions about SQL injection vulnerabilities.\n\nIt is vital that you understand how Doctrine approaches security, because\nwe cannot protect you from SQL injection.\n\nPlease also read the documentation chapter on Security in Doctrine DBAL. This\npage only handles Security issues in the ORM.\n\n- `DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`\n\nIf you find a Security bug in Doctrine, please follow our\n`Security reporting guidelines <https://www.doctrine-project.org/policies/security.html#reporting>`_.\n\nUser input and Doctrine ORM\n---------------------------\n\nThe ORM is much better at protecting against SQL injection than the DBAL alone.\nYou can consider the following APIs to be safe from SQL injection:\n\n- ``\\Doctrine\\ORM\\EntityManager#find()`` and ``getReference()``.\n- All values on Objects inserted and updated through ``Doctrine\\ORM\\EntityManager#persist()``\n- All find methods on ``Doctrine\\ORM\\EntityRepository``.\n- User Input set to DQL Queries or QueryBuilder methods through\n    - ``setParameter()`` or variants\n    - ``setMaxResults()``\n    - ``setFirstResult()``\n- Queries through the Criteria API on ``Doctrine\\ORM\\PersistentCollection`` and\n  ``Doctrine\\ORM\\EntityRepository``.\n\nYou are **NOT** safe from SQL injection when using user input with:\n\n- Expression API of ``Doctrine\\ORM\\QueryBuilder``\n- Concatenating user input into DQL SELECT, UPDATE or DELETE statements or\n  Native SQL.\n\nThis means SQL injections can only occur with Doctrine ORM when working with\nQuery Objects of any kind. The safe rule is to always use prepared statement\nparameters for user objects when using a Query object.\n\n.. warning::\n\n    Insecure code follows, don't copy paste this.\n\nThe following example shows insecure DQL usage:\n\n.. code-block:: php\n\n    <?php\n\n    // INSECURE\n    $dql = \"SELECT u\n              FROM MyProject\\Entity\\User u\n             WHERE u.status = '\" .  $_GET['status'] . \"'\n         ORDER BY \" . $_GET['orderField'] . \" ASC\";\n\nFor Doctrine there is absolutely no way to find out which parts of ``$dql`` are\nfrom user input and which are not, even if we have our own parsing process\nthis is technically impossible. The correct way is:\n\n.. code-block:: php\n\n    <?php\n\n    $orderFieldWhitelist = array('email', 'username');\n    $orderField = \"email\";\n\n    if (in_array($_GET['orderField'], $orderFieldWhitelist)) {\n        $orderField = $_GET['orderField'];\n    }\n\n    $dql = \"SELECT u\n              FROM MyProject\\Entity\\User u\n             WHERE u.status = ?1\n         ORDER BY u.\" . $orderField . \" ASC\";\n\n    $query = $entityManager->createQuery($dql);\n    $query->setParameter(1, $_GET['status']);\n\n\nPreventing Mass Assignment Vulnerabilities\n------------------------------------------\n\nORMs are very convenient for CRUD applications and Doctrine is no exception.\nHowever CRUD apps are often vulnerable to mass assignment security problems\nwhen implemented naively.\n\nDoctrine is not vulnerable to this problem out of the box, but you can easily\nmake your entities vulnerable to mass assignment when you add methods of\nthe kind ``updateFromArray()`` or ``updateFromJson()`` to them. A vulnerable\nentity might look like this:\n\n.. code-block:: php\n\n    <?php\n\n    #[Entity]\n    class InsecureEntity\n    {\n        #[Id, Column, GeneratedValue]\n        private int|null $id = null;\n\n        #[Column]\n        private string $email;\n\n        #[Column]\n        private bool $isAdmin;\n\n        /** @param array<string, mixed> $userInput */\n        public function fromArray(array $userInput): void\n        {\n            foreach ($userInput as $key => $value) {\n                $this->$key = $value;\n            }\n        }\n    }\n\nNow the possibility of mass-assignment exists on this entity and can\nbe exploited by attackers to set the \"isAdmin\" flag to true on any\nobject when you pass the whole request data to this method like:\n\n.. code-block:: php\n\n    <?php\n    $entity = new InsecureEntity();\n    $entity->fromArray($_POST);\n\n    $entityManager->persist($entity);\n    $entityManager->flush();\n\nYou can spot this problem in this very simple example easily. However\nin combination with frameworks and form libraries it might not be\nso obvious when this issue arises. Be careful to avoid this\nkind of mistake.\n\nHow to fix this problem? You should always have a whitelist\nof allowed key to set via mass assignment functions.\n\n.. code-block:: php\n\n    public function fromArray(array $userInput, $allowedFields = array())\n    {\n        foreach ($userInput as $key => $value) {\n            if (in_array($key, $allowedFields)) {\n                $this->$key = $value;\n            }\n        }\n    }\n"
  },
  {
    "path": "docs/en/reference/tools.rst",
    "content": "Tools\n=====\n\nDoctrine Console\n----------------\n\nThe Doctrine Console is a Command Line Interface tool for simplifying common\nadministration tasks during the development of a project that uses ORM.\n\nFor the following examples, we will set up the CLI as ``bin/doctrine``.\n\nSetting Up the Console\n~~~~~~~~~~~~~~~~~~~~~~\n\nWhenever the ``doctrine`` command line tool is invoked, it can\naccess all Commands that were registered by a developer. There is no\nauto-detection mechanism at work. The Doctrine binary\nalready registers all the commands that currently ship with\nDoctrine DBAL and ORM. If you want to use additional commands you\nhave to register them yourself.\n\nAll the commands of the Doctrine Console require access to the\n``EntityManager``. You have to inject it into the console application.\n\nHere is an example of a project-specific ``bin/doctrine`` binary.\n\n.. code-block:: php\n\n    #!/usr/bin/env php\n    <?php\n\n    use Doctrine\\ORM\\Tools\\Console\\ConsoleRunner;\n    use Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\n\n    // replace with path to your own project bootstrap file\n    require_once 'bootstrap.php';\n\n    // replace with mechanism to retrieve EntityManager in your app\n    $entityManager = GetEntityManager();\n\n    $commands = [\n        // If you want to add your own custom console commands,\n        // you can do so here.\n    ];\n\n    ConsoleRunner::run(\n        new SingleManagerProvider($entityManager),\n        $commands\n    );\n\n.. note::\n\n    You have to adjust this snippet for your specific application or framework\n    and use their facilities to access the Doctrine EntityManager and\n    Connection Resources.\n\nDisplay Help Information\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nType ``php bin/doctrine`` on the command line and you should see an\noverview of the available commands or use the ``--help`` flag to get\ninformation on the available commands. If you want to know more\nabout the use of generate entities for example, you can call:\n\n::\n\n    $> php bin/doctrine orm:generate-entities --help\n\nCommand Overview\n~~~~~~~~~~~~~~~~\n\nThe following Commands are currently available:\n\n\n-  ``help`` Displays help for a command (?)\n-  ``list`` Lists commands\n-  ``dbal:import`` Import SQL file(s) directly to Database.\n-  ``dbal:run-sql`` Executes arbitrary SQL directly from the\n   command line.\n-  ``orm:clear-cache:metadata`` Clear all metadata cache of the\n   various cache drivers.\n-  ``orm:clear-cache:query`` Clear all query cache of the various\n   cache drivers.\n-  ``orm:clear-cache:result`` Clear result cache of the various\n   cache drivers.\n-  ``orm:generate-proxies`` Generates proxy classes for entity\n   classes. Deprecated in favor of using native lazy objects.\n-  ``orm:run-dql`` Executes arbitrary DQL directly from the command\n   line.\n-  ``orm:schema-tool:create`` Processes the schema and either\n   create it directly on EntityManager Storage Connection or generate\n   the SQL output.\n-  ``orm:schema-tool:drop`` Processes the schema and either drop\n   the database schema of EntityManager Storage Connection or generate\n   the SQL output.\n-  ``orm:schema-tool:update`` Processes the schema and either\n   update the database schema of EntityManager Storage Connection or\n   generate the SQL output.\n-  ``orm:debug:event-manager`` Lists event listeners for an entity\n   manager, optionally filtered by event name.\n-  ``orm:debug:entity-listeners`` Lists entity listeners for a given\n   entity, optionally filtered by event name.\n\nThe following alias is defined:\n\n\n-  ``orm:generate:proxies`` is alias for ``orm:generate-proxies``.\n\n.. note::\n\n    Console also supports auto completion, for example, instead of\n    ``orm:clear-cache:query`` you can use just ``o:c:q``.\n\nDatabase Schema Generation\n--------------------------\n\n.. note::\n\n    SchemaTool can do harm to your database. It will drop or alter\n    tables, indexes, sequences and such. Please use this tool with\n    caution in development and not on a production server. It is meant\n    for helping you develop your Database Schema, but NOT with\n    migrating schema from A to B in production. A safe approach would\n    be generating the SQL on development server and saving it into SQL\n    Migration files that are executed manually on the production\n    server.\n\n    SchemaTool assumes your Doctrine Project uses the given database on\n    its own. Update and Drop commands will mess with other tables if\n    they are not related to the current project that is using Doctrine.\n    Please be careful!\n\n\nTo generate your database schema from your Doctrine mapping files\nyou can use the ``SchemaTool`` class or the ``schema-tool`` Console\nCommand.\n\nWhen using the SchemaTool class directly, create your schema using\nthe ``createSchema()`` method. First create an instance of the\n``SchemaTool`` and pass it an instance of the ``EntityManager``\nthat you want to use to create the schema. This method receives an\narray of ``ClassMetadata`` instances.\n\n.. code-block:: php\n\n    <?php\n    $tool = new \\Doctrine\\ORM\\Tools\\SchemaTool($em);\n    $classes = array(\n      $em->getClassMetadata('Entities\\User'),\n      $em->getClassMetadata('Entities\\Profile')\n    );\n    $tool->createSchema($classes);\n\nTo drop the schema you can use the ``dropSchema()`` method.\n\n.. code-block:: php\n\n    <?php\n    $tool->dropSchema($classes);\n\nThis drops all the tables that are currently used by your metadata\nmodel. When you are changing your metadata a lot during development\nyou might want to drop the complete database instead of only the\ntables of the current model to clean up with orphaned tables.\n\n.. code-block:: php\n\n    <?php\n    $tool->dropSchema($classes, \\Doctrine\\ORM\\Tools\\SchemaTool::DROP_DATABASE);\n\nYou can also use database introspection to update your schema\neasily with the ``updateSchema()`` method. It will compare your\nexisting database schema to the passed array of ``ClassMetadata``\ninstances.\n\n.. code-block:: php\n\n    <?php\n    $tool->updateSchema($classes);\n\nIf you want to use this functionality from the command line you can\nuse the ``schema-tool`` command.\n\nTo create the schema use the ``create`` command:\n\n.. code-block:: php\n\n    $ php bin/doctrine orm:schema-tool:create\n\nTo drop the schema use the ``drop`` command:\n\n.. code-block:: php\n\n    $ php bin/doctrine orm:schema-tool:drop\n\nIf you want to drop and then recreate the schema then use both\noptions:\n\n.. code-block:: php\n\n    $ php bin/doctrine orm:schema-tool:drop\n    $ php bin/doctrine orm:schema-tool:create\n\nAs you would think, if you want to update your schema use the\n``update`` command:\n\n.. code-block:: php\n\n    $ php bin/doctrine orm:schema-tool:update\n\nAll of the above commands also accept a ``--dump-sql`` option that\nwill output the SQL for the ran operation.\n\n.. code-block:: php\n\n    $ php bin/doctrine orm:schema-tool:create --dump-sql\n\nRuntime vs Development Mapping Validation\n-----------------------------------------\n\nFor performance reasons Doctrine ORM has to skip some of the\nnecessary validation of metadata mappings. You have to execute\nthis validation in your development workflow to verify the\nassociations are correctly defined.\n\nYou can either use the Doctrine Command Line Tool:\n\n.. code-block:: php\n\n    doctrine orm:validate-schema\n\nIf the validation fails, you can change the verbosity level to\ncheck the detected errors:\n\n    doctrine orm:validate-schema -v\n\nOr you can trigger the validation manually:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Tools\\SchemaValidator;\n\n    $validator = new SchemaValidator($entityManager);\n    $errors = $validator->validateMapping();\n\n    if (count($errors) > 0) {\n        // Lots of errors!\n        echo implode(\"\\n\\n\", $errors);\n    }\n\nIf the mapping is invalid the errors array contains a positive\nnumber of elements with error messages.\n\n.. warning::\n\n    One mapping option that is not validated is the use of the referenced column name.\n    It has to point to the equivalent primary key otherwise Doctrine will not work.\n\n.. note::\n\n    One common error is to use a backlash in front of the\n    fully-qualified class-name. Whenever a FQCN is represented inside a\n    string (such as in your mapping definitions) you have to drop the\n    prefix backslash. PHP does this with ``get_class()`` or Reflection\n    methods for backwards compatibility reasons.\n\n\nAdding own commands\n-------------------\n\nYou can also add your own commands on-top of the Doctrine supported\ntools if you are using a manually built console script.\n\nTo include a new command on Doctrine Console, you need to do modify the\n``doctrine.php`` file a little:\n\n.. code-block:: php\n\n    <?php\n    // doctrine.php\n    use Symfony\\Component\\Console\\Application;\n\n    // as before ...\n\n    // replace the ConsoleRunner::run() statement with:\n    $cli = new Application('Doctrine Command Line Interface', \\Doctrine\\ORM\\Version::VERSION);\n    $cli->setCatchExceptions(true);\n    $cli->setHelperSet($helperSet);\n\n    // Register All Doctrine Commands\n    ConsoleRunner::addCommands($cli);\n\n    // Register your own command\n    $cli->addCommand(new \\MyProject\\Tools\\Console\\Commands\\MyCustomCommand);\n\n    // Runs console application\n    $cli->run();\n\nAdditionally, include multiple commands (and overriding previously\ndefined ones) is possible through the command:\n\n.. code-block:: php\n\n    <?php\n\n    $cli->addCommands(array(\n        new \\MyProject\\Tools\\Console\\Commands\\MyCustomCommand(),\n        new \\MyProject\\Tools\\Console\\Commands\\SomethingCommand(),\n        new \\MyProject\\Tools\\Console\\Commands\\AnotherCommand(),\n        new \\MyProject\\Tools\\Console\\Commands\\OneMoreCommand(),\n    ));\n\n\nRe-use console application\n--------------------------\n\nYou are also able to retrieve and re-use the default console application.\nJust call ``ConsoleRunner::createApplication(...)`` with an appropriate\nHelperSet, like it is described in the configuration section.\n\n.. code-block:: php\n\n    <?php\n\n    // Retrieve default console application\n    $cli = ConsoleRunner::createApplication($helperSet);\n\n    // Runs console application\n    $cli->run();\n"
  },
  {
    "path": "docs/en/reference/transactions-and-concurrency.rst",
    "content": "Transactions and Concurrency\n============================\n\n.. _transactions-and-concurrency_transaction-demarcation:\n\nTransaction Demarcation\n-----------------------\n\nTransaction demarcation is the task of defining your transaction\nboundaries. Proper transaction demarcation is very important\nbecause if not done properly it can negatively affect the\nperformance of your application. Many databases and database\nabstraction layers like PDO by default operate in auto-commit mode,\nwhich means that every single SQL statement is wrapped in a small\ntransaction. Without any explicit transaction demarcation from your\nside, this quickly results in poor performance because transactions\nare not cheap.\n\nFor the most part, Doctrine ORM already takes care of proper\ntransaction demarcation for you: All the write operations\n(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()``\nis invoked which wraps all of these changes in a single\ntransaction.\n\nHowever, Doctrine ORM also allows (and encourages) you to take over\nand control transaction demarcation yourself.\n\nThese are two ways to deal with transactions when using the\nDoctrine ORM and are now described in more detail.\n\n.. _transactions-and-concurrency_approach-implicitly:\n\nApproach 1: Implicitly\n~~~~~~~~~~~~~~~~~~~~~~\n\nThe first approach is to use the implicit transaction handling\nprovided by the Doctrine ORM EntityManager. Given the following\ncode snippet, without any explicit transaction demarcation:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n    $user = new User;\n    $user->setName('George');\n    $em->persist($user);\n    $em->flush();\n\nSince we do not do any custom transaction demarcation in the above\ncode, ``EntityManager#flush()`` will begin and commit/rollback a\ntransaction. This behavior is made possible by the aggregation of\nthe DML operations by the Doctrine ORM and is sufficient if all the\ndata manipulation that is part of a unit of work happens through\nthe domain model and thus the ORM.\n\n.. _transactions-and-concurrency_approach-explicitly:\n\nApproach 2: Explicitly\n~~~~~~~~~~~~~~~~~~~~~~\n\nThe explicit alternative is to use the ``Doctrine\\DBAL\\Connection``\nAPI directly to control the transaction boundaries. The code then\nlooks like this:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n    $em->getConnection()->beginTransaction(); // suspend auto-commit\n    try {\n        // ... do some work\n        $user = new User;\n        $user->setName('George');\n        $em->persist($user);\n        $em->flush();\n        $em->getConnection()->commit();\n    } catch (Exception $e) {\n        $em->getConnection()->rollBack();\n        throw $e;\n    }\n\nExplicit transaction demarcation is required when you want to\ninclude custom DBAL operations in a unit of work or when you want\nto make use of some methods of the ``EntityManager`` API that\nrequire an active transaction. Such methods will throw a\n``TransactionRequiredException`` to inform you of that\nrequirement.\n\nA more convenient alternative for explicit transaction demarcation is the use\nof provided control abstractions in the form of\n``Connection#transactional($func)`` and ``EntityManager#wrapInTransaction($func)``.\nWhen used, these control abstractions ensure that you never forget to rollback\nthe transaction, in addition to the obvious code reduction. An example that is\nfunctionally equivalent to the previously shown code looks as follows:\n\n.. code-block:: php\n\n    <?php\n    // transactional with Connection instance\n    // $conn instanceof Connection\n    $conn->transactional(function($conn) {\n        // ... do some work\n        $user = new User;\n        $user->setName('George');\n    });\n\n    // transactional with EntityManager instance\n    // $em instanceof EntityManager\n    $em->wrapInTransaction(function($em) {\n        // ... do some work\n        $user = new User;\n        $user->setName('George');\n        $em->persist($user);\n    });\n\nThe difference between ``Connection#transactional($func)`` and\n``EntityManager#transactional($func)`` is that the latter\nabstraction flushes the ``EntityManager`` prior to transaction\ncommit and in case of an exception the ``EntityManager`` gets closed\nin addition to the transaction rollback.\n\n.. _transactions-and-concurrency_exception-handling:\n\nException Handling\n~~~~~~~~~~~~~~~~~~\n\nWhen using implicit transaction demarcation and an exception occurs\nduring ``EntityManager#flush()``, the transaction is automatically\nrolled back and the ``EntityManager`` closed.\n\nWhen using explicit transaction demarcation and an exception\noccurs, the transaction should be rolled back immediately and the\n``EntityManager`` closed by invoking ``EntityManager#close()`` and\nsubsequently discarded, as demonstrated in the example above. This\ncan be handled elegantly by the control abstractions shown earlier.\nNote that when catching ``Exception`` you should generally re-throw\nthe exception. If you intend to recover from some exceptions, catch\nthem explicitly in earlier catch blocks (but do not forget to\nrollback the transaction and close the ``EntityManager`` there as\nwell). All other best practices of exception handling apply\nsimilarly (i.e. either log or re-throw, not both, etc.).\n\nAs a result of this procedure, all previously managed or removed\ninstances of the ``EntityManager`` become detached. The state of\nthe detached objects will be the state at the point at which the\ntransaction was rolled back. The state of the objects is in no way\nrolled back and thus the objects are now out of synch with the\ndatabase. The application can continue to use the detached objects,\nknowing that their state is potentially no longer accurate.\n\nIf you intend to start another unit of work after an exception has\noccurred you should do that with a new ``EntityManager``.\n\n.. _transactions-and-concurrency_locking-support:\n\nLocking Support\n---------------\n\nDoctrine ORM offers support for Pessimistic- and Optimistic-locking\nstrategies natively. This allows to take very fine-grained control\nover what kind of locking is required for your Entities in your\napplication.\n\n.. _transactions-and-concurrency_optimistic-locking:\n\nOptimistic Locking\n~~~~~~~~~~~~~~~~~~\n\nDatabase transactions are fine for concurrency control during a\nsingle request. However, a database transaction should not span\nacross requests, the so-called \"user think time\". Therefore a\nlong-running \"business transaction\" that spans multiple requests\nneeds to involve several database transactions. Thus, database\ntransactions alone can no longer control concurrency during such a\nlong-running business transaction. Concurrency control becomes the\npartial responsibility of the application itself.\n\nDoctrine has integrated support for automatic optimistic locking\nvia a version field. In this approach any entity that should be\nprotected against concurrent modifications during long-running\nbusiness transactions gets a version field that is either a simple\nnumber (mapping type: integer) or a timestamp (mapping type:\ndatetime). When changes to such an entity are persisted at the end\nof a long-running conversation the version of the entity is\ncompared to the version in the database and if they don't match, an\n``OptimisticLockException`` is thrown, indicating that the entity\nhas been modified by someone else already.\n\nYou designate a version field in an entity as follows. In this\nexample we'll use an integer.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class User\n        {\n            // ...\n            #[Version, Column(type: 'integer')]\n            private int $version;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"User\">\n            <field name=\"version\" type=\"integer\" version=\"true\" />\n          </entity>\n        </doctrine-mapping>\n\nAlternatively a datetime type can be used (which maps to a SQL\ntimestamp or datetime):\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        class User\n        {\n            // ...\n            #[Version, Column(type: 'datetime')]\n            private DateTime $version;\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"User\">\n            <field name=\"version\" type=\"datetime\" version=\"true\" />\n          </entity>\n        </doctrine-mapping>\n\nVersion numbers (not timestamps) should however be preferred as\nthey can not potentially conflict in a highly concurrent\nenvironment, unlike timestamps where this is a possibility,\ndepending on the resolution of the timestamp on the particular\ndatabase platform.\n\nWhen a version conflict is encountered during\n``EntityManager#flush()``, an ``OptimisticLockException`` is thrown\nand the active transaction rolled back (or marked for rollback).\nThis exception can be caught and handled. Potential responses to an\nOptimisticLockException are to present the conflict to the user or\nto refresh or reload objects in a new transaction and then retrying\nthe transaction.\n\nWith PHP promoting a share-nothing architecture, the time between\nshowing an update form and actually modifying the entity can in the\nworst scenario be as long as your applications session timeout. If\nchanges happen to the entity in that time frame you want to know\ndirectly when retrieving the entity that you will hit an optimistic\nlocking exception:\n\nYou can always verify the version of an entity during a request\neither when calling ``EntityManager#find()``:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\DBAL\\LockMode;\n    use Doctrine\\ORM\\OptimisticLockException;\n\n    $theEntityId = 1;\n    $expectedVersion = 184;\n\n    try {\n        $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);\n\n        // do the work\n\n        $em->flush();\n    } catch(OptimisticLockException $e) {\n        echo \"Sorry, but someone else has already changed this entity. Please apply the changes again!\";\n    }\n\nOr you can use ``EntityManager#lock()`` to find out:\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\DBAL\\LockMode;\n    use Doctrine\\ORM\\OptimisticLockException;\n\n    $theEntityId = 1;\n    $expectedVersion = 184;\n\n    $entity = $em->find('User', $theEntityId);\n\n    try {\n        // assert version\n        $em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion);\n\n    } catch(OptimisticLockException $e) {\n        echo \"Sorry, but someone else has already changed this entity. Please apply the changes again!\";\n    }\n\nImportant Implementation Notes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can easily get the optimistic locking workflow wrong if you\ncompare the wrong versions. Say you have Alice and Bob editing a\nhypothetical blog post:\n\n-  Alice reads the headline of the blog post being \"Foo\", at\n   optimistic lock version 1 (GET Request)\n-  Bob reads the headline of the blog post being \"Foo\", at\n   optimistic lock version 1 (GET Request)\n-  Bob updates the headline to \"Bar\", upgrading the optimistic lock\n   version to 2 (POST Request of a Form)\n-  Alice updates the headline to \"Baz\", ... (POST Request of a\n   Form)\n\nNow at the last stage of this scenario the blog post has to be read\nagain from the database before Alice's headline can be applied. At\nthis point you will want to check if the blog post is still at\nversion 1 (which it is not in this scenario).\n\nUsing optimistic locking correctly, you *have* to add the version\nas an additional hidden field (or into the SESSION for more\nsafety). Otherwise you cannot verify the version is still the one\nbeing originally read from the database when Alice performed her\nGET request for the blog post. If this happens you might see lost\nupdates you wanted to prevent with Optimistic Locking.\n\nSee the example code, The form (GET Request):\n\n.. code-block:: php\n\n    <?php\n    $post = $em->find('BlogPost', 123456);\n\n    echo '<input type=\"hidden\" name=\"id\" value=\"' . $post->getId() . '\" />';\n    echo '<input type=\"hidden\" name=\"version\" value=\"' . $post->getCurrentVersion() . '\" />';\n\nAnd the change headline action (POST Request):\n\n.. code-block:: php\n\n    <?php\n    $postId = (int)$_GET['id'];\n    $postVersion = (int)$_GET['version'];\n\n    $post = $em->find('BlogPost', $postId, \\Doctrine\\DBAL\\LockMode::OPTIMISTIC, $postVersion);\n\n.. _transactions-and-concurrency_pessimistic-locking:\n\nPessimistic Locking\n~~~~~~~~~~~~~~~~~~~\n\nDoctrine ORM supports Pessimistic Locking at the database level. No\nattempt is being made to implement pessimistic locking inside\nDoctrine, rather vendor-specific and ANSI-SQL commands are used to\nacquire row-level locks. Every Entity can be part of a pessimistic\nlock, there is no special metadata required to use this feature.\n\nHowever for Pessimistic Locking to work you have to disable the\nAuto-Commit Mode of your Database and start a transaction around\nyour pessimistic lock use-case using the \"Approach 2: Explicit\nTransaction Demarcation\" described above. Doctrine ORM will throw an\nException if you attempt to acquire an pessimistic lock and no\ntransaction is running.\n\nDoctrine ORM currently supports two pessimistic lock modes:\n\n\n-  Pessimistic Write\n   (``Doctrine\\DBAL\\LockMode::PESSIMISTIC_WRITE``), locks the\n   underlying database rows for concurrent Read and Write Operations.\n-  Pessimistic Read (``Doctrine\\DBAL\\LockMode::PESSIMISTIC_READ``),\n   locks other concurrent requests that attempt to update or lock rows\n   in write mode.\n\nYou can use pessimistic locks in four different scenarios:\n\n\n1. Using\n   ``EntityManager#find($className, $id, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_WRITE)``\n   or\n   ``EntityManager#find($className, $id, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_READ)``\n2. Using\n   ``EntityManager#lock($entity, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_WRITE)``\n   or\n   ``EntityManager#lock($entity, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_READ)``\n3. Using\n   ``EntityManager#refresh($entity, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_WRITE)``\n   or\n   ``EntityManager#refresh($entity, \\Doctrine\\DBAL\\LockMode::PESSIMISTIC_READ)``\n4. Using\n   ``Query#setLockMode(\\Doctrine\\DBAL\\LockMode::PESSIMISTIC_WRITE)``\n   or\n   ``Query#setLockMode(\\Doctrine\\DBAL\\LockMode::PESSIMISTIC_READ)``\n"
  },
  {
    "path": "docs/en/reference/typedfieldmapper.rst",
    "content": "Implementing a TypedFieldMapper\n===============================\n\n.. versionadded:: 2.14\n\nYou can specify custom typed field mapping between PHP type and DBAL type using ``Doctrine\\ORM\\Configuration``\nand a custom ``Doctrine\\ORM\\Mapping\\TypedFieldMapper`` implementation.\n\n.. code-block:: php\n\n    <?php\n    $configuration->setTypedFieldMapper(new CustomTypedFieldMapper());\n\n\nDefaultTypedFieldMapper\n-----------------------\n\nBy default the ``Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper`` is used, and you can pass an array of\nPHP type => DBAL type mappings into its constructor to override the default behavior or add new mappings.\n\n.. code-block:: php\n\n    <?php\n    use App\\CustomIds\\CustomIdObject;\n    use App\\DBAL\\Type\\CustomIdObjectType;\n    use Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\n\n    $configuration->setTypedFieldMapper(new DefaultTypedFieldMapper([\n        CustomIdObject::class => CustomIdObjectType::class,\n    ]));\n\nThen, an entity using the ``CustomIdObject`` typed field will be correctly assigned its DBAL type\n(``CustomIdObjectType``) without the need of explicit declaration.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[ORM\\Entity]\n        #[ORM\\Table(name: 'cms_users_typed_with_custom_typed_field')]\n        class UserTypedWithCustomTypedField\n        {\n            #[ORM\\Column]\n            public CustomIdObject $customId;\n\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n          <entity name=\"UserTypedWithCustomTypedField\">\n            <field name=\"customId\"/>\n            <!-- -->\n          </entity>\n        </doctrine-mapping>\n\n    .. code-block:: yaml\n\n        UserTypedWithCustomTypedField:\n          type: entity\n          fields:\n            customId: ~\n\nIt is perfectly valid to override even the \"automatic\" mapping rules mentioned above:\n\n.. code-block:: php\n\n    <?php\n    use App\\DBAL\\Type\\CustomIntType;\n    use Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\n\n    $configuration->setTypedFieldMapper(new DefaultTypedFieldMapper([\n        'int' => CustomIntType::class,\n    ]));\n\n.. note::\n\n    If chained, once the first ``TypedFieldMapper`` assigns a type to a field, the ``DefaultTypedFieldMapper`` will\n    ignore its mapping and not override it anymore (if it is later in the chain). See below for chaining type mappers.\n\n\nTypedFieldMapper interface\n-------------------------\nThe interface ``Doctrine\\ORM\\Mapping\\TypedFieldMapper`` allows you to implement your own\ntyped field mapping logic. It consists of just one function\n\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Validates & completes the given field mapping based on typed property.\n     *\n     * @param array{fieldName: string, enumType?: string, type?: mixed}  $mapping The field mapping to validate & complete.\n     * @param \\ReflectionProperty                                        $field\n     *\n     * @return array{fieldName: string, enumType?: string, type?: mixed} The updated mapping.\n     */\n    public function validateAndComplete(array $mapping, ReflectionProperty $field): array;\n\n\nChainTypedFieldMapper\n---------------------\n\nThe class ``Doctrine\\ORM\\Mapping\\ChainTypedFieldMapper`` allows you to chain multiple ``TypedFieldMapper`` instances.\nWhen being evaluated, the ``TypedFieldMapper::validateAndComplete`` is called in the order in which\nthe instances were supplied to the ``ChainTypedFieldMapper`` constructor.\n\n.. code-block:: php\n\n    <?php\n    use App\\DBAL\\Type\\CustomIntType;\n    use Doctrine\\ORM\\Mapping\\ChainTypedFieldMapper;\n    use Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\n\n    $configuration->setTypedFieldMapper(\n        new ChainTypedFieldMapper(\n            new DefaultTypedFieldMapper(['int' => CustomIntType::class,]),\n            new CustomTypedFieldMapper()\n        )\n    );\n\n\nImplementing a TypedFieldMapper\n-------------------------------\n\nIf you want to assign all ``BackedEnum`` fields to your custom ``BackedEnumDBALType`` or you want to use different\nDBAL types based on whether the entity field is nullable or not, you can achieve this by implementing your own\ntyped field mapper.\n\nYou need to create a class which implements ``Doctrine\\ORM\\Mapping\\TypedFieldMapper``.\n\n.. code-block:: php\n\n    <?php\n    final class CustomEnumTypedFieldMapper implements TypedFieldMapper\n    {\n        /**\n         * {@inheritDoc}\n         */\n        public function validateAndComplete(array $mapping, ReflectionProperty $field): array\n        {\n            $type = $field->getType();\n\n            if (\n                ! isset($mapping['type'])\n                && ($type instanceof ReflectionNamedType)\n            ) {\n                if (! $type->isBuiltin() && enum_exists($type->getName())) {\n                    $mapping['type'] = BackedEnumDBALType::class;\n                }\n            }\n\n            return $mapping;\n        }\n    }\n\n.. note::\n\n    Note that this case checks whether the mapping is already assigned, and if yes, it skips it. This is up to your\n    implementation. You can make a \"greedy\" mapper which will always override the mapping with its own type, or one\n    that behaves like the ``DefaultTypedFieldMapper`` and does not modify the type once its set prior in the chain.\n"
  },
  {
    "path": "docs/en/reference/unitofwork-associations.rst",
    "content": "Association Updates: Owning Side and Inverse Side\n=================================================\n\nWhen mapping bidirectional associations it is important to\nunderstand the concept of the owning and inverse sides. The\nfollowing general rules apply:\n\n-  Relationships may be bidirectional or unidirectional.\n-  A bidirectional relationship has both an owning side and an inverse side\n-  A unidirectional relationship only has an owning side.\n-  Doctrine will **only** check the owning side of an association for changes.\n\nBidirectional Associations\n--------------------------\n\nThe following rules apply to **bidirectional** associations:\n\n- The inverse side has to have the ``mappedBy`` attribute of the OneToOne,\n  OneToMany, or ManyToMany mapping declaration. The ``mappedBy``\n  attribute contains the name of the association-field on the owning side.\n- The owning side has to have the ``inversedBy`` attribute of the\n  OneToOne, ManyToOne, or ManyToMany mapping declaration.\n  The ``inversedBy`` attribute contains the name of the association-field\n  on the inverse-side.\n- ManyToOne is always the owning side of a bidirectional association.\n- OneToMany is always the inverse side of a bidirectional association.\n- The owning side of a OneToOne association is the entity with the table\n  containing the foreign key.\n- You can pick the owning side of a many-to-many association yourself.\n\nImportant concepts\n------------------\n\n**Doctrine will only check the owning side of an association for changes.**\n\nTo fully understand this, remember how bidirectional associations\nare maintained in the object world. There are 2 references on each\nside of the association and these 2 references both represent the\nsame association but can change independently of one another. Of\ncourse, in a correct application the semantics of the bidirectional\nassociation are properly maintained by the application developer\n(that's their responsibility). Doctrine needs to know which of these\ntwo in-memory references is the one that should be persisted and\nwhich not. This is what the owning/inverse concept is mainly used\nfor.\n\n**Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine's point of view)**\n\nThe owning side of a bidirectional association is the side Doctrine\n\"looks at\" when determining the state of the association, and\nconsequently whether there is anything to do to update the\nassociation in the database.\n\n.. note::\n\n    \"Owning side\" and \"inverse side\" are technical concepts of\n    the ORM technology, not concepts of your domain model. What you\n    consider as the owning side in your domain model can be different\n    from what the owning side is for Doctrine. These are unrelated.\n\n"
  },
  {
    "path": "docs/en/reference/unitofwork.rst",
    "content": "Doctrine Internals explained\n============================\n\nObject relational mapping is a complex topic and sufficiently understanding how Doctrine works internally helps you use its full power.\n\nHow Doctrine keeps track of Objects\n-----------------------------------\n\nDoctrine uses the Identity Map pattern to track objects. Whenever you fetch an\nobject from the database, Doctrine will keep a reference to this object inside\nits UnitOfWork. The array holding all the entity references is two-levels deep\nand has the keys \"root entity name\" and \"id\". Since Doctrine allows composite\nkeys the id is a sorted, serialized version of all the key columns.\n\nThis allows Doctrine room for optimizations. If you call the EntityManager and\nask for an entity with a specific ID twice, it will return the same instance:\n\n.. code-block:: php\n\n    public function testIdentityMap(): void\n    {\n        $objectA = $this->entityManager->find('EntityName', 1);\n        $objectB = $this->entityManager->find('EntityName', 1);\n\n        $this->assertSame($objectA, $objectB)\n    }\n\nOnly one SELECT query will be fired against the database here. In the second\n``EntityManager#find()`` call Doctrine will check the identity map first and\ndoesn't need to make that database roundtrip.\n\nEven if you get a proxy object first then fetch the object by the same id you\nwill still end up with the same reference:\n\n.. code-block:: php\n\n    public function testIdentityMapReference(): void\n    {\n        $objectA = $this->entityManager->getReference('EntityName', 1);\n        // check entity is not initialized\n        $this->assertTrue($this->entityManager->isUninitializedObject($objectA));\n\n        $objectB = $this->entityManager->find('EntityName', 1);\n\n        $this->assertSame($objectA, $objectB)\n    }\n\nThe identity map being indexed by primary keys only allows shortcuts when you\nask for objects by primary key. Assume you have the following ``persons``\ntable:\n\n::\n\n    id | name\n    -------------\n    1  | Benjamin\n    2  | Bud\n\nTake the following example where two\nconsecutive calls are made against a repository to fetch an entity by a set of\ncriteria:\n\n.. code-block:: php\n\n    public function testIdentityMapRepositoryFindBy()\n    {\n        $repository = $this->entityManager->getRepository('Person');\n        $objectA = $repository->findOneBy(array('name' => 'Benjamin'));\n        $objectB = $repository->findOneBy(array('name' => 'Benjamin'));\n\n        $this->assertSame($objectA, $objectB);\n    }\n\nThis query will still return the same references and `$objectA` and `$objectB`\nare indeed referencing the same object. However when checking your SQL logs you\nwill realize that two queries have been executed against the database. Doctrine\nonly knows objects by id, so a query for different criteria has to go to the\ndatabase, even if it was executed just before.\n\nBut instead of creating a second Person object Doctrine first gets the primary\nkey from the row and check if it already has an object inside the UnitOfWork\nwith that primary key. In our example it finds an object and decides to return\nthis instead of creating a new one.\n\nThe identity map has a second use-case. When you call ``EntityManager#flush``\nDoctrine will ask the identity map for all objects that are currently managed.\nThis means you don't have to call ``EntityManager#persist`` over and over again\nto pass known objects to the EntityManager. This is a NO-OP for known entities,\nbut leads to much code written that is confusing to other developers.\n\nThe following code WILL update your database with the changes made to the\n``Person`` object, even if you did not call ``EntityManager#persist``:\n\n.. code-block:: php\n\n    <?php\n    $user = $entityManager->find(\"Person\", 1);\n    $user->setName(\"Guilherme\");\n    $entityManager->flush();\n\nHow Doctrine Detects Changes\n----------------------------\n\nDoctrine is a data-mapper that tries to achieve persistence-ignorance (PI).\nThis means you map PHP objects into a relational database that don't\nnecessarily know about the database at all. A natural question would now be,\n\"how does Doctrine even detect objects have changed?\".\n\nFor this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch\nan object from the database Doctrine will keep a copy of all the properties and\nassociations inside the UnitOfWork. Because variables in the PHP language are\nsubject to \"copy-on-write\" the memory usage of a PHP request that only reads\nobjects from the database is the same as if Doctrine did not keep this variable\ncopy. Only if you start changing variables PHP will create new variables internally\nthat consume new memory.\n\nNow whenever you call ``EntityManager#flush`` Doctrine will iterate over the\nIdentity Map and for each object compares the original property and association\nvalues with the values that are currently set on the object. If changes are\ndetected then the object is queued for a SQL UPDATE operation. Only the fields\nthat actually changed are updated.\n\nThis process has an obvious performance impact. The larger the size of the\nUnitOfWork is, the longer this computation takes. There are several ways to\noptimize the performance of the Flush Operation:\n\n- Mark entities as read only. These entities can only be inserted or removed,\n  but are never updated. They are omitted in the changeset calculation.\n- Temporarily mark entities as read only. If you have a very large UnitOfWork\n  but know that a large set of entities has not changed, just mark them as read\n  only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``.\n- Use :doc:`Change Tracking Policies <change-tracking-policies>` to use more\n  explicit strategies of notifying the UnitOfWork what objects/properties\n  changed.\n\nQuery Internals\n---------------\n\nThe different ORM Layers\n------------------------\n\nDoctrine ships with a set of layers with different responsibilities. This\nsection gives a short explanation of each layer.\n\nHydration\n~~~~~~~~~\n\nResponsible for creating a final result from a raw database statement and a\nresult-set mapping object. The developer can choose which kind of result they\nwish to be hydrated. Default result-types include:\n\n- SQL to Entities\n- SQL to structured Arrays\n- SQL to simple scalar result arrays\n- SQL to a single result variable\n\nHydration to entities and arrays is one of the most complex parts of Doctrine\nalgorithm-wise. It can build results with for example:\n\n- Single table selects\n- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent.\n- Mixed results of objects and scalar values\n- Hydration of results by a given scalar value as key.\n\nPersisters\n~~~~~~~~~~\n\ntbr\n\nUnitOfWork\n~~~~~~~~~~\n\ntbr\n\nResultSetMapping\n~~~~~~~~~~~~~~~~\n\ntbr\n\nDQL Parser\n~~~~~~~~~~\n\ntbr\n\nSQLWalker\n~~~~~~~~~\n\ntbr\n\nEntityManager\n~~~~~~~~~~~~~\n\ntbr\n\nClassMetadataFactory\n~~~~~~~~~~~~~~~~~~~~\n\ntbr\n"
  },
  {
    "path": "docs/en/reference/working-with-associations.rst",
    "content": "Working with Associations\n=========================\n\nAssociations between entities are represented just like in regular\nobject-oriented PHP code using references to other objects or\ncollections of objects.\n\nChanges to associations in your code are not synchronized to the\ndatabase directly, only when calling ``EntityManager#flush()``.\n\nThere are other concepts you should know about when working\nwith associations in Doctrine:\n\n-  If an entity is removed from a collection, the association is\n   removed, not the entity itself. A collection of entities always\n   only represents the association to the containing entities, not the\n   entity itself.\n-  When a bidirectional association is updated, Doctrine only checks\n   on one of both sides for these changes. This is called the :doc:`owning side <unitofwork-associations>`\n   of the association.\n-  A property with a reference to many entities has to be instances of the\n   ``Doctrine\\Common\\Collections\\Collection`` interface.\n\nAssociation Example Entities\n----------------------------\n\nWe will use a simple comment system with Users and Comments as\nentities to show examples of association management. See the PHP\ndocblocks of each association in the following example for\ninformation about its type and if it's the owning or inverse side.\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class User\n    {\n        #[Id, GeneratedValue, Column]\n        private int|null $id = null;\n\n        /**\n         * Bidirectional - Many users have Many favorite comments (OWNING SIDE)\n         *\n         * @var Collection<int, Comment>\n         */\n        #[ManyToMany(targetEntity: Comment::class, inversedBy: 'userFavorites')]\n        #[JoinTable(name: 'user_favorite_comments')]\n        private Collection $favorites;\n\n        /**\n         * Unidirectional - Many users have marked many comments as read\n         *\n         * @var Collection<int, Comment>\n         */\n        #[ManyToMany(targetEntity: Comment::class)]\n        #[JoinTable(name: 'user_read_comments')]\n        private Collection $commentsRead;\n\n        /**\n         * Bidirectional - One-To-Many (INVERSE SIDE)\n         *\n         * @var Collection<int, Comment>\n         */\n        #[OneToMany(targetEntity: Comment::class, mappedBy: 'author')]\n        private Collection $commentsAuthored;\n\n        /** Unidirectional - Many-To-One */\n        #[ManyToOne(targetEntity: Comment::class)]\n        private Comment|null $firstComment = null;\n    }\n\n    #[Entity]\n    class Comment\n    {\n        #[Id, GeneratedValue, Column]\n        private string $id;\n\n        /**\n         * Bidirectional - Many comments are favorited by many users (INVERSE SIDE)\n         *\n         * @var Collection<int, User>\n         */\n        #[ManyToMany(targetEntity: User::class, mappedBy: 'favorites')]\n        private Collection $userFavorites;\n\n        /**\n         * Bidirectional - Many Comments are authored by one user (OWNING SIDE)\n         */\n        #[ManyToOne(targetEntity: User::class, inversedBy: 'commentsAuthored')]\n        private User|null $author = null;\n    }\n\nThis two entities generate the following MySQL Schema (Foreign Key\ndefinitions omitted):\n\n.. code-block:: sql\n\n    CREATE TABLE User (\n        id VARCHAR(255) NOT NULL,\n        firstComment_id VARCHAR(255) DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE Comment (\n        id VARCHAR(255) NOT NULL,\n        author_id VARCHAR(255) DEFAULT NULL,\n        PRIMARY KEY(id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE user_favorite_comments (\n        user_id VARCHAR(255) NOT NULL,\n        favorite_comment_id VARCHAR(255) NOT NULL,\n        PRIMARY KEY(user_id, favorite_comment_id)\n    ) ENGINE = InnoDB;\n\n    CREATE TABLE user_read_comments (\n        user_id VARCHAR(255) NOT NULL,\n        comment_id VARCHAR(255) NOT NULL,\n        PRIMARY KEY(user_id, comment_id)\n    ) ENGINE = InnoDB;\n\nEstablishing Associations\n-------------------------\n\nEstablishing an association between two entities is\nstraight-forward. Here are some examples for the unidirectional\nrelations of the ``User``:\n\n.. code-block:: php\n\n    <?php\n    class User\n    {\n        // ...\n        /** @return Collection<int, Comment> */\n        public function getReadComments(): Collection {\n             return $this->commentsRead;\n        }\n\n        public function setFirstComment(Comment $c): void {\n            $this->firstComment = $c;\n        }\n    }\n\nThe interaction code would then look like in the following snippet\n(``$em`` here is an instance of the EntityManager):\n\n.. code-block:: php\n\n    <?php\n    $user = $em->find('User', $userId);\n\n    // unidirectional many to many\n    $comment = $em->find('Comment', $readCommentId);\n    $user->getReadComments()->add($comment);\n\n    $em->flush();\n\n    // unidirectional many to one\n    $myFirstComment = new Comment();\n    $user->setFirstComment($myFirstComment);\n\n    $em->persist($myFirstComment);\n    $em->flush();\n\nIn the case of bi-directional associations you have to update the\nfields on both sides:\n\n.. code-block:: php\n\n    <?php\n    class User\n    {\n        // ..\n\n        /** @return Collection<int, Comment> */\n        public function getAuthoredComments(): Collection {\n            return $this->commentsAuthored;\n        }\n\n        /** @return Collection<int, Comment> */\n        public function getFavoriteComments(): Collection {\n            return $this->favorites;\n        }\n    }\n\n    class Comment\n    {\n        // ...\n\n        /** @return Collection<int, User> */\n        public function getUserFavorites(): Collection {\n            return $this->userFavorites;\n        }\n\n        public function setAuthor(User|null $author = null): void {\n            $this->author = $author;\n        }\n    }\n\n    // Many-to-Many\n    $user->getFavorites()->add($favoriteComment);\n    $favoriteComment->getUserFavorites()->add($user);\n\n    $em->flush();\n\n    // Many-To-One / One-To-Many Bidirectional\n    $newComment = new Comment();\n    $user->getAuthoredComments()->add($newComment);\n    $newComment->setAuthor($user);\n\n    $em->persist($newComment);\n    $em->flush();\n\nNotice how always both sides of the bidirectional association are\nupdated. The previous unidirectional associations were simpler to\nhandle.\n\nRemoving Associations\n---------------------\n\nRemoving an association between two entities is similarly\nstraight-forward. There are two strategies to do so, by key and by\nelement. Here are some examples:\n\n.. code-block:: php\n\n    <?php\n    // Remove by Elements\n    $user->getComments()->removeElement($comment);\n    $comment->setAuthor(null);\n\n    $user->getFavorites()->removeElement($comment);\n    $comment->getUserFavorites()->removeElement($user);\n\n    // Remove by Key\n    $user->getComments()->remove($ithComment);\n    $comment->setAuthor(null);\n\nYou need to call ``$em->flush()`` to make persist these changes in\nthe database permanently.\n\nNotice how both sides of the bidirectional association are always\nupdated. Unidirectional associations are consequently simpler to\nhandle.\n\nAlso note that if you use type-hinting in your methods, you will\nhave to specify a nullable type, i.e. ``setAddress(?Address $address)``,\notherwise ``setAddress(null)`` will fail to remove the association.\nAnother way to deal with this is to provide a special method, like\n``removeAddress()``. This can also provide better encapsulation as\nit hides the internal meaning of not having an address.\n\nWhen working with collections, keep in mind that a Collection is\nessentially an ordered map (just like a PHP array). That is why the\n``remove`` operation accepts an index/key. ``removeElement`` is a\nseparate method that has O(n) complexity using ``array_search``,\nwhere n is the size of the map.\n\n.. note::\n\n    Since Doctrine always only looks at the owning side of a\n    bidirectional association for updates, it is not necessary for\n    write operations that an inverse collection of a bidirectional\n    one-to-many or many-to-many association is updated. This knowledge\n    can often be used to improve performance by avoiding the loading of\n    the inverse collection.\n\n\nYou can also clear the contents of a whole collection using the\n``Collections::clear()`` method. You should be aware that using\nthis method can lead to a straight and optimized database delete or\nupdate call during the flush operation that is not aware of\nentities that have been re-added to the collection.\n\nSay you clear a collection of tags by calling\n``$post->getTags()->clear();`` and then call\n``$post->getTags()->add($tag)``. This will not recognize the tag having\nalready been added previously and will consequently issue two separate database\ncalls.\n\nAssociation Management Methods\n------------------------------\n\nIt is generally a good idea to encapsulate proper association\nmanagement inside the entity classes. This makes it easier to use\nthe class correctly and can encapsulate details about how the\nassociation is maintained.\n\nThe following code shows updates to the previous User and Comment\nexample that encapsulate much of the association management code:\n\n.. code-block:: php\n\n    <?php\n    class User\n    {\n        // ...\n        public function markCommentRead(Comment $comment): void {\n            // Collections implement ArrayAccess\n            $this->commentsRead[] = $comment;\n        }\n\n        public function addComment(Comment $comment): void {\n            if (count($this->commentsAuthored) == 0) {\n                $this->setFirstComment($comment);\n            }\n            $this->comments[] = $comment;\n            $comment->setAuthor($this);\n        }\n\n        private function setFirstComment(Comment $c): void {\n            $this->firstComment = $c;\n        }\n\n        public function addFavorite(Comment $comment): void {\n            $this->favorites->add($comment);\n            $comment->addUserFavorite($this);\n        }\n\n        public function removeFavorite(Comment $comment): void {\n            $this->favorites->removeElement($comment);\n            $comment->removeUserFavorite($this);\n        }\n    }\n\n    class Comment\n    {\n        // ..\n\n        public function addUserFavorite(User $user): void {\n            $this->userFavorites[] = $user;\n        }\n\n        public function removeUserFavorite(User $user): void {\n            $this->userFavorites->removeElement($user);\n        }\n    }\n\nYou will notice that ``addUserFavorite`` and ``removeUserFavorite``\ndo not call ``addFavorite`` and ``removeFavorite``, thus the\nbidirectional association is strictly-speaking still incomplete.\nHowever if you would naively add the ``addFavorite`` in\n``addUserFavorite``, you end up with an infinite loop, so more work\nis needed. As you can see, proper bidirectional association\nmanagement in plain OOP is a non-trivial task and encapsulating all\nthe details inside the classes can be challenging.\n\n.. note::\n\n    If you want to make sure that your collections are perfectly\n    encapsulated you should not return them from a\n    ``getCollectionName()`` method directly, but call\n    ``$collection->toArray()``. This way a client programmer for the\n    entity cannot circumvent the logic you implement on your entity for\n    association management. For example:\n\n\n.. code-block:: php\n\n    <?php\n    class User {\n        /** @return array<int, Comment> */\n        public function getReadComments(): array {\n            return $this->commentsRead->toArray();\n        }\n    }\n\nThis will however always initialize the collection, with all the\nperformance penalties given the size. In some scenarios of large\ncollections it might even be a good idea to completely hide the\nread access behind methods on the EntityRepository.\n\nThere is no single, best way for association management. It greatly\ndepends on the requirements of your concrete domain model as well\nas your preferences.\n\nSynchronizing Bidirectional Collections\n---------------------------------------\n\nIn the case of Many-To-Many associations you as the developer have the\nresponsibility of keeping the collections on the owning and inverse side\nin sync when you apply changes to them. Doctrine can only\nguarantee a consistent state for the hydration, not for your client\ncode.\n\nUsing the User-Comment entities from above, a very simple example\ncan show the possible caveats you can encounter:\n\n.. code-block:: php\n\n    <?php\n    $user->getFavorites()->add($favoriteComment);\n    // not calling $favoriteComment->getUserFavorites()->add($user);\n\n    $user->getFavorites()->contains($favoriteComment); // TRUE\n    $favoriteComment->getUserFavorites()->contains($user); // FALSE\n\nThere are two approaches to handle this problem in your code:\n\n\n1. Ignore updating the inverse side of bidirectional collections,\n   BUT never read from them in requests that changed their state. In\n   the next request Doctrine hydrates the consistent collection state\n   again.\n2. Always keep the bidirectional collections in sync through\n   association management methods. Reads of the Collections directly\n   after changes are consistent then.\n\n.. _transitive-persistence:\n\nTransitive persistence / Cascade Operations\n-------------------------------------------\n\nDoctrine ORM provides a mechanism for transitive persistence through cascading of certain operations.\nEach association to another entity or a collection of\nentities can be configured to automatically cascade the following operations to the associated entities:\n``persist``, ``remove``, ``detach``, ``refresh`` or ``all``.\n\nThe main use case for ``cascade: persist`` is to avoid \"exposing\" associated entities to your PHP application.\nContinuing with the User-Comment example of this chapter, this is how the creation of a new user and a new\ncomment might look like in your controller (without ``cascade: persist``):\n\n.. code-block:: php\n\n    <?php\n    $user = new User();\n    $myFirstComment = new Comment();\n    $user->addComment($myFirstComment);\n\n    $em->persist($user);\n    $em->persist($myFirstComment); // required, if `cascade: persist` is not set\n    $em->flush();\n\nNote that the Comment entity is instantiated right here in the controller.\nTo avoid this, ``cascade: persist`` allows you to \"hide\" the Comment entity from the controller,\nonly accessing it through the User entity:\n\n.. code-block:: php\n\n    <?php\n    // User entity\n    class User\n    {\n        private int $id;\n\n        /** @var Collection<int, Comment> */\n        private Collection $comments;\n\n        public function __construct()\n        {\n            $this->id = User::new();\n            $this->comments = new ArrayCollection();\n        }\n\n        public function comment(string $text, DateTimeInterface $time) : void\n        {\n            $newComment = Comment::create($text, $time);\n            $newComment->setUser($this);\n            $this->comments->add($newComment);\n        }\n\n        // ...\n    }\n\nIf you then set up the cascading to the ``User#commentsAuthored`` property...\n\n.. code-block:: php\n\n    <?php\n    class User\n    {\n        // ...\n        /** Bidirectional - One-To-Many (INVERSE SIDE) */\n        #[OneToMany(targetEntity: Comment::class, mappedBy: 'author', cascade: ['persist', 'remove'])]\n        private $commentsAuthored;\n        // ...\n    }\n\n...you can now create a user and an associated comment like this:\n\n.. code-block:: php\n\n    <?php\n    $user = new User();\n    $user->comment('Lorem ipsum', new DateTime());\n\n    $em->persist($user);\n    $em->flush();\n\n.. note::\n\n    The idea of ``cascade: persist`` is not to save you any lines of code in the controller.\n    If you instantiate the comment object in the controller (i.e. don't set up the user entity as shown above),\n    even with ``cascade: persist`` you still have to call ``$myFirstComment->setUser($user);``.\n\nThanks to ``cascade: remove``, you can easily delete a user and all linked comments without having to loop through them:\n\n.. code-block:: php\n\n    <?php\n    $user = $em->find('User', $deleteUserId);\n\n    $em->remove($user);\n    $em->flush();\n\n.. note::\n\n    Cascade operations are performed in memory. That means collections and related entities\n    are fetched into memory (even if they are marked as lazy) when\n    the cascade operation is about to be performed. This approach allows\n    entity lifecycle events to be performed for each of these operations.\n\n    However, pulling object graphs into memory on cascade can cause considerable performance\n    overhead, especially when the cascaded collections are large. Make sure\n    to weigh the benefits and downsides of each cascade operation that you define.\n\n    To rely on the database level cascade operations for the delete operation instead, you can\n    configure each join column with :doc:`the onDelete option <working-with-objects>`.\n\nEven though automatic cascading is convenient, it should be used\nwith care. Do not blindly apply ``cascade=all`` to all associations as\nit will unnecessarily degrade the performance of your application.\nFor each cascade operation that gets activated, Doctrine also\napplies that operation to the association, be it single or\ncollection valued.\n\n.. _persistence-by-reachability:\n\nPersistence by Reachability: Cascade Persist\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are additional semantics that apply to the Cascade Persist\noperation. During each ``flush()`` operation Doctrine detects if there\nare new entities in any collection and three possible cases can\nhappen:\n\n\n1. New entities in a collection marked as ``cascade: persist`` will be\n   directly persisted by Doctrine.\n2. New entities in a collection not marked as ``cascade: persist`` will\n   produce an Exception and rollback the ``flush()`` operation.\n3. Collections without new entities are skipped.\n\nThis concept is called Persistence by Reachability: New entities\nthat are found on already managed entities are automatically\npersisted as long as the association is defined as ``cascade: persist``.\n\nOrphan Removal\n--------------\n\nThere is another concept of cascading that is relevant only when removing entities\nfrom collections. If an Entity of type ``A`` contains references to privately\nowned Entities ``B`` then if the reference from ``A`` to ``B`` is removed the\nentity ``B`` should also be removed, because it is not used anymore.\n\nOrphanRemoval works with one-to-one, one-to-many and many-to-many associations.\n\n.. note::\n\n    When using the ``orphanRemoval=true`` option Doctrine makes the assumption\n    that the entities are privately owned and will **NOT** be reused by other entities.\n    If you neglect this assumption your entities will get deleted by Doctrine even if\n    you assigned the orphaned entity to another one.\n\n.. note::\n\n    ``orphanRemoval=true`` option should be used in combination with ``cascade=[\"persist\"]`` option\n    as the child entity, that is manually persisted, will not be deleted automatically by Doctrine\n    when a collection is still an instance of ArrayCollection (before first flush / hydration).\n    This is a Doctrine limitation since ArrayCollection does not have access to a UnitOfWork.\n\nAs a better example consider an Addressbook application where you have Contacts, Addresses\nand StandingData:\n\n.. code-block:: php\n\n    <?php\n\n    namespace Addressbook;\n\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n\n    #[Entity]\n    class Contact\n    {\n        #[Id, Column(type: 'integer'), GeneratedValue]\n        private int|null $id = null;\n\n        #[OneToOne(targetEntity: StandingData::class, cascade: ['persist'], orphanRemoval: true)]\n        private StandingData|null $standingData = null;\n\n        /** @var Collection<int, Address> */\n        #[OneToMany(targetEntity: Address::class, mappedBy: 'contact', cascade: ['persist'], orphanRemoval: true)]\n        private Collection $addresses;\n\n        public function __construct()\n        {\n            $this->addresses = new ArrayCollection();\n        }\n\n        public function newStandingData(StandingData $sd): void\n        {\n            $this->standingData = $sd;\n        }\n\n        public function removeAddress(int $pos): void\n        {\n            unset($this->addresses[$pos]);\n        }\n    }\n\nNow two examples of what happens when you remove the references:\n\n.. code-block:: php\n\n    <?php\n\n    $contact = $em->find(\"Addressbook\\Contact\", $contactId);\n    $contact->newStandingData(new StandingData(\"Firstname\", \"Lastname\", \"Street\"));\n    $contact->removeAddress(1);\n\n    $em->flush();\n\nIn this case you have not only changed the ``Contact`` entity itself but\nyou have also removed the references for standing data and as well as one\naddress reference. When flush is called not only are the references removed\nbut both the old standing data and the one address entity are also deleted\nfrom the database.\n\n.. _filtering-collections:\n\nFiltering Collections\n---------------------\n\nCollections have a filtering API that allows to slice parts of data from\na collection. If the collection has not been loaded from the database yet,\nthe filtering API can work on the SQL level to make optimized access to\nlarge collections.\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\Common\\Collections\\Criteria;\n\n    $group          = $entityManager->find('Group', $groupId);\n    $userCollection = $group->getUsers();\n\n    $criteria = Criteria::create()\n        ->where(Criteria::expr()->eq(\"birthday\", \"1982-02-17\"))\n        ->orderBy(array(\"username\" => Criteria::ASC))\n        ->setFirstResult(0)\n        ->setMaxResults(20)\n    ;\n\n    $birthdayUsers = $userCollection->matching($criteria);\n\n.. tip::\n\n    You can move the access of slices of collections into dedicated methods of\n    an entity. For example ``Group#getTodaysBirthdayUsers()``.\n\nThe Criteria has a limited matching language that works both on the\nSQL and on the PHP collection level. This means you can use collection matching\ninterchangeably, independent of in-memory or sql-backed collections.\n\n.. code-block:: php\n\n    <?php\n\n    use Doctrine\\Common\\Collections;\n\n    class Criteria\n    {\n        /**\n         * @return Criteria\n         */\n        static public function create();\n        /**\n         * @param Expression $where\n         * @return Criteria\n         */\n        public function where(Expression $where);\n        /**\n         * @param Expression $where\n         * @return Criteria\n         */\n        public function andWhere(Expression $where);\n        /**\n         * @param Expression $where\n         * @return Criteria\n         */\n        public function orWhere(Expression $where);\n        /**\n         * @param array $orderings\n         * @return Criteria\n         */\n        public function orderBy(array $orderings);\n        /**\n         * @param int $firstResult\n         * @return Criteria\n         */\n        public function setFirstResult($firstResult);\n        /**\n         * @param int $maxResults\n         * @return Criteria\n         */\n        public function setMaxResults($maxResults);\n        public function getOrderings();\n        public function getWhereExpression();\n        public function getFirstResult();\n        public function getMaxResults();\n    }\n\nYou can build expressions through the ExpressionBuilder. It has the following\nmethods:\n\n* ``andX($arg1, $arg2, ...)``\n* ``orX($arg1, $arg2, ...)``\n* ``not($expression)``\n* ``eq($field, $value)``\n* ``gt($field, $value)``\n* ``lt($field, $value)``\n* ``lte($field, $value)``\n* ``gte($field, $value)``\n* ``neq($field, $value)``\n* ``isNull($field)``\n* ``in($field, array $values)``\n* ``notIn($field, array $values)``\n* ``contains($field, $value)``\n* ``memberOf($value, $field)``\n* ``startsWith($field, $value)``\n* ``endsWith($field, $value)``\n\n\n.. note::\n\n    Depending on whether the collection has already been loaded from the\n    database or not, criteria matching may happen at the database/SQL level\n    or on objects in memory. This may lead to different results and come\n    surprising, for example when a code change in one place leads to a collection\n    becoming initialized and, as a side effect, returning a different result\n    or even breaking a ``matching()`` call somewhere else. Also, collection\n    initialization state in practical use cases may differ from the one covered\n    in unit tests.\n\n    Database level comparisons are based on scalar representations of the values\n    stored in entity properties. The field names passed to expressions correspond\n    to property names. Comparison and sorting may be affected by\n    database-specific behavior. For example, MySQL enum types sort by index position,\n    not lexicographically by value.\n\n    In-memory handling is based on the ``Selectable`` API of `Doctrine Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html#matching>`.\n    In this case, field names passed to expressions are being used to derive accessor\n    method names. Strict type comparisons are used for equal and not-equal checks,\n    and generally PHP language rules are being used for other comparison operators\n    or sorting.\n\n    As a general guidance, for consistent results use the Criteria API with scalar\n    values only. Note that ``DateTime`` and ``DateTimeImmutable`` are two predominant\n    examples of value objects that are *not* scalars.\n\n    Refrain from using special database-level column types or custom Doctrine Types\n    that may lead to database-specific comparison or sorting rules being applied, or\n    to database-level values being different from object field values.\n\n    Provide accessor methods for all entity fields used in criteria expressions,\n    and implement those methods in a way that their return value is the\n    same as the database-level value.\n"
  },
  {
    "path": "docs/en/reference/working-with-objects.rst",
    "content": "Working with Objects\n====================\n\nIn this chapter we will help you understand the ``EntityManager``\nand the ``UnitOfWork``. A Unit of Work is similar to an\nobject-level transaction. A new Unit of Work is implicitly started\nwhen an EntityManager is initially created or after\n``EntityManager#flush()`` has been invoked. A Unit of Work is\ncommitted (and a new one started) by invoking\n``EntityManager#flush()``.\n\nA Unit of Work can be manually closed by calling\nEntityManager#close(). Any changes to objects within this Unit of\nWork that have not yet been persisted are lost.\n\n.. note::\n\n    It is very important to understand that only\n    ``EntityManager#flush()`` ever causes write operations against the\n    database to be executed. Any other methods such as\n    ``EntityManager#persist($entity)`` or\n    ``EntityManager#remove($entity)`` only notify the UnitOfWork to\n    perform these operations during flush.\n\n    Not calling ``EntityManager#flush()`` will lead to all changes\n    during that request being lost.\n\n.. note::\n\n    Doctrine NEVER touches the public API of methods in your entity\n    classes (like getters and setters) nor the constructor method.\n    Instead, it uses reflection to get/set data from/to your entity objects.\n    When Doctrine fetches data from DB and saves it back,\n    any code put in your get/set methods won't be implicitly taken into account.\n\nEntities and the Identity Map\n-----------------------------\n\nEntities are objects with identity. Their identity has a conceptual\nmeaning inside your domain. In a CMS application each article has a\nunique id. You can uniquely identify each article by that id.\n\nTake the following example, where you find an article with the\nheadline \"Hello World\" with the ID 1234:\n\n.. code-block:: php\n\n    <?php\n    $article = $entityManager->find('CMS\\Article', 1234);\n    $article->setHeadline('Hello World dude!');\n\n    $article2 = $entityManager->find('CMS\\Article', 1234);\n    echo $article2->getHeadline();\n\nIn this case the Article is accessed from the entity manager twice,\nbut modified in between. Doctrine ORM realizes this and will only\never give you access to one instance of the Article with ID 1234,\nno matter how often do you retrieve it from the EntityManager and\neven no matter what kind of Query method you are using (find,\nRepository Finder or DQL). This is called \"Identity Map\" pattern,\nwhich means Doctrine keeps a map of each entity and ids that have\nbeen retrieved per PHP request and keeps returning you the same\ninstances.\n\nIn the previous example the echo prints \"Hello World dude!\" to the\nscreen. You can even verify that ``$article`` and ``$article2`` are\nindeed pointing to the same instance by running the following\ncode:\n\n.. code-block:: php\n\n    <?php\n    if ($article === $article2) {\n        echo \"Yes we are the same!\";\n    }\n\nSometimes you want to clear the identity map of an EntityManager to\nstart over. We use this regularly in our unit-tests to enforce\nloading objects from the database again instead of serving them\nfrom the identity map. You can call ``EntityManager#clear()`` to\nachieve this result.\n\nEntity Object Graph Traversal\n-----------------------------\n\nAlthough Doctrine allows for a complete separation of your domain\nmodel (Entity classes) there will never be a situation where\nobjects are \"missing\" when traversing associations. You can walk\nall the associations inside your entity models as deep as you\nwant.\n\nTake the following example of a single ``Article`` entity fetched\nfrom newly opened EntityManager.\n\n.. code-block:: php\n\n    <?php\n    #[Entity]\n    class Article\n    {\n        #[Id, Column(type: 'integer'), GeneratedValue]\n        private int|null $id = null;\n\n        #[Column(type: 'string')]\n        private string $headline;\n\n        #[ManyToOne(targetEntity: User::class)]\n        private User|null $author = null;\n\n        /** @var Collection<int, Comment> */\n        #[OneToMany(targetEntity: Comment::class, mappedBy: 'article')]\n        private Collection $comments;\n\n        public function __construct()\n        {\n            $this->comments = new ArrayCollection();\n        }\n\n        public function getAuthor(): User|null { return $this->author; }\n        public function getComments(): Collection { return $this->comments; }\n    }\n\n    $article = $em->find('Article', 1);\n\nThis code only retrieves the ``Article`` instance with id 1 executing\na single SELECT statement against the articles table in the database.\nYou can still access the associated properties author and comments\nand the associated objects they contain.\n\nThis works by utilizing the lazy loading pattern. Instead of\npassing you back a real Author instance and a collection of\ncomments Doctrine will create proxy instances for you. Only if you\naccess these proxies for the first time they will go through the\nEntityManager and load their state from the database.\n\nThis lazy-loading process happens behind the scenes, hidden from\nyour code. See the following code:\n\n.. code-block:: php\n\n    <?php\n    $article = $em->find('Article', 1);\n\n    // accessing a method of the user instance triggers the lazy-load\n    echo \"Author: \" . $article->getAuthor()->getName() . \"\\n\";\n\n    // Lazy Loading Proxies pass instanceof tests:\n    if ($article->getAuthor() instanceof User) {\n        // a User Proxy is a generated \"UserProxy\" class\n    }\n\n    // accessing the comments as an iterator triggers the lazy-load\n    // retrieving ALL the comments of this article from the database\n    // using a single SELECT statement\n    foreach ($article->getComments() as $comment) {\n        echo $comment->getText() . \"\\n\\n\";\n    }\n\n    // Article::$comments passes instanceof tests for the Collection interface\n    // But it will NOT pass for the ArrayCollection interface\n    if ($article->getComments() instanceof \\Doctrine\\Common\\Collections\\Collection) {\n        echo \"This will always be true!\";\n    }\n\n.. warning::\n\n    Traversing the object graph for parts that are lazy-loaded will\n    easily trigger lots of SQL queries and will perform badly if used\n    too heavily. Make sure to use DQL to fetch-join all the parts of the\n    object-graph that you need as efficiently as possible.\n\n\nPersisting entities\n-------------------\n\nAn entity can be made persistent by passing it to the\n``EntityManager#persist($entity)`` method. By applying the persist\noperation on some entity, that entity becomes MANAGED, which means\nthat its persistence is from now on managed by an EntityManager. As\na result the persistent state of such an entity will subsequently\nbe properly synchronized with the database when\n``EntityManager#flush()`` is invoked.\n\n.. note::\n\n    Invoking the ``persist`` method on an entity does NOT\n    cause an immediate SQL INSERT to be issued on the database.\n    Doctrine applies a strategy called \"transactional write-behind\",\n    which means that it will delay most SQL commands until\n    ``EntityManager#flush()`` is invoked which will then issue all\n    necessary SQL statements to synchronize your objects with the\n    database in the most efficient way and a single, short transaction,\n    taking care of maintaining referential integrity.\n\n.. note::\n\n    Do not make any assumptions in your code about the number of queries\n    it takes to flush changes, about the ordering of ``INSERT``, ``UPDATE``\n    and ``DELETE`` queries or the order in which entities will be processed.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    $user = new User;\n    $user->setName('Mr.Right');\n    $em->persist($user);\n    $em->flush();\n\n.. note::\n\n    Generated entity identifiers / primary keys are\n    guaranteed to be available after the next successful flush\n    operation that involves the entity in question. You can not rely on\n    a generated identifier to be available directly after invoking\n    ``persist``. The inverse is also true. You can not rely on a\n    generated identifier being not available after a failed flush\n    operation.\n\n\nThe semantics of the persist operation, applied on an entity X, are\nas follows:\n\n\n-  If X is a new entity, it becomes managed. The entity X will be\n   entered into the database as a result of the flush operation.\n-  If X is a preexisting managed entity, it is ignored by the\n   persist operation. However, the persist operation is cascaded to\n   entities referenced by X, if the relationships from X to these\n   other entities are mapped with cascade=PERSIST or cascade=ALL (see\n   \":ref:`transitive-persistence`\").\n-  If X is a removed entity, it becomes managed.\n-  If X is a detached entity, an exception will be thrown on\n   flush.\n\n.. caution::\n\n    Do not pass detached entities to the persist operation. The persist operation always\n    considers entities that are not yet known to the ``EntityManager`` as new entities\n    (refer to the ``STATE_NEW`` constant inside the ``UnitOfWork``).\n\nRemoving entities\n-----------------\n\nAn entity can be removed from persistent storage by passing it to\nthe ``EntityManager#remove($entity)`` method. By applying the\n``remove`` operation on some entity, that entity becomes REMOVED,\nwhich means that its persistent state will be deleted once\n``EntityManager#flush()`` is invoked.\n\n.. note::\n\n    Just like ``persist``, invoking ``remove`` on an entity\n    does NOT cause an immediate SQL DELETE to be issued on the\n    database. The entity will be deleted on the next invocation of\n    ``EntityManager#flush()`` that involves that entity. This\n    means that entities scheduled for removal can still be queried\n    for and appear in query and collection results. See\n    the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`\n    for more information.\n\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    $em->remove($user);\n    $em->flush();\n\nThe semantics of the remove operation, applied to an entity X are\nas follows:\n\n\n-  If X is a new entity, it is ignored by the remove operation.\n   However, the remove operation is cascaded to entities referenced by\n   X, if the relationship from X to these other entities is mapped\n   with cascade=REMOVE or cascade=ALL (see \":ref:`transitive-persistence`\").\n-  If X is a managed entity, the remove operation causes it to\n   become removed. The remove operation is cascaded to entities\n   referenced by X, if the relationships from X to these other\n   entities is mapped with cascade=REMOVE or cascade=ALL (see\n   \":ref:`transitive-persistence`\").\n-  If X is a detached entity, an InvalidArgumentException will be\n   thrown.\n-  If X is a removed entity, it is ignored by the remove operation.\n-  A removed entity X will be removed from the database as a result\n   of the flush operation.\n\nAfter an entity has been removed, its in-memory state is the same as\nbefore the removal, except for generated identifiers.\n\nDuring the ``EntityManager#flush()`` operation, the removed entity\nwill also be removed from all collections in entities currently\nloaded into memory.\n\n.. _remove_object_many_to_many_join_tables:\n\nJoin-table management when removing from many-to-many collections\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRegarding existing rows in many-to-many join tables that refer to\nan entity being removed, the following applies.\n\nWhen the entity being removed does not declare the many-to-many association\nitself (that is, the many-to-many association is unidirectional and\nthe entity is on the inverse side), the ORM has no reasonable way to\ndetect associations targeting the entity's class. Thus, no ORM-level handling\nof join-table rows is attempted and database-level constraints apply.\nIn case of database-level ``ON DELETE RESTRICT`` constraints, the\n``EntityManager#flush()`` operation may abort and a ``ConstraintViolationException``\nmay be thrown. No in-memory collections will be modified in this case.\nWith ``ON DELETE CASCADE``, the RDBMS will take care of removing rows\nfrom join tables.\n\nWhen the entity being removed is part of bi-directional many-to-many\nassociation, either as the owning or inverse side, the ORM will\ndelete rows from join tables before removing the entity itself. That means\ndatabase-level ``ON DELETE RESTRICT`` constraints on join tables are not\neffective, since the join table rows are removed first. Removal of join table\nrows happens through specialized methods in entity and collection persister\nclasses and take one query per entity and join table. In case the association\nuses a ``@JoinColumn`` configuration with ``onDelete=\"CASCADE\"``, instead\nof using a dedicated ``DELETE`` query the database-level operation will be\nrelied upon.\n\n.. note::\n\n    In case you rely on database-level ``ON DELETE RESTRICT`` constraints,\n    be aware that by making many-to-many associations bidirectional the\n    assumed protection may be lost.\n\n\nPerformance of different deletion strategies\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDeleting an object with all its associated objects can be achieved\nin multiple ways with very different performance impacts.\n\n1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM will\n   fetch this association. If it's a Single association it will pass\n   this entity to ``EntityManager#remove()``. If the association is a\n   collection, Doctrine will loop over all its elements and pass them to\n   ``EntityManager#remove()``.\n   In both cases the cascade remove semantics are applied recursively.\n   For large object graphs this removal strategy can be very costly.\n2. Using a DQL ``DELETE`` statement allows you to delete multiple\n   entities of a type with a single command and without hydrating\n   these entities. This can be very efficient to delete large object\n   graphs from the database.\n3. Using foreign key semantics ``onDelete=\"CASCADE\"`` can force the\n   database to remove all associated objects internally. This strategy\n   is a bit tricky to get right but can be very powerful and fast. You\n   should be aware however that using strategy 1 (``CASCADE=REMOVE``)\n   completely by-passes any foreign key ``onDelete=CASCADE`` option,\n   because Doctrine will fetch and remove all associated entities\n   explicitly nevertheless.\n\n.. note::\n\n    Calling ``remove`` on an entity will remove the object from the identity\n    map and therefore detach it. Querying the same entity again, for example\n    via a lazy loaded relation, will return a new object.\n\n\nDetaching entities\n------------------\n\nAn entity is detached from an EntityManager and thus no longer\nmanaged by invoking the ``EntityManager#detach($entity)`` method on\nit or by cascading the detach operation to it. Changes made to the\ndetached entity, if any (including removal of the entity), will not\nbe synchronized to the database after the entity has been\ndetached.\n\nDoctrine will not hold on to any references to a detached entity.\n\nExample:\n\n.. code-block:: php\n\n    <?php\n    $em->detach($entity);\n\nThe semantics of the detach operation, applied to an entity X are\nas follows:\n\n\n-  If X is a managed entity, the detach operation causes it to\n   become detached. The detach operation is cascaded to entities\n   referenced by X, if the relationships from X to these other\n   entities is mapped with cascade=DETACH or cascade=ALL (see\n   \":ref:`transitive-persistence`\"). Entities which previously referenced X\n   will continue to reference X.\n-  If X is a new or detached entity, it is ignored by the detach\n   operation.\n-  If X is a removed entity, the detach operation is cascaded to\n   entities referenced by X, if the relationships from X to these\n   other entities is mapped with cascade=DETACH or cascade=ALL (see\n   \":ref:`transitive-persistence`\"). Entities which previously referenced X\n   will continue to reference X.\n\nThere are several situations in which an entity is detached\nautomatically without invoking the ``detach`` method:\n\n\n-  When ``EntityManager#clear()`` is invoked, all entities that are\n   currently managed by the EntityManager instance become detached.\n-  When serializing an entity. The entity retrieved upon subsequent\n   unserialization will be detached (This is the case for all entities\n   that are serialized and stored in some cache).\n\nThe ``detach`` operation is usually not as frequently needed and\nused as ``persist`` and ``remove``.\n\n\nSynchronization with the Database\n---------------------------------\n\nThe state of persistent entities is synchronized with the database\non flush of an ``EntityManager`` which commits the underlying\n``UnitOfWork``. The synchronization involves writing any updates to\npersistent entities and their relationships to the database.\nThereby bidirectional relationships are persisted based on the\nreferences held by the owning side of the relationship as explained\nin the Association Mapping chapter.\n\nWhen ``EntityManager#flush()`` is called, Doctrine inspects all\nmanaged, new and removed entities and will perform the following\noperations.\n\n.. _workingobjects_database_uow_outofsync:\n\nEffects of Database and UnitOfWork being Out-Of-Sync\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAs soon as you begin to change the state of entities, call persist or remove the\ncontents of the UnitOfWork and the database will drive out of sync. They can\nonly be synchronized by calling ``EntityManager#flush()``. This section\ndescribes the effects of database and UnitOfWork being out of sync.\n\n-  Entities that are scheduled for removal can still be queried from the database.\n   They are returned from DQL and Repository queries and are visible in collections.\n-  Entities that are passed to ``EntityManager#persist`` do not turn up in query\n   results.\n-  Entities that have changed will not be overwritten with the state from the database.\n   This is because the identity map will detect the construction of an already existing\n   entity and assumes its the most up to date version.\n\n``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually.\n\nSynchronizing New and Managed Entities\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe flush operation applies to a managed entity with the following\nsemantics:\n\n\n-  The entity itself is synchronized to the database using a SQL\n   UPDATE statement, only if at least one persistent field has\n   changed.\n-  No SQL updates are executed if the entity did not change.\n\nThe flush operation applies to a new entity with the following\nsemantics:\n\n\n-  The entity itself is synchronized to the database using a SQL\n   INSERT statement.\n\nFor all (initialized) relationships of the new or managed entity\nthe following semantics apply to each associated entity X:\n\n\n-  If X is new and persist operations are configured to cascade on\n   the relationship, X will be persisted.\n-  If X is new and no persist operations are configured to cascade\n   on the relationship, an exception will be thrown as this indicates\n   a programming error.\n-  If X is removed and persist operations are configured to cascade\n   on the relationship, an exception will be thrown as this indicates\n   a programming error (X would be re-persisted by the cascade).\n-  If X is detached and persist operations are configured to\n   cascade on the relationship, an exception will be thrown (This is\n   semantically the same as passing X to persist()).\n\nSynchronizing Removed Entities\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe flush operation applies to a removed entity by deleting its\npersistent state from the database. No cascade options are relevant\nfor removed entities on flush, the cascade remove option is already\nexecuted during ``EntityManager#remove($entity)``.\n\nThe size of a Unit of Work\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe size of a Unit of Work mainly refers to the number of managed\nentities at a particular point in time.\n\nThe cost of flushing\n~~~~~~~~~~~~~~~~~~~~\n\nHow costly a flush operation is, mainly depends on two factors:\n\n\n-  The size of the EntityManager's current UnitOfWork.\n-  The configured change tracking policies\n\nYou can get the size of a UnitOfWork as follows:\n\n.. code-block:: php\n\n    <?php\n    $uowSize = $em->getUnitOfWork()->size();\n\nThe size represents the number of managed entities in the Unit of\nWork. This size affects the performance of flush() operations due\nto change tracking (see \"Change Tracking Policies\") and, of course,\nmemory consumption, so you may want to check it from time to time\nduring development.\n\n.. note::\n\n    Do not invoke ``flush`` after every change to an entity\n    or every single invocation of persist/remove/... This is an\n    anti-pattern and unnecessarily reduces the performance of your\n    application. Instead, form units of work that operate on your\n    objects and call ``flush`` when you are done. While serving a\n    single HTTP request there should be usually no need for invoking\n    ``flush`` more than 0-2 times.\n\n\nDirect access to a Unit of Work\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can get direct access to the Unit of Work by calling\n``EntityManager#getUnitOfWork()``. This will return the UnitOfWork\ninstance the EntityManager is currently using.\n\n.. code-block:: php\n\n    <?php\n    $uow = $em->getUnitOfWork();\n\n.. note::\n\n    Directly manipulating a UnitOfWork is not recommended.\n    When working directly with the UnitOfWork API, respect methods\n    marked as INTERNAL by not using them and carefully read the API\n    documentation.\n\n\nEntity State\n~~~~~~~~~~~~\n\nAs outlined in the architecture overview an entity can be in one of\nfour possible states: NEW, MANAGED, REMOVED, DETACHED. If you\nexplicitly need to find out what the current state of an entity is\nin the context of a certain ``EntityManager`` you can ask the\nunderlying ``UnitOfWork``:\n\n.. code-block:: php\n\n    <?php\n    switch ($em->getUnitOfWork()->getEntityState($entity)) {\n        case UnitOfWork::STATE_MANAGED:\n            ...\n        case UnitOfWork::STATE_REMOVED:\n            ...\n        case UnitOfWork::STATE_DETACHED:\n            ...\n        case UnitOfWork::STATE_NEW:\n            ...\n    }\n\nAn entity is in MANAGED state if it is associated with an\n``EntityManager`` and it is not REMOVED.\n\nAn entity is in REMOVED state after it has been passed to\n``EntityManager#remove()`` until the next flush operation of the\nsame EntityManager. A REMOVED entity is still associated with an\n``EntityManager`` until the next flush operation.\n\nAn entity is in DETACHED state if it has persistent state and\nidentity but is currently not associated with an\n``EntityManager``.\n\nAn entity is in NEW state if has no persistent state and identity\nand is not associated with an ``EntityManager`` (for example those\njust created via the \"new\" operator).\n\nQuerying\n--------\n\nDoctrine ORM provides the following ways, in increasing level of\npower and flexibility, to query for persistent objects. You should\nalways start with the simplest one that suits your needs.\n\nBy Primary Key\n~~~~~~~~~~~~~~\n\nThe most basic way to query for a persistent object is by its\nidentifier / primary key using the\n``EntityManager#find($entityName, $id)`` method. Here is an\nexample:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n    $user = $em->find('MyProject\\Domain\\User', $id);\n\nThe return value is either the found entity instance or null if no\ninstance could be found with the given identifier.\n\nEssentially, ``EntityManager#find()`` is just a shortcut for the\nfollowing:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n    $user = $em->getRepository('MyProject\\Domain\\User')->find($id);\n\n``EntityManager#getRepository($entityName)`` returns a repository\nobject which provides many ways to retrieve entities of the\nspecified type. By default, the repository instance is of type\n``Doctrine\\ORM\\EntityRepository``. You can also use custom\nrepository classes as shown later.\n\nBy Simple Conditions\n~~~~~~~~~~~~~~~~~~~~\n\nTo query for one or more entities based on several conditions that\nform a logical conjunction, use the ``findBy`` and ``findOneBy``\nmethods on a repository as follows:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n\n    // All users that are 20 years old\n    $users = $em->getRepository('MyProject\\Domain\\User')->findBy(array('age' => 20));\n\n    // All users that are 20 years old and have a surname of 'Miller'\n    $users = $em->getRepository('MyProject\\Domain\\User')->findBy(array('age' => 20, 'surname' => 'Miller'));\n\n    // A single user by its nickname\n    $user = $em->getRepository('MyProject\\Domain\\User')->findOneBy(array('nickname' => 'romanb'));\n\nYou can also load by owning side associations through the repository:\n\n.. code-block:: php\n\n    <?php\n    $number = $em->find('MyProject\\Domain\\Phonenumber', 1234);\n    $user = $em->getRepository('MyProject\\Domain\\User')->findOneBy(array('phone' => $number->getId()));\n\nThe ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters:\n\n.. code-block:: php\n\n    <?php\n    $tenUsers = $em->getRepository('MyProject\\Domain\\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);\n\nIf you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically:\n\n.. code-block:: php\n\n    <?php\n    $users = $em->getRepository('MyProject\\Domain\\User')->findBy(array('age' => array(20, 30, 40)));\n    // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40)\n\nAn EntityRepository also provides a mechanism for more concise\ncalls through its use of ``__call``. Thus, the following two\nexamples are equivalent:\n\n.. code-block:: php\n\n    <?php\n    // A single user by its nickname\n    $user = $em->getRepository('MyProject\\Domain\\User')->findOneBy(array('nickname' => 'romanb'));\n\n    // A single user by its nickname (__call magic)\n    $user = $em->getRepository('MyProject\\Domain\\User')->findOneByNickname('romanb');\n\nAdditionally, you can just count the result of the provided conditions when you don't really need the data:\n\n.. code-block:: php\n\n    <?php\n    // Check there is no user with nickname\n    $availableNickname = 0 === $em->getRepository('MyProject\\Domain\\User')->count(['nickname' => 'nonexistent']);\n\nBy Criteria\n~~~~~~~~~~~\n\nThe Repository implement the ``Doctrine\\Common\\Collections\\Selectable``\ninterface. That means you can build ``Doctrine\\Common\\Collections\\Criteria``\nand pass them to the ``matching($criteria)`` method.\n\nSee section `Filtering collections` of chapter :doc:`Working with Associations <working-with-associations>`\n\nBy Eager Loading\n~~~~~~~~~~~~~~~~\n\nWhenever you query for an entity that has persistent associations\nand these associations are mapped as EAGER, they will automatically\nbe loaded together with the entity being queried and is thus\nimmediately available to your application.\n\nEager Loading can also be configured at runtime through\n``AbstractQuery::setFetchMode`` in DQL or Native Queries.\n\nEager loading for many-to-one and one-to-one associations is using either a\nLEFT JOIN or a second query for fetching the related entity eagerly.\n\nEager loading for many-to-one associations uses a second query to load\nthe collections for several entities at the same time.\n\nWhen many-to-many, one-to-one or one-to-many associations are eagerly loaded,\nthen the global batch size configuration is used to avoid IN(?) queries with\ntoo many arguments. The default batch size is 100 and can be changed with\n``Configuration::setEagerFetchBatchSize()``.\n\nFor eagerly loaded Many-To-Many associations one query has to be made for each\ncollection.\n\nBy Lazy Loading\n~~~~~~~~~~~~~~~\n\nWhenever you have a managed entity instance at hand, you can\ntraverse and use any associations of that entity that are\nconfigured LAZY as if they were in-memory already. Doctrine will\nautomatically load the associated objects on demand through the\nconcept of lazy-loading.\n\nBy DQL\n~~~~~~\n\nThe most powerful and flexible method to query for persistent\nobjects is the Doctrine Query Language, an object query language.\nDQL enables you to query for persistent objects in the language of\nobjects. DQL understands classes, fields, inheritance and\nassociations. DQL is syntactically very similar to the familiar SQL\nbut *it is not SQL*.\n\nA DQL query is represented by an instance of the\n``Doctrine\\ORM\\Query`` class. You create a query using\n``EntityManager#createQuery($dql)``. Here is a simple example:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n\n    // All users with an age between 20 and 30 (inclusive).\n    $q = $em->createQuery(\"select u from MyDomain\\Model\\User u where u.age >= 20 and u.age <= 30\");\n    $users = $q->getResult();\n\nNote that this query contains no knowledge about the relational\nschema, only about the object model. DQL supports positional as\nwell as named parameters, many functions, (fetch) joins,\naggregates, subqueries and much more. Detailed information about\nDQL and its syntax as well as the Doctrine class can be found in\n:doc:`the dedicated chapter <dql-doctrine-query-language>`.\nFor programmatically building up queries based on conditions that\nare only known at runtime, Doctrine provides the special\n``Doctrine\\ORM\\QueryBuilder`` class. While this a powerful tool,\nit also brings more complexity to your code compared to plain DQL,\nso you should only use it when you need it. More information on\nconstructing queries with a QueryBuilder can be found\n:doc:`in Query Builder chapter <query-builder>`.\n\nBy Native Queries\n~~~~~~~~~~~~~~~~~\n\nAs an alternative to DQL or as a fallback for special SQL\nstatements native queries can be used. Native queries are built by\nusing a hand-crafted SQL query and a ResultSetMapping that\ndescribes how the SQL result set should be transformed by Doctrine.\nMore information about native queries can be found in\n:doc:`the dedicated chapter <native-sql>`.\n\nCustom Repositories\n~~~~~~~~~~~~~~~~~~~\n\nBy default the EntityManager returns a default implementation of\n``Doctrine\\ORM\\EntityRepository`` when you call\n``EntityManager#getRepository($entityClass)``. You can overwrite\nthis behaviour by specifying the class name of your own Entity\nRepository in the Attribute or XML metadata. In large\napplications that require lots of specialized DQL queries using a\ncustom repository is one recommended way of grouping these queries\nin a central location.\n\n.. code-block:: php\n\n    <?php\n    namespace MyDomain\\Model;\n\n    use MyDomain\\Model\\UserRepository;\n    use Doctrine\\ORM\\EntityRepository;\n    use Doctrine\\ORM\\Mapping as ORM;\n\n    #[ORM\\Entity(repositoryClass: UserRepository::class)]\n    class User\n    {\n\n    }\n\n    class UserRepository extends EntityRepository\n    {\n        /** @return Collection<User> */\n        public function getAllAdminUsers(): Collection\n        {\n            return $this->_em->createQuery('SELECT u FROM MyDomain\\Model\\User u WHERE u.status = \"admin\"')\n                             ->getResult();\n        }\n    }\n\nYou can access your repository now by calling:\n\n.. code-block:: php\n\n    <?php\n    // $em instanceof EntityManager\n\n    $admins = $em->getRepository('MyDomain\\Model\\User')->getAllAdminUsers();\n"
  },
  {
    "path": "docs/en/reference/xml-mapping.rst",
    "content": "XML Mapping\n===========\n\nThe XML mapping driver enables you to provide the ORM metadata in\nform of XML documents. It requires the ``dom`` extension in order to be\nable to validate your mapping documents against its XML Schema.\n\nThe XML driver is backed by an XML Schema document that describes\nthe structure of a mapping document. The most recent version of the\nXML Schema document is available online at\n`https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd>`_.\nThe most convenient way to work with\nXML mapping files is to use an IDE/editor that can provide\ncode-completion based on such an XML Schema document. The following\nis an outline of a XML mapping document with the proper xmlns/xsi\nsetup for the latest code in trunk.\n\n.. code-block:: xml\n\n    <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n        ...\n\n    </doctrine-mapping>\n\nThe XML mapping document of a class is loaded on-demand the first\ntime it is requested and subsequently stored in the metadata cache.\nIn order to work, this requires certain conventions:\n\n\n-  Each entity/mapped superclass must get its own dedicated XML\n   mapping document.\n-  The name of the mapping document must consist of the fully\n   qualified name of the class, where namespace separators are\n   replaced by dots (.). For example an Entity with the fully\n   qualified class-name \"MyProject\" would require a mapping file\n   \"MyProject.Entities.User.dcm.xml\" unless the extension is changed.\n-  All mapping documents should get the extension \".dcm.xml\" to\n   identify it as a Doctrine mapping file. This is more of a\n   convention and you are not forced to do this. You can change the\n   file extension easily enough.\n\n.. code-block:: php\n\n    <?php\n    $driver->getLocator()->setFileExtension('.xml');\n\nIt is recommended to put all XML mapping documents in a single\nfolder but you can spread the documents over several folders if you\nwant to. In order to tell the XmlDriver where to look for your\nmapping documents, supply an array of paths as the first argument\nof the constructor, like this:\n\n.. code-block:: php\n\n    <?php\n    $config = new \\Doctrine\\ORM\\Configuration();\n    $driver = new \\Doctrine\\ORM\\Mapping\\Driver\\XmlDriver(array('/path/to/files1', '/path/to/files2'));\n    $config->setMetadataDriverImpl($driver);\n\n.. warning::\n\n    Note that Doctrine ORM does not modify any settings for ``libxml``,\n    therefore, external XML entities may or may not be enabled or\n    configured correctly.\n    XML mappings are not XXE/XEE attack vectors since they are not\n    related with user input, but it is recommended that you do not\n    use external XML entities in your mapping files to avoid running\n    into unexpected behaviour.\n\nSimplified XML Driver\n~~~~~~~~~~~~~~~~~~~~~\n\nThe Symfony project sponsored a driver that simplifies usage of the XML Driver.\nThe changes between the original driver are:\n\n1. File Extension is .orm.xml\n2. Filenames are shortened, \"MyProject\\Entities\\User\" will become User.orm.xml\n3. You can add a global file and add multiple entities in this file.\n\nConfiguration of this client works a little bit different:\n\n.. code-block:: php\n\n    <?php\n    $namespaces = array(\n        '/path/to/files1' => 'MyProject\\Entities',\n        '/path/to/files2' => 'OtherProject\\Entities'\n    );\n    $driver = new \\Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver($namespaces);\n    $driver->setGlobalBasename('global'); // global.orm.xml\n\nExample\n-------\n\nAs a quick start, here is a small example document that makes use\nof several common elements:\n\n.. code-block:: xml\n\n    // Doctrine.Tests.ORM.Mapping.User.dcm.xml\n    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n        <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\User\" table=\"cms_users\">\n\n            <indexes>\n                <index name=\"name_idx\" columns=\"name\"/>\n            </indexes>\n\n            <unique-constraints>\n                <unique-constraint columns=\"name,user_email\" name=\"search_idx\" />\n            </unique-constraints>\n\n            <lifecycle-callbacks>\n                <lifecycle-callback type=\"prePersist\" method=\"doStuffOnPrePersist\"/>\n                <lifecycle-callback type=\"prePersist\" method=\"doOtherStuffOnPrePersistToo\"/>\n                <lifecycle-callback type=\"postPersist\" method=\"doStuffOnPostPersist\"/>\n            </lifecycle-callbacks>\n\n            <id name=\"id\" type=\"integer\" column=\"id\">\n                <generator strategy=\"AUTO\"/>\n                <sequence-generator sequence-name=\"tablename_seq\" allocation-size=\"100\" initial-value=\"1\" />\n            </id>\n\n            <field name=\"name\" column=\"name\" type=\"string\" length=\"50\" nullable=\"true\" unique=\"true\" />\n            <field name=\"email\" column=\"user_email\" type=\"string\" index=\"true\" column-definition=\"CHAR(32) NOT NULL\" />\n\n            <one-to-one field=\"address\" target-entity=\"Address\" inversed-by=\"user\">\n                <cascade><cascade-remove /></cascade>\n                <join-column name=\"address_id\" referenced-column-name=\"id\" on-delete=\"CASCADE\" on-update=\"CASCADE\"/>\n            </one-to-one>\n\n            <one-to-many field=\"phonenumbers\" target-entity=\"Phonenumber\" mapped-by=\"user\">\n                <cascade>\n                    <cascade-persist/>\n                </cascade>\n                <order-by>\n                    <order-by-field name=\"number\" direction=\"ASC\" />\n                </order-by>\n            </one-to-many>\n\n            <many-to-many field=\"groups\" target-entity=\"Group\">\n                <cascade>\n                    <cascade-all/>\n                </cascade>\n                <join-table name=\"cms_users_groups\">\n                    <join-columns>\n                        <join-column name=\"user_id\" referenced-column-name=\"id\" nullable=\"false\" unique=\"false\" />\n                    </join-columns>\n                    <inverse-join-columns>\n                        <join-column name=\"group_id\" referenced-column-name=\"id\" column-definition=\"INT NULL\" />\n                    </inverse-join-columns>\n                </join-table>\n            </many-to-many>\n\n        </entity>\n\n    </doctrine-mapping>\n\nBe aware that class-names specified in the XML files should be\nfully qualified.\n\nXML-Element Reference\n---------------------\n\nThe XML-Element reference explains all the tags and attributes that\nthe Doctrine Mapping XSD Schema defines. You should read the\nBasic-, Association- and Inheritance Mapping chapters to understand\nwhat each of this definitions means in detail.\n\nDefining an Entity\n~~~~~~~~~~~~~~~~~~\n\nEach XML Mapping File contains the definition of one entity,\nspecified as the ``<entity />`` element as a direct child of the\n``<doctrine-mapping />`` element:\n\n.. code-block:: xml\n\n    <doctrine-mapping>\n        <entity name=\"MyProject\\User\" table=\"cms_users\" schema=\"schema_name\" repository-class=\"MyProject\\UserRepository\">\n            <!-- definition here -->\n        </entity>\n    </doctrine-mapping>\n\nRequired attributes:\n\n\n-  name - The fully qualified class-name of the entity.\n\nOptional attributes:\n\n\n-  **table** - The Table-Name to be used for this entity. Otherwise the\n   Unqualified Class-Name is used by default.\n-  **repository-class** - The fully qualified class-name of an\n   alternative ``Doctrine\\ORM\\EntityRepository`` implementation to be\n   used with this entity.\n-  **inheritance-type** - The type of inheritance, defaults to none. A\n   more detailed description follows in the\n   *Defining Inheritance Mappings* section.\n-  **read-only** - Specifies that this entity is marked as read only and not\n   considered for change-tracking. Entities of this type can be persisted\n   and removed though.\n-  **schema** - The schema the table lies in, for platforms that support schemas\n\nDefining Fields\n~~~~~~~~~~~~~~~\n\nEach entity class can contain zero to infinite fields that are\nmanaged by Doctrine. You can define them using the ``<field />``\nelement as a children to the ``<entity />`` element. The field\nelement is only used for primitive types that are not the ID of the\nentity. For the ID mapping you have to use the ``<id />`` element.\n\n.. code-block:: xml\n\n    <entity name=\"MyProject\\User\">\n\n        <field name=\"name\" type=\"string\" length=\"50\" />\n        <field name=\"username\" type=\"string\" unique=\"true\" />\n        <field name=\"age\" type=\"integer\" nullable=\"true\" />\n        <field name=\"isActive\" column=\"is_active\" type=\"boolean\" />\n        <field name=\"weight\" type=\"decimal\" scale=\"5\" precision=\"2\" />\n        <field name=\"login_count\" type=\"integer\" nullable=\"false\">\n            <options>\n                <option name=\"comment\">The number of times the user has logged in.</option>\n                <option name=\"default\">0</option>\n            </options>\n        </field>\n    </entity>\n\nRequired attributes:\n\n\n-  name - The name of the Property/Field on the given Entity PHP\n   class.\n\nOptional attributes:\n\n\n-  type - The ``Doctrine\\DBAL\\Types\\Type`` name, defaults to\n   \"string\"\n-  column - Name of the column in the database, defaults to the\n   field name.\n-  length - The length of the given type, for use with strings\n   only.\n-  unique - Should this field contain a unique value across the\n   table? Defaults to false.\n-  index - Should an index be created for this column? Defaults to\n   false.\n-  nullable - Should this field allow NULL as a value? Defaults to\n   false.\n-  insertable - Should this field be inserted? Defaults to true.\n-  updatable - Should this field be updated? Defaults to true.\n-  generated - Enum of the values ALWAYS, INSERT, NEVER that determines if\n   generated value must be fetched from database after INSERT or UPDATE.\n   Defaults to \"NEVER\".\n-  version - Should this field be used for optimistic locking? Only\n   works on fields with type integer or datetime.\n-  scale - Scale of a decimal type.\n-  precision - Precision of a decimal type.\n-  options - Array of additional options:\n\n   -  default - The default value to set for the column if no value\n      is supplied.\n   -  unsigned - Boolean value to determine if the column should\n      be capable of representing only non-negative integers\n      (applies only for integer column and might not be supported by\n      all vendors).\n   -  fixed - Boolean value to determine if the specified length of\n      a string column should be fixed or varying (applies only for\n      string/binary column and might not be supported by all vendors).\n   -  comment - The comment of the column in the schema (might not\n      be supported by all vendors).\n   -  customSchemaOptions - Array of additional schema options\n      which are mostly vendor specific.\n-  column-definition - Optional alternative SQL representation for\n   this column. This definition begin after the field-name and has to\n   specify the complete column definition. Using this feature will\n   turn this field dirty for Schema-Tool update commands at all\n   times.\n\n.. note::\n\n    For more detailed information on each attribute, please refer to\n    the DBAL ``Schema-Representation`` documentation.\n\nDefining Identity and Generator Strategies\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAn entity has to have at least one ``<id />`` element. For\ncomposite keys you can specify more than one id-element, however\nsurrogate keys are recommended for use with Doctrine ORM. The Id\nfield allows to define properties of the identifier and allows a\nsubset of the ``<field />`` element attributes:\n\n.. code-block:: xml\n\n    <entity name=\"MyProject\\User\">\n        <id name=\"id\" type=\"integer\" column=\"user_id\" />\n    </entity>\n\nRequired attributes:\n\n\n-  name - The name of the Property/Field on the given Entity PHP\n   class.\n-  type - The ``Doctrine\\DBAL\\Types\\Type`` name, preferably\n   \"string\" or \"integer\".\n\nOptional attributes:\n\n\n-  column - Name of the column in the database, defaults to the\n   field name.\n\nUsing the simplified definition above Doctrine will use no\nidentifier strategy for this entity. That means you have to\nmanually set the identifier before calling\n``EntityManager#persist($entity)``. This is the so called\n``NONE`` strategy.\n\nIf you want to switch the identifier generation strategy you have\nto nest a ``<generator />`` element inside the id-element. This of\ncourse only works for surrogate keys. For composite keys you always\nhave to use the ``NONE`` strategy.\n\n.. code-block:: xml\n\n    <entity name=\"MyProject\\User\">\n        <id name=\"id\" type=\"integer\" column=\"user_id\">\n            <generator strategy=\"AUTO\" />\n        </id>\n    </entity>\n\nThe following values are allowed for the ``<generator />`` strategy\nattribute:\n\n\n-  AUTO - Automatic detection of the identifier strategy based on\n   the preferred solution of the database vendor.\n-  IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs\n   available to Doctrine AFTER the INSERT statement has been executed.\n-  SEQUENCE - Use of a database sequence to retrieve the\n   entity-ids. This is possible before the INSERT statement is\n   executed.\n\nIf you are using the SEQUENCE strategy you can define an additional\nelement to describe the sequence:\n\n.. code-block:: xml\n\n    <entity name=\"MyProject\\User\">\n        <id name=\"id\" type=\"integer\" column=\"user_id\">\n            <generator strategy=\"SEQUENCE\" />\n            <sequence-generator sequence-name=\"user_seq\" allocation-size=\"5\" initial-value=\"1\" />\n        </id>\n    </entity>\n\nRequired attributes for ``<sequence-generator />``:\n\n\n-  sequence-name - The name of the sequence\n\nOptional attributes for ``<sequence-generator />``:\n\n\n-  allocation-size - By how much steps should the sequence be\n   incremented when a value is retrieved. Defaults to 1\n-  initial-value - What should the initial value of the sequence\n   be.\n\n    **NOTE**\n\n    If you want to implement a cross-vendor compatible application you\n    have to specify and additionally define the <sequence-generator />\n    element, if Doctrine chooses the sequence strategy for a\n    platform.\n\n\nDefining a Mapped Superclass\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes you want to define a class that multiple entities inherit\nfrom, which itself is not an entity however. The chapter on\n*Inheritance Mapping* describes a Mapped Superclass in detail. You\ncan define it in XML using the ``<mapped-superclass />`` tag.\n\n.. code-block:: xml\n\n    <doctrine-mapping>\n        <mapped-superclass name=\"MyProject\\BaseClass\">\n            <field name=\"created\" type=\"datetime\" />\n            <field name=\"updated\" type=\"datetime\" />\n        </mapped-superclass>\n    </doctrine-mapping>\n\nRequired attributes:\n\n\n-  name - Class name of the mapped superclass.\n\nYou can nest any number of ``<field />`` and unidirectional\n``<many-to-one />`` or ``<one-to-one />`` associations inside a\nmapped superclass.\n\nDefining Inheritance Mappings\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are currently two inheritance persistence strategies that you\ncan choose from when defining entities that inherit from each\nother. Single Table inheritance saves the fields of the complete\ninheritance hierarchy in a single table, joined table inheritance\ncreates a table for each entity combining the fields using join\nconditions.\n\nYou can specify the inheritance type in the ``<entity />`` element\nand then use the ``<discriminator-column />`` and\n``<discriminator-mapping />`` attributes.\n\n.. code-block:: xml\n\n    <entity name=\"MyProject\\Animal\" inheritance-type=\"JOINED\">\n        <discriminator-column name=\"discr\" type=\"string\" />\n        <discriminator-map>\n            <discriminator-mapping value=\"cat\" class=\"MyProject\\Cat\" />\n            <discriminator-mapping value=\"dog\" class=\"MyProject\\Dog\" />\n            <discriminator-mapping value=\"mouse\" class=\"MyProject\\Mouse\" />\n        </discriminator-map>\n    </entity>\n\nThe allowed values for inheritance-type attribute are ``JOINED`` or\n``SINGLE_TABLE``.\n\n.. note::\n\n    All inheritance related definitions have to be defined on the root\n    entity of the hierarchy.\n\n\nDefining Lifecycle Callbacks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can define the lifecycle callback methods on your entities\nusing the ``<lifecycle-callbacks />`` element:\n\n.. code-block:: xml\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\User\" table=\"cms_users\">\n\n        <lifecycle-callbacks>\n            <lifecycle-callback type=\"prePersist\" method=\"onPrePersist\" />\n        </lifecycle-callbacks>\n    </entity>\n\nDefining One-To-One Relations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can define One-To-One Relations/Associations using the\n``<one-to-one />`` element. The required and optional attributes\ndepend on the associations being on the inverse or owning side.\n\nFor the inverse side the mapping is as simple as:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <one-to-one field=\"address\" target-entity=\"Address\" mapped-by=\"user\" />\n    </entity>\n\nRequired attributes for inverse One-To-One:\n\n\n-  field - Name of the property/field on the entity's PHP class.\n-  target-entity - Name of the entity associated entity class. If\n   this is not qualified the namespace of the current class is\n   prepended. *IMPORTANT:* No leading backslash!\n-  mapped-by - Name of the field on the owning side (here Address\n   entity) that contains the owning side association.\n\nFor the owning side this mapping would look like:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\Address\">\n        <one-to-one field=\"user\" target-entity=\"User\" inversed-by=\"address\" />\n    </entity>\n\nRequired attributes for owning One-to-One:\n\n\n-  field - Name of the property/field on the entity's PHP class.\n-  target-entity - Name of the entity associated entity class. If\n   this is not qualified the namespace of the current class is\n   prepended. *IMPORTANT:* No leading backslash!\n\nOptional attributes for owning One-to-One:\n\n\n-  inversed-by - If the association is bidirectional the\n   inversed-by attribute has to be specified with the name of the\n   field on the inverse entity that contains the back-reference.\n-  orphan-removal - If true, the inverse side entity is always\n   deleted when the owning side entity is. Defaults to false.\n-  fetch - Either LAZY or EAGER, defaults to LAZY. This attribute\n   makes only sense on the owning side, the inverse side *ALWAYS* has\n   to use the ``FETCH`` strategy.\n\nThe definition for the owning side relies on a bunch of mapping\ndefaults for the join column names. Without the nested\n``<join-column />`` element Doctrine assumes to foreign key to be\ncalled ``user_id`` on the Address Entities table. This is because\nthe ``MyProject\\Address`` entity is the owning side of this\nassociation, which means it contains the foreign key.\n\nThe completed explicitly defined mapping is:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\Address\">\n        <one-to-one field=\"user\" target-entity=\"User\" inversed-by=\"address\">\n            <join-column name=\"user_id\" referenced-column-name=\"id\" />\n        </one-to-one>\n    </entity>\n\nDefining Many-To-One Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe many-to-one association is *ALWAYS* the owning side of any\nbidirectional association. This simplifies the mapping compared to\nthe one-to-one case. The minimal mapping for this association looks\nlike:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\Article\">\n        <many-to-one field=\"author\" target-entity=\"User\" />\n    </entity>\n\nRequired attributes:\n\n\n-  field - Name of the property/field on the entity's PHP class.\n-  target-entity - Name of the entity associated entity class. If\n   this is not qualified the namespace of the current class is\n   prepended. *IMPORTANT:* No leading backslash!\n\nOptional attributes:\n\n\n-  inversed-by - If the association is bidirectional the\n   inversed-by attribute has to be specified with the name of the\n   field on the inverse entity that contains the back-reference.\n-  orphan-removal - If true the entity on the inverse side is\n   always deleted when the owning side entity is and it is not\n   connected to any other owning side entity anymore. Defaults to\n   false.\n-  fetch - Either LAZY or EAGER, defaults to LAZY.\n\nThis definition relies on a bunch of mapping defaults with regards\nto the naming of the join-column/foreign key. The explicitly\ndefined mapping includes a ``<join-column />`` tag nested inside\nthe many-to-one association tag:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\Article\">\n        <many-to-one field=\"author\" target-entity=\"User\">\n            <join-column name=\"author_id\" referenced-column-name=\"id\" />\n        </many-to-one>\n    </entity>\n\nThe join-column attribute ``name`` specifies the column name of the\nforeign key and the ``referenced-column-name`` attribute specifies\nthe name of the primary key column on the User entity.\n\nDefining One-To-Many Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe one-to-many association is *ALWAYS* the inverse side of any\nassociation. There exists no such thing as a uni-directional\none-to-many association, which means this association only ever\nexists for bi-directional associations.\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <one-to-many field=\"phonenumbers\" target-entity=\"Phonenumber\" mapped-by=\"user\" />\n    </entity>\n\nRequired attributes:\n\n\n-  field - Name of the property/field on the entity's PHP class.\n-  target-entity - Name of the entity associated entity class. If\n   this is not qualified the namespace of the current class is\n   prepended. *IMPORTANT:* No leading backslash!\n-  mapped-by - Name of the field on the owning side (here\n   Phonenumber entity) that contains the owning side association.\n\nOptional attributes:\n\n\n-  fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.\n-  index-by: Index the collection by a field on the target entity.\n\nDefining Many-To-Many Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFrom all the associations the many-to-many has the most complex\ndefinition. When you rely on the mapping defaults you can omit many\ndefinitions and rely on their implicit values.\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <many-to-many field=\"groups\" target-entity=\"Group\" />\n    </entity>\n\nRequired attributes:\n\n\n-  field - Name of the property/field on the entity's PHP class.\n-  target-entity - Name of the entity associated entity class. If\n   this is not qualified the namespace of the current class is\n   prepended. *IMPORTANT:* No leading backslash!\n\nOptional attributes:\n\n\n-  mapped-by - Name of the field on the owning side that contains\n   the owning side association if the defined many-to-many association\n   is on the inverse side.\n-  inversed-by - If the association is bidirectional the\n   inversed-by attribute has to be specified with the name of the\n   field on the inverse entity that contains the back-reference.\n-  fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.\n-  index-by: Index the collection by a field on the target entity.\n\nThe mapping defaults would lead to a join-table with the name\n\"User\\_Group\" being created that contains two columns \"user\\_id\"\nand \"group\\_id\". The explicit definition of this mapping would be:\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <many-to-many field=\"groups\" target-entity=\"Group\">\n            <join-table name=\"cms_users_groups\">\n                <join-columns>\n                    <join-column name=\"user_id\" referenced-column-name=\"id\"/>\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"group_id\" referenced-column-name=\"id\"/>\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n    </entity>\n\nHere both the ``<join-columns>`` and ``<inverse-join-columns>``\ntags are necessary to tell Doctrine for which side the specified\njoin-columns apply. These are nested inside a ``<join-table />``\nattribute which allows to specify the table name of the\nmany-to-many join-table.\n\nCascade Element\n~~~~~~~~~~~~~~~\n\nDoctrine allows cascading of several UnitOfWork operations to\nrelated entities. You can specify the cascade operations in the\n``<cascade />`` element inside any of the association mapping\ntags.\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <many-to-many field=\"groups\" target-entity=\"Group\">\n            <cascade>\n                <cascade-all/>\n            </cascade>\n        </many-to-many>\n    </entity>\n\nBesides ``<cascade-all />`` the following operations can be\nspecified by their respective tags:\n\n\n-  ``<cascade-persist />``\n-  ``<cascade-remove />``\n-  ``<cascade-refresh />``\n-  ``<cascade-detach />``\n\nJoin Column Element\n~~~~~~~~~~~~~~~~~~~\n\nIn any explicitly defined association mapping you will need the\n``<join-column />`` tag. It defines how the foreign key and primary\nkey names are called that are used for joining two entities.\n\nRequired attributes:\n\n\n-  name - The column name of the foreign key.\n-  referenced-column-name - The column name of the associated\n   entities primary key\n\nOptional attributes:\n\n\n-  unique - If the join column should contain a UNIQUE constraint.\n   This makes sense for Many-To-Many join-columns only to simulate a\n   one-to-many unidirectional using a join-table.\n-  nullable - should the join column be nullable, defaults to true.\n-  on-delete - Foreign Key Cascade action to perform when entity is\n   deleted, defaults to NO ACTION/RESTRICT but can be set to\n   \"CASCADE\".\n\nDefining Order of To-Many Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can require one-to-many or many-to-many associations to be\nretrieved using an additional ``ORDER BY``.\n\n.. code-block:: xml\n\n    <entity class=\"MyProject\\User\">\n        <many-to-many field=\"groups\" target-entity=\"Group\">\n            <order-by>\n                <order-by-field name=\"name\" direction=\"ASC\" />\n            </order-by>\n        </many-to-many>\n    </entity>\n\nDefining Indexes or Unique Constraints\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo define additional indexes or unique constraints on the entities\ntable you can use the ``<indexes />`` and\n``<unique-constraints />`` elements:\n\n.. code-block:: xml\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\User\" table=\"cms_users\">\n\n        <indexes>\n            <index name=\"name_idx\" columns=\"name\"/>\n            <index columns=\"user_email\"/>\n        </indexes>\n\n        <unique-constraints>\n            <unique-constraint columns=\"name,user_email\" name=\"search_idx\" />\n        </unique-constraints>\n    </entity>\n\nYou have to specify the column and not the entity-class field names\nin the index and unique-constraint definitions.\n\nDerived Entities ID syntax\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf the primary key of an entity contains a foreign key to another entity we speak of a derived\nentity relationship. You can define this in XML with the \"association-key\" attribute in the ``<id>`` tag.\n\n.. code-block:: xml\n\n    <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n         <entity name=\"Application\\Model\\ArticleAttribute\">\n            <id name=\"article\" association-key=\"true\" />\n            <id name=\"attribute\" type=\"string\" />\n\n            <field name=\"value\" type=\"string\" />\n\n            <many-to-one field=\"article\" target-entity=\"Article\" inversed-by=\"attributes\" />\n         </entity>\n\n    </doctrine-mapping>\n"
  },
  {
    "path": "docs/en/sidebar.rst",
    "content": ":orphan:\n\n.. toctree::\n   :caption: Tutorials\n   :depth: 3\n\n   tutorials/getting-started\n   tutorials/working-with-indexed-associations\n   tutorials/extra-lazy-associations\n   tutorials/composite-primary-keys\n   tutorials/ordered-associations\n   tutorials/override-field-association-mappings-in-subclasses\n   tutorials/pagination\n   tutorials/embeddables\n\n.. toctree::\n   :caption: Reference\n   :depth: 3\n\n   reference/architecture\n   reference/configuration\n   reference/faq\n   reference/basic-mapping\n   reference/association-mapping\n   reference/inheritance-mapping\n   reference/working-with-objects\n   reference/working-with-associations\n   reference/typedfieldmapper\n   reference/events\n   reference/unitofwork\n   reference/unitofwork-associations\n   reference/transactions-and-concurrency\n   reference/batch-processing\n   reference/dql-doctrine-query-language\n   reference/query-builder\n   reference/native-sql\n   reference/change-tracking-policies\n   reference/partial-hydration\n   reference/partial-objects\n   reference/attributes-reference\n   reference/xml-mapping\n   reference/php-mapping\n   reference/caching\n   reference/improving-performance\n   reference/tools\n   reference/metadata-drivers\n   reference/best-practices\n   reference/limitations-and-known-issues\n   tutorials/pagination\n   reference/filters\n   reference/namingstrategy\n   reference/advanced-configuration\n   reference/second-level-cache\n   reference/security\n\n.. toctree::\n   :caption: Cookbook\n   :depth: 3\n\n   cookbook/aggregate-fields\n   cookbook/custom-mapping-types\n   cookbook/decorator-pattern\n   cookbook/dql-custom-walkers\n   cookbook/dql-user-defined-functions\n   cookbook/generated-columns\n   cookbook/implementing-arrayaccess-for-domain-objects\n   cookbook/resolve-target-entity-listener\n   cookbook/sql-table-prefixes\n   cookbook/strategy-cookbook-introduction\n   cookbook/validation-of-entities\n   cookbook/working-with-datetime\n   cookbook/mysql-enums\n   cookbook/advanced-field-value-conversion-using-custom-mapping-types\n   cookbook/entities-in-session\n"
  },
  {
    "path": "docs/en/tutorials/composite-primary-keys.rst",
    "content": "Composite and Foreign Keys as Primary Key\n=========================================\n\nDoctrine ORM supports composite primary keys natively. Composite keys are a very powerful relational database concept\nand we took good care to make sure Doctrine ORM supports as many of the composite primary key use-cases.\nFor Doctrine ORM composite keys of primitive data-types are supported, even foreign keys as\nprimary keys are supported.\n\nThis tutorial shows how the semantics of composite primary keys work and how they map to the database.\n\nGeneral Considerations\n~~~~~~~~~~~~~~~~~~~~~~\n\nEvery entity with a composite key cannot use an id generator other than \"NONE\". That means\nthe ID fields have to have their values set before you call ``EntityManager#persist($entity)``.\n\nPrimitive Types only\n~~~~~~~~~~~~~~~~~~~~\n\nYou can have composite keys as long as they only consist of the primitive types\n``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name\nand year of production as primary keys:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        namespace VehicleCatalogue\\Model;\n\n        #[Entity]\n        class Car\n        {\n            public function __construct(\n                #[Id, Column]\n                private string $name,\n                #[Id, Column]\n                private int $year,\n            ) {\n            }\n\n            public function getModelName(): string\n            {\n                return $this->name;\n            }\n\n            public function getYearOfProduction(): int\n            {\n                return $this->year;\n            }\n        }\n\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n              xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n              xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n            <entity name=\"VehicleCatalogue\\Model\\Car\">\n                <id field=\"name\" type=\"string\" />\n                <id field=\"year\" type=\"integer\" />\n            </entity>\n        </doctrine-mapping>\n\nNow you can use this entity:\n\n.. code-block:: php\n\n    <?php\n    namespace VehicleCatalogue\\Model;\n\n    // $em is the EntityManager\n\n    $car = new Car(\"Audi A8\", 2010);\n    $em->persist($car);\n    $em->flush();\n\nAnd for querying you can use arrays to both DQL and EntityRepositories:\n\n.. code-block:: php\n\n    <?php\n    namespace VehicleCatalogue\\Model;\n\n    // $em is the EntityManager\n    $audi = $em->find(\"VehicleCatalogue\\Model\\Car\", [\"name\" => \"Audi A8\", \"year\" => 2010]);\n\n    $dql = \"SELECT c FROM VehicleCatalogue\\Model\\Car c WHERE c.name = ?1 AND c.year = ?2\";\n    $audi = $em->createQuery($dql)\n               ->setParameter(1, \"Audi A8\")\n               ->setParameter(2, 2010)\n               ->getSingleResult();\n\nYou can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name``\nand to ``year`` to the related entities.\n\n.. note::\n\n    This example shows how you can nicely solve the requirement for existing\n    values before ``EntityManager#persist()``: By adding them as mandatory values for the constructor.\n\nIdentity through foreign Entities\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are tons of use-cases where the identity of an Entity should be determined by the entity\nof one or many parent entities.\n\n-   Dynamic Attributes of an Entity (for example Article). Each Article has many\n    attributes with primary key \"article_id\" and \"attribute_name\".\n-   Address object of a Person, the primary key of the address is \"user_id\". This is not a case of a composite primary\n    key, but the identity is derived through a foreign entity and a foreign key.\n-   Join Tables with metadata can be modelled as Entity, for example connections between two articles\n    with a little description and a score.\n\nThe semantics of mapping identity through foreign entities are easy:\n\n-   Only allowed on Many-To-One or One-To-One associations.\n-   Plug an ``#[Id]`` attribute onto every association.\n-   Set an attribute ``association-key`` with the field name of the association in XML.\n\nUse-Case 1: Dynamic Attributes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe keep up the example of an Article with arbitrary attributes, the mapping looks like this:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        namespace Application\\Model;\n\n        use Doctrine\\Common\\Collections\\ArrayCollection;\n\n        #[Entity]\n        class Article\n        {\n            #[Id, Column, GeneratedValue]\n            private int|null $id = null;\n            #[Column]\n            private string $title;\n\n            /** @var ArrayCollection<string, ArticleAttribute> */\n            #[OneToMany(targetEntity: ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')]\n            private Collection $attributes;\n\n            public function addAttribute(string $name, string $value): void\n            {\n                $this->attributes[$name] = new ArticleAttribute($name, $value, $this);\n            }\n        }\n\n        #[Entity]\n        class ArticleAttribute\n        {\n            #[Id, ManyToOne(targetEntity: Article::class, inversedBy: 'attributes')]\n            private Article $article;\n\n            #[Id, Column]\n            private string $attribute;\n\n            #[Column]\n            private string $value;\n\n            public function __construct(string $name, string $value, Article $article)\n            {\n                $this->attribute = $name;\n                $this->value = $value;\n                $this->article = $article;\n            }\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n              xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n              xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n             <entity name=\"Application\\Model\\ArticleAttribute\">\n                <id name=\"article\" association-key=\"true\" />\n                <id name=\"attribute\" type=\"string\" />\n\n                <field name=\"value\" type=\"string\" />\n\n                <many-to-one field=\"article\" target-entity=\"Article\" inversed-by=\"attributes\" />\n             <entity>\n\n        </doctrine-mapping>\n\nUse-Case 2: Simple Derived Identity\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes you have the requirement that two objects are related by a One-To-One association\nand that the dependent class should re-use the primary key of the class it depends on.\nOne good example for this is a user-address relationship:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n\n        #[Entity]\n        class User\n        {\n            #[Id, Column, GeneratedValue]\n            private int|null $id = null;\n        }\n\n        #[Entity]\n        class Address\n        {\n            #[Id, OneToOne(targetEntity: User::class)]\n            private User|null $user = null;\n        }\n\nUse-Case 3: Join-Table with Metadata\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn the classic order product shop example there is the concept of the order item\nwhich contains references to order and product and additional data such as the amount\nof products purchased and maybe even the current price.\n\n.. code-block:: php\n\n    <?php\n\n    use DateTime;\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n\n    #[Entity]\n    class Order\n    {\n        #[Id, Column, GeneratedValue]\n        private int|null $id = null;\n\n        /** @var ArrayCollection<int, OrderItem> */\n        #[OneToMany(targetEntity: OrderItem::class, mappedBy: 'order')]\n        private Collection $items;\n\n        #[Column]\n        private bool $paid = false;\n        #[Column]\n        private bool $shipped = false;\n        #[Column]\n        private DateTime $created;\n\n        public function __construct(\n            #[ManyToOne(targetEntity: Customer::class)]\n            private Customer $customer\n        ) {\n            $this->items = new ArrayCollection();\n            $this->created = new DateTime(\"now\");\n        }\n    }\n\n    #[Entity]\n    class Product\n    {\n        #[Id, Column, GeneratedValue]\n        private int|null $id = null;\n\n        #[Column]\n        private string $name;\n\n        #[Column]\n        private int $currentPrice;\n\n        public function getCurrentPrice(): int\n        {\n            return $this->currentPrice;\n        }\n    }\n\n    #[Entity]\n    class OrderItem\n    {\n        #[Id, ManyToOne(targetEntity: Order::class)]\n        private Order|null $order = null;\n\n        #[Id, ManyToOne(targetEntity: Product::class)]\n        private Product|null $product = null;\n\n        #[Column]\n        private int $amount = 1;\n\n        #[Column]\n        private int $offeredPrice;\n\n        public function __construct(Order $order, Product $product, int $amount = 1)\n        {\n            $this->order = $order;\n            $this->product = $product;\n            $this->offeredPrice = $product->getCurrentPrice();\n            $this->amount = $amount;\n        }\n    }\n\n\nPerformance Considerations\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nUsing composite keys always comes with a performance hit compared to using entities with\na simple surrogate key. This performance impact is mostly due to additional PHP code that is\nnecessary to handle this kind of keys, most notably when using derived identifiers.\n\nOn the SQL side there is not much overhead as no additional or unexpected queries have to be\nexecuted to manage entities with derived foreign keys.\n"
  },
  {
    "path": "docs/en/tutorials/embeddables.rst",
    "content": "Separating Concerns using Embeddables\n=====================================\n\nEmbeddables are classes which are not entities themselves, but are embedded\nin entities and can also be queried in DQL. You'll mostly want to use them\nto reduce duplication or separating concerns. Value objects such as date range\nor address are the primary use case for this feature.\n\n.. note::\n\n    Embeddables can only contain properties with basic ``@Column`` mapping.\n\nFor the purposes of this tutorial, we will assume that you have a ``User``\nclass in your application and you would like to store an address in\nthe ``User`` class. We will model the ``Address`` class as an embeddable\ninstead of simply adding the respective columns to the ``User`` class.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n\n        #[Entity]\n        class User\n        {\n            #[Embedded(class: Address::class)]\n            private Address $address;\n        }\n\n        #[Embeddable]\n        class Address\n        {\n            #[Column(type: \"string\")]\n            private string $street;\n\n            #[Column(type: \"string\")]\n            private string $postalCode;\n\n            #[Column(type: \"string\")]\n            private string $city;\n\n            #[Column(type: \"string\")]\n            private string $country;\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <embedded name=\"address\" class=\"Address\" />\n            </entity>\n\n            <embeddable name=\"Address\">\n                <field name=\"street\" type=\"string\" />\n                <field name=\"postalCode\" type=\"string\" />\n                <field name=\"city\" type=\"string\" />\n                <field name=\"country\" type=\"string\" />\n            </embeddable>\n        </doctrine-mapping>\n\nIn terms of your database schema, Doctrine will automatically inline all\ncolumns from the ``Address`` class into the table of the ``User`` class,\njust as if you had declared them directly there.\n\nInitializing embeddables\n------------------------\n\nIn case all fields in the embeddable are ``nullable``, you might want\nto initialize the embeddable, to avoid getting a null value instead of\nthe embedded object.\n\n.. code-block:: php\n\n    public function __construct()\n    {\n        $this->address = new Address();\n    }\n\nColumn Prefixing\n----------------\n\nBy default, Doctrine names your columns by prefixing them, using the value\nobject name.\n\nFollowing the example above, your columns would be named as ``address_street``,\n``address_postalCode``...\n\nYou can change this behaviour to meet your needs by changing the\n``columnPrefix`` attribute in the ``@Embedded`` notation.\n\nThe following example shows you how to set your prefix to ``myPrefix_``:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n\n        #[Entity]\n        class User\n        {\n            #[Embedded(class: Address::class, columnPrefix: \"myPrefix_\")]\n            private Address $address;\n        }\n\n    .. code-block:: xml\n\n        <entity name=\"User\">\n            <embedded name=\"address\" class=\"Address\" column-prefix=\"myPrefix_\" />\n        </entity>\n\nTo have Doctrine drop the prefix and use the value object's property name\ndirectly, set ``columnPrefix=false`` (``use-column-prefix=\"false\"`` for XML):\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n\n        #[Entity]\n        class User\n        {\n            #[Embedded(class: Address::class, columnPrefix: false)]\n            private Address $address;\n        }\n\n    .. code-block:: xml\n\n        <entity name=\"User\">\n            <embedded name=\"address\" class=\"Address\" use-column-prefix=\"false\" />\n        </entity>\n\n\nDQL\n---\n\nYou can also use mapped fields of embedded classes in DQL queries, just\nas if they were declared in the ``User`` class:\n\n.. code-block:: sql\n\n    SELECT u FROM User u WHERE u.address.city = :myCity\n"
  },
  {
    "path": "docs/en/tutorials/extra-lazy-associations.rst",
    "content": "Extra Lazy Associations\n=======================\n\n.. versionadded:: 2.1\n\nIn many cases associations between entities can get pretty large. Even in a simple scenario like a blog.\nwhere posts can be commented, you always have to assume that a post draws hundreds of comments.\nIn Doctrine ORM if you accessed an association it would always get loaded completely into memory. This\ncan lead to pretty serious performance problems, if your associations contain several hundreds or thousands\nof entities.\n\nDoctrine ORM includes a feature called **Extra Lazy** for associations. Associations\nare marked as **Lazy** by default, which means the whole collection object for an association is populated\nthe first time its accessed. If you mark an association as extra lazy the following methods on collections\ncan be called without triggering a full load of the collection:\n\n-  ``Collection#contains($entity)``\n-  ``Collection#containsKey($key)``\n-  ``Collection#count()``\n-  ``Collection#first()``\n-  ``Collection#get($key)``\n-  ``Collection#isEmpty()``\n-  ``Collection#slice($offset, $length = null)``\n\nFor each of the above methods the following semantics apply:\n\n-  For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database.\n-  For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed.\n\nAdditionally even with Doctrine ORM the following methods do not trigger the collection load:\n\n-  ``Collection#add($entity)``\n-  ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does\n   not work when setting specific keys like ``$coll[0] = $entity``.\n\nWith extra lazy collections you can now not only add entities to large collections but also paginate them\neasily using a combination of ``count`` and ``slice``.\n\n\n.. warning::\n\n   ``removeElement`` directly issued DELETE queries to the database from\n   version 2.4.0 to 2.7.0.  This circumvents the flush operation and might run\n   outside a transactional boundary if you don't create one yourself. We\n   consider this a critical bug in the assumption of how the ORM works and\n   reverted ``removeElement`` EXTRA_LAZY behavior in 2.7.1.\n\n\nEnabling Extra-Lazy Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe mapping configuration is simple. Instead of using the default value of ``fetch=\"LAZY\"`` you have to\nswitch to extra lazy as shown in these examples:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        namespace Doctrine\\Tests\\Models\\CMS;\n\n        #[Entity]\n        class CmsGroup\n        {\n            /** @var Collection<int, CmsUser> */\n            #[ManyToMany(targetEntity: CmsUser::class, mappedBy: 'groups', fetch: 'EXTRA_LAZY')]\n            public Collection $users;\n        }\n\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n              xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n              xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n            <entity name=\"Doctrine\\Tests\\Models\\CMS\\CmsGroup\">\n                <!-- ... -->\n                <many-to-many field=\"users\" target-entity=\"CmsUser\" mapped-by=\"groups\" fetch=\"EXTRA_LAZY\" />\n            </entity>\n        </doctrine-mapping>\n"
  },
  {
    "path": "docs/en/tutorials/getting-started.rst",
    "content": "Getting Started with Doctrine\n=============================\n\nThis guide covers getting started with the Doctrine ORM. After working\nthrough the guide you should know:\n\n- How to install and configure Doctrine by connecting it to a database\n- Mapping PHP objects to database tables\n- Generating a database schema from PHP objects\n- Using the ``EntityManager`` to insert, update, delete and find\n  objects in the database.\n\nGuide Assumptions\n-----------------\n\nThis guide is designed for beginners that haven't worked with Doctrine ORM\nbefore. There are some prerequisites for the tutorial that have to be\ninstalled:\n\n- PHP (latest stable version)\n- Composer Package Manager (\\ `Install Composer\n  <https://getcomposer.org/doc/00-intro.md>`_)\n\nThe code of this tutorial is `available on Github <https://github.com/doctrine/doctrine2-orm-tutorial>`_.\n\nWhat is Doctrine?\n-----------------\n\nDoctrine ORM is an `object-relational mapper (ORM) <https://en.wikipedia.org/wiki/Object-relational_mapping>`_\nfor PHP that provides transparent persistence for PHP objects. It uses the Data Mapper\npattern at the heart, aiming for a complete separation of your domain/business\nlogic from the persistence in a relational database management system.\n\nThe benefit of Doctrine for the programmer is the ability to focus\non the object-oriented business logic and worry about persistence only\nas a secondary problem. This doesn't mean persistence is downplayed by Doctrine\n2, however it is our belief that there are considerable benefits for\nobject-oriented programming if persistence and entities are kept\nseparated.\n\nWhat are Entities?\n~~~~~~~~~~~~~~~~~~\n\nEntities are PHP Objects that can be identified over many requests\nby a unique identifier or primary key. These classes don't need to extend any\nabstract base class or interface.\n\nAn entity contains persistable properties. A persistable property\nis an instance variable of the entity that is saved into and retrieved from the database\nby Doctrine's data mapping capabilities.\n\nAn entity class can be final or read-only when you use\n:ref:`native lazy objects <reference-native-lazy-objects>`.\nIt may contain final methods or read-only properties too.\n\nAn Example Model: Bug Tracker\n-----------------------------\n\nFor this Getting Started Guide for Doctrine we will implement the\nBug Tracker domain model from the\n`Zend_Db_Table <https://framework.zend.com/manual/1.12/en/zend.db.adapter.html>`_\ndocumentation. Reading their documentation we can extract the\nrequirements:\n\n-  A Bug has a description, creation date, status, reporter and\n   engineer\n-  A Bug can occur on different Products (platforms)\n-  A Product has a name.\n-  Bug reporters and engineers are both Users of the system.\n-  A User can create new Bugs.\n-  The assigned engineer can close a Bug.\n-  A User can see all their reported or assigned Bugs.\n-  Bugs can be paginated through a list-view.\n\nProject Setup\n-------------\n\nCreate a new empty folder for this tutorial project, for example\n``doctrine2-tutorial`` and create a new file ``composer.json`` inside\nthat directory with the following contents:\n\n::\n\n    {\n        \"require\": {\n            \"doctrine/orm\": \"^3\",\n            \"doctrine/dbal\": \"^4\",\n            \"symfony/cache\": \"^7\"\n        },\n        \"autoload\": {\n            \"psr-0\": {\"\": \"src/\"}\n        }\n    }\n\n\nInstall Doctrine using the Composer Dependency Management tool, by calling:\n\n::\n\n    $ composer install\n\nThis will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM,\ninto the ``vendor`` directory.\n\nAdd the following directories::\n\n    doctrine2-tutorial\n    |-- config\n    |   `-- xml\n    `-- src\n\n.. note::\n    It is strongly recommended that you require ``doctrine/dbal`` in your\n    ``composer.json`` as well, because using the ORM means mapping objects\n    and their fields to database tables and their columns, and that\n    requires mentioning so-called types that are defined in ``doctrine/dbal``\n    in your application. Having an explicit requirement means you control\n    when the upgrade to the next major version happens, so that you can\n    do the necessary changes in your application beforehand.\n\nObtaining the EntityManager\n---------------------------\n\nDoctrine's public interface is through the ``EntityManager``. This class\nprovides access points to the complete lifecycle management for your entities,\nand transforms entities from and back to persistence. You have to\nconfigure and create it to use your entities with Doctrine ORM. I\nwill show the configuration steps and then discuss them step by\nstep:\n\n.. code-block:: php\n\n    <?php\n    // bootstrap.php\n    use Doctrine\\DBAL\\DriverManager;\n    use Doctrine\\ORM\\EntityManager;\n    use Doctrine\\ORM\\ORMSetup;\n\n    require_once \"vendor/autoload.php\";\n\n    // Create a simple \"default\" Doctrine ORM configuration for Attributes\n    $config = ORMSetup::createAttributeMetadataConfig( // on PHP < 8.4, use ORMSetup::createAttributeMetadataConfiguration()\n        paths: [__DIR__ . '/src'],\n        isDevMode: true,\n    );\n    // or if you prefer XML\n    // $config = ORMSetup::createXMLMetadataConfig( // on PHP < 8.4, use ORMSetup::createXMLMetadataConfiguration()\n    //    paths: [__DIR__ . '/config/xml'],\n    //    isDevMode: true,\n    //);\n\n    // configuring the database connection\n    $connection = DriverManager::getConnection([\n        'driver' => 'pdo_sqlite',\n        'path' => __DIR__ . '/db.sqlite',\n    ], $config);\n\n    // obtaining the entity manager\n    $entityManager = new EntityManager($connection, $config);\n\nThe ``require_once`` statement sets up the class autoloading for Doctrine and\nits dependencies using Composer's autoloader.\n\nThe second block consists of the instantiation of the ORM\n``Configuration`` object using the ``ORMSetup`` helper. It assumes a bunch\nof defaults that you don't have to bother about for now. You can\nread up on the configuration details in the\n:doc:`reference chapter on configuration <../reference/configuration>`.\n\nThe third block shows the configuration options required to connect to\na database. In this case, we'll use a file-based SQLite database. All the\nconfiguration options for all the shipped drivers are given in the\n`DBAL Configuration section of the manual <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/>`_.\n\nThe last block shows how the ``EntityManager`` is obtained from a\nfactory method.\n\nGenerating the Database Schema\n------------------------------\n\nDoctrine has a command-line interface that allows you to access the SchemaTool,\na component that can generate a relational database schema based entirely on the\ndefined entity classes and their metadata. For this tool to work, you need to\ncreate an executable console script as described in the\n:doc:`tools chapter <../reference/tools>`.\n\nIf you created the ``bootstrap.php`` file as described in the previous section,\nthat script could look like this:\n\n.. code-block:: php\n\n    #!/usr/bin/env php\n    <?php\n    // bin/doctrine\n\n    use Doctrine\\ORM\\Tools\\Console\\ConsoleRunner;\n    use Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\n\n    // Adjust this path to your actual bootstrap.php\n    require __DIR__ . 'path/to/your/bootstrap.php';\n\n    ConsoleRunner::run(\n        new SingleManagerProvider($entityManager)\n    );\n\nIn the following examples, we will assume that this script has been created as\n``bin/doctrine``.\n\n::\n\n    $ php bin/doctrine orm:schema-tool:create\n\nSince we haven't added any entity metadata in ``src`` yet, you'll see a message\nstating \"No Metadata Classes to process.\" In the next section, we'll create a\nProduct entity along with the corresponding metadata, and run this command again.\n\nNote that as you modify your entities' metadata during the development process,\nyou'll need to update your database schema to stay in sync with the metadata.\nYou can easily recreate the database using the following commands:\n\n::\n\n    $ php bin/doctrine orm:schema-tool:drop --force\n    $ php bin/doctrine orm:schema-tool:create\n\nOr you can use the update functionality:\n\n::\n\n    $ php bin/doctrine orm:schema-tool:update --force\n\nThe updating of databases uses a diff algorithm for a given\ndatabase schema. This is a cornerstone of the ``Doctrine\\DBAL`` package,\nwhich can even be used without the Doctrine ORM package.\n\nStarting with the Product Entity\n--------------------------------\n\nWe start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product``\nentity definition:\n\n.. code-block:: php\n\n    <?php\n    // src/Product.php\n    class Product\n    {\n        private int|null $id = null;\n        private string $name;\n    }\n\nWhen creating entity classes, all of the fields should be ``private``.\n\nUse ``protected`` when strictly needed and very rarely if not ever ``public``.\n\nAdding behavior to Entities\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are two options to define methods in entities:\n**getters/setters**, or **mutators and DTOs**,\nrespectively for **anemic entities** or **rich entities**.\n\n**Anemic entities: Getters and setters**\n\nThe most popular method is to create two kinds of methods to\n**read** (getter) and **update** (setter) the object's properties.\n\nThe id field has no setter since, generally speaking, your code\nshould not set this value since it represents a database id value.\n(Note that Doctrine itself can still set the value using the\nReflection API instead of a defined setter function.)\n\n.. note::\n\n    Doctrine ORM does not use any of the methods you defined: it uses\n    reflection to read and write values to your objects, and will never\n    call methods, not even ``__construct``.\n\nThis approach is mostly used when you want to focus on behavior-less\nentities, and when you want to have all your business logic in your\nservices rather than in the objects themselves.\n\nGetters and setters are a common convention which makes it possible to\nexpose each field of your entity to the external world, while allowing\nyou to keep some type safety in place.\n\nSuch an approach is a good choice for RAD (rapid application development),\nbut may lead to problems later down the road, because providing such an\neasy way to modify any field in your entity means that the entity itself\ncannot guarantee validity of its internal state. Having any object in\ninvalid state is dangerous:\n\n- An invalid state can bring bugs in your business logic.\n- The state can be implicitly saved in the database: any forgotten ``flush``\n  can persist the broken state.\n- If persisted, the corrupted data will be retrieved later in your application\n  when the data is loaded again, thereby leading to bugs in your business logic.\n- When bugs occur after corrupted data is persisted, troubleshooting will\n  become much harder, and you might be aware of the bug too late to fix it in a\n  proper manner.\n\nimplicitly saved in database, thereby leading to corrupted or inconsistent\ndata in your storage, and later in your application when the data is loaded again.\n\n.. note::\n\n    This method, although very common, is inappropriate for Domain Driven\n    Design (\\ `DDD <https://en.wikipedia.org/wiki/Domain-driven_design>`_)\n    where methods should represent real business operations and not simple\n    property change, And business invariants should be maintained both in the\n    application state (entities in this case) and in the database, with no\n    space for data corruption.\n\nHere is an example of a simple **anemic entity**:\n\n.. configuration-block::\n\n    .. code-block:: php\n\n        <?php\n        class User\n        {\n            private $username;\n            private $passwordHash;\n            private $bans;\n\n            public function getUsername(): string\n            {\n                return $this->username;\n            }\n\n            public function setUsername(string $username): void\n            {\n                $this->username = $username;\n            }\n\n            public function getPasswordHash(): string\n            {\n                return $this->passwordHash;\n            }\n\n            public function setPasswordHash(string $passwordHash): void\n            {\n                $this->passwordHash = $passwordHash;\n            }\n\n            public function getBans(): array\n            {\n                return $this->bans;\n            }\n\n            public function addBan(Ban $ban): void\n            {\n                $this->bans[] = $ban;\n            }\n        }\n\nIn the example above, we avoid all possible logic in the entity and only care\nabout putting and retrieving data into it without validation (except the one\nprovided by type-hints) nor consideration about the object's state.\n\nAs Doctrine ORM is a persistence tool for your domain, the state of an object is\nreally important. This is why we strongly recommend using rich entities.\n\n**Rich entities: Mutators and DTOs**\n\nWe recommend using a rich entity design and rely on more complex mutators,\nand if needed based on DTOs.\nIn this design, you should **not** use getters nor setters, and instead,\nimplement methods that represent the **behavior** of your domain.\n\nFor example, when having a ``User`` entity, we could foresee\nthe following kind of optimization.\n\nExample of a rich entity with proper accessors and mutators:\n\n.. configuration-block::\n\n    .. code-block:: php\n        <?php\n        class User\n        {\n            private $banned;\n            private $username;\n            private $passwordHash;\n            private $bans;\n\n            public function toNickname(): string\n            {\n                return $this->username;\n            }\n\n            public function authenticate(string $password, callable $checkHash): bool\n            {\n                return $checkHash($password, $this->passwordHash) && ! $this->hasActiveBans();\n            }\n\n            public function changePassword(string $password, callable $hash): void\n            {\n                $this->passwordHash = $hash($password);\n            }\n\n            public function ban(\\DateInterval $duration): void\n            {\n                assert($duration->invert !== 1);\n\n                $this->bans[] = new Ban($this);\n            }\n        }\n\n.. note::\n\n    Please note that this example is only a stub. When going further in the\n    documentation, we will update this object with more behavior and maybe\n    update some methods.\n\nThe entities should only mutate state after checking that all business logic\ninvariants are being respected.\nAdditionally, our entities should never see their state change without\nvalidation. For example, creating a ``new Product()`` object without any data\nmakes it an **invalid object**.\nRich entities should represent **behavior**, not **data**, therefore\nthey should be valid even after a ``__construct()`` call.\n\nTo help creating such objects, we can rely on ``DTOs``, and/or make\nour entities always up-to-date. This can be performed with static constructors,\nor rich mutators that accept ``DTOs`` as parameters.\n\nThe role of the ``DTO`` is to maintain the entity's state and to help us rely\nupon objects that correctly represent the data that is used to mutate the\nentity.\n\n.. note::\n\n    A `DTO <https://en.wikipedia.org/wiki/Data_transfer_object>`_ is an object\n    that only carries data without any logic. Its only goal is to be transferred\n    from one service to another.\n    A ``DTO`` often represents data sent by a client and that has to be validated,\n    but can also be used as simple data carrier for other cases.\n\nBy using ``DTOs``, if we take our previous ``User`` example, we could create\na ``ProfileEditingForm`` DTO that will be a plain model, totally unrelated to\nour database, that will be populated via a form and validated.\nThen we can add a new mutator to our ``User``:\n\n.. configuration-block::\n\n    .. code-block:: php\n        <?php\n        class User\n        {\n            public function updateFromProfile(ProfileEditingDTO $profileFormDTO): void\n            {\n                // ...\n            }\n\n            public static function createFromRegistration(UserRegistrationDTO $registrationDTO): self\n            {\n                // ...\n            }\n        }\n\nThere are several advantages to using such a model:\n\n* **Entity state is always valid.** Since no setters exist, this means that we\nonly update portions of the entity that should already be valid.\n\n* Instead of having plain getters and setters, our entity now has\n**real behavior**: it is much easier to determine the logic in the domain.\n\n* DTOs can be reused in other components, for example deserializing mixed\ncontent, using forms...\n\n* Classic and static constructors can be used to manage different ways to\ncreate our objects, and they can also use DTOs.\n\n* Anemic entities tend to isolate the entity from logic, whereas rich\nentities allow putting the logic in the object itself, including data\nvalidation.\n\nThe next step for persistence with Doctrine is to describe the structure of\nthe ``Product`` entity to Doctrine using a metadata language. The metadata\nlanguage describes how entities, their properties and references should be\npersisted and what constraints should be applied to them.\n\nMetadata for an Entity can be configured using attributes directly in\nthe Entity class itself, or in an external XML file. This\nGetting Started guide will demonstrate metadata mappings using both\nmethods, but you only need to choose one.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        // src/Product.php\n\n        use Doctrine\\ORM\\Mapping as ORM;\n\n        #[ORM\\Entity]\n        #[ORM\\Table(name: 'products')]\n        class Product\n        {\n            #[ORM\\Id]\n            #[ORM\\Column(type: 'integer')]\n            #[ORM\\GeneratedValue]\n            private int|null $id = null;\n            #[ORM\\Column(type: 'string')]\n            private string $name;\n\n            // .. (other code)\n        }\n\n    .. code-block:: xml\n\n        <!-- config/xml/Product.dcm.xml -->\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n              <entity name=\"Product\" table=\"products\">\n                  <id name=\"id\" type=\"integer\">\n                      <generator strategy=\"AUTO\" />\n                  </id>\n\n                  <field name=\"name\" type=\"string\" />\n              </entity>\n        </doctrine-mapping>\n\nThe top-level ``entity`` definition specifies information about\nthe class and table name. The primitive type ``Product#name`` is\ndefined as a ``field`` attribute. The ``id`` property is defined with\nthe ``id`` tag.  It has a ``generator`` tag nested inside, which\nspecifies that the primary key generation mechanism should automatically\nuse the database platform's native id generation strategy (for\nexample, AUTO INCREMENT in the case of MySql, or Sequences in the\ncase of PostgreSQL and Oracle).\n\nNow that we have defined our first entity and its metadata,\nlet's update the database schema:\n\n::\n\n    $ php bin/doctrine orm:schema-tool:update --force --dump-sql\n\nSpecifying both flags ``--force`` and ``--dump-sql`` will cause the DDL\nstatements to be executed and then printed to the screen.\n\nNow, we'll create a new script to insert products into the database:\n\n.. code-block:: php\n\n    <?php\n    // create_product.php <name>\n    require_once \"bootstrap.php\";\n\n    $newProductName = $argv[1];\n\n    $product = new Product();\n    $product->setName($newProductName);\n\n    $entityManager->persist($product);\n    $entityManager->flush();\n\n    echo \"Created Product with ID \" . $product->getId() . \"\\n\";\n\nCall this script from the command-line to see how new products are created:\n\n::\n\n    $ php create_product.php ORM\n    $ php create_product.php DBAL\n\nWhat is happening here? Using the ``Product`` class is pretty standard OOP.\nThe interesting bits are the use of the ``EntityManager`` service. To\nnotify the EntityManager that a new entity should be inserted into the database,\nyou have to call ``persist()``. To initiate a transaction to actually *perform*\nthe insertion, you have to explicitly call ``flush()`` on the ``EntityManager``.\n\nThis distinction between persist and flush is what allows the aggregation of\nall database writes (INSERT, UPDATE, DELETE) into one single transaction, which\nis executed when ``flush()`` is called. Using this approach, the write-performance\nis significantly better than in a scenario in which writes are performed on\neach entity in isolation.\n\nNext, we'll fetch a list of all the Products in the database. Let's create a\nnew script for this:\n\n.. code-block:: php\n\n    <?php\n    // list_products.php\n    require_once \"bootstrap.php\";\n\n    $productRepository = $entityManager->getRepository('Product');\n    $products = $productRepository->findAll();\n\n    foreach ($products as $product) {\n        echo sprintf(\"-%s\\n\", $product->getName());\n    }\n\nThe ``EntityManager#getRepository()`` method can create a finder object (called\na repository) for every type of entity. It is provided by Doctrine and contains\nsome finder methods like ``findAll()``.\n\nLet's continue by creating a script to display the name of a product based on its ID:\n\n.. code-block:: php\n\n    <?php\n    // show_product.php <id>\n    require_once \"bootstrap.php\";\n\n    $id = $argv[1];\n    $product = $entityManager->find('Product', $id);\n\n    if ($product === null) {\n        echo \"No product found.\\n\";\n        exit(1);\n    }\n\n    echo sprintf(\"-%s\\n\", $product->getName());\n\nNext we'll update a product's name, given its id. This simple example will\nhelp demonstrate Doctrine's implementation of the :ref:`UnitOfWork pattern <unit-of-work>`. Doctrine\nkeeps track of all the entities that were retrieved from the Entity Manager,\nand can detect when any of those entities' properties have been modified.\nAs a result, rather than needing to call ``persist($entity)`` for each individual\nentity whose properties were changed, a single call to ``flush()`` at the end of a\nrequest is sufficient to update the database for all of the modified entities.\n\n.. code-block:: php\n\n    <?php\n    // update_product.php <id> <new-name>\n    require_once \"bootstrap.php\";\n\n    $id = $argv[1];\n    $newName = $argv[2];\n\n    $product = $entityManager->find('Product', $id);\n\n    if ($product === null) {\n        echo \"Product $id does not exist.\\n\";\n        exit(1);\n    }\n\n    $product->setName($newName);\n\n    $entityManager->flush();\n\nAfter calling this script on one of the existing products, you can verify the\nproduct name changed by calling the ``show_product.php`` script.\n\nAdding Bug and User Entities\n----------------------------\n\nWe continue with the bug tracker example by creating the ``Bug`` and ``User``\nclasses. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.\n\n.. code-block:: php\n\n    <?php\n    // src/Bug.php\n\n    use Doctrine\\ORM\\Mapping as ORM;\n\n    #[ORM\\Entity]\n    #[ORM\\Table(name: 'bugs')]\n    class Bug\n    {\n        #[ORM\\Id]\n        #[ORM\\Column(type: 'integer')]\n        #[ORM\\GeneratedValue]\n        private int|null $id;\n\n        #[ORM\\Column(type: 'string')]\n        private string $description;\n\n        #[ORM\\Column(type: 'datetime')]\n        private DateTime $created;\n\n        #[ORM\\Column(type: 'string')]\n        private string $status;\n\n        public function getId(): int|null\n        {\n            return $this->id;\n        }\n\n        public function getDescription(): string\n        {\n            return $this->description;\n        }\n\n        public function setDescription(string $description): void\n        {\n            $this->description = $description;\n        }\n\n        public function setCreated(DateTime $created)\n        {\n            $this->created = $created;\n        }\n\n        public function getCreated(): DateTime\n        {\n            return $this->created;\n        }\n\n        public function setStatus($status): void\n        {\n            $this->status = $status;\n        }\n\n        public function getStatus():string\n        {\n            return $this->status;\n        }\n    }\n\n.. code-block:: php\n\n    <?php\n    // src/User.php\n\n    use Doctrine\\ORM\\Mapping as ORM;\n\n    #[ORM\\Entity]\n    #[ORM\\Table(name: 'users')]\n    class User\n    {\n        /** @var int */\n        #[ORM\\Id]\n        #[ORM\\GeneratedValue]\n        #[ORM\\Column(type: 'integer')]\n        private int|null $id = null;\n\n        /** @var string */\n        #[ORM\\Column(type: 'string')]\n        private string $name;\n\n        public function getId(): int|null\n        {\n            return $this->id;\n        }\n\n        public function getName(): string\n        {\n            return $this->name;\n        }\n\n        public function setName(string $name): void\n        {\n            $this->name = $name;\n        }\n    }\n\nAll of the properties we've seen so far are of simple types (integer, string,\nand datetime). But now, we'll add properties that will store objects of\nspecific *entity types* in order to model the relationships between different\nentities.\n\nAt the database level, relationships between entities are represented by foreign\nkeys. But with Doctrine, you'll never have to (and never should) work with\nthe foreign keys directly. You should only work with objects that represent\nforeign keys through their own identities.\n\nFor every foreign key you either have a Doctrine ManyToOne or OneToOne\nassociation. On the inverse sides of these foreign keys you can have\nOneToMany associations. Obviously you can have ManyToMany associations\nthat connect two tables with each other through a join table with\ntwo foreign keys.\n\nNow that you know the basics about references in Doctrine, we can extend the\ndomain model to match the requirements:\n\n.. code-block:: php\n\n    <?php\n    // src/Bug.php\n\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n    use Doctrine\\Common\\Collections\\Collection;\n\n    class Bug\n    {\n        // ... (previous code)\n\n        /** @var Collection<int, Product> */\n        private Collection $products;\n\n        public function __construct()\n        {\n            $this->products = new ArrayCollection();\n        }\n    }\n\n.. code-block:: php\n\n    <?php\n    // src/User.php\n    use Doctrine\\Common\\Collections\\ArrayCollection;\n\n    class User\n    {\n        // ... (previous code)\n\n        /** @var Collection<int, Bug> */\n        private Collection $reportedBugs;\n        /** @var Collection<int, Bug> */\n        private Collection $assignedBugs;\n\n        public function __construct()\n        {\n            $this->reportedBugs = new ArrayCollection();\n            $this->assignedBugs = new ArrayCollection();\n        }\n    }\n\n.. note::\n\n    Whenever an entity is created from the database, a ``Collection``\n    implementation of the type ``PersistentCollection`` will be injected into\n    your entity instead of an ``ArrayCollection``. This helps Doctrine ORM\n    understand the changes that have happened to the collection that are\n    noteworthy for persistence.\n\nBecause we only work with collections for the references we must be\ncareful to implement a bidirectional reference in the domain model.\nThe concept of owning or inverse side of a relation is central to\nthis notion and should always be kept in mind. The following\nassumptions are made about relations and have to be followed to be\nable to work with Doctrine ORM. These assumptions are not unique to\nDoctrine ORM but are best practices in handling database relations\nand Object-Relational Mapping.\n\n-  In a one-to-one relation, the entity holding the foreign key of\n   the related entity on its own database table is *always* the owning\n   side of the relation.\n-  In a many-to-one relation, the Many-side is the owning side by\n   default because it holds the foreign key. Accordingly, the One-side\n   is the inverse side by default.\n-  In a many-to-one relation, the One-side can only be the owning side if\n   the relation is implemented as a ManyToMany with a join table, and the\n   One-side is restricted to allow only UNIQUE values per database constraint.\n-  In a many-to-many relation, both sides can be the owning side of\n   the relation. However, in a bi-directional many-to-many relation,\n   only one side is allowed to be the owning side.\n-  Changes to Collections are saved or updated, when the entity on\n   the *owning* side of the collection is saved or updated.\n-  Saving an Entity at the inverse side of a relation never\n   triggers a persist operation to changes to the collection.\n\n.. note::\n\n    Consistency of bi-directional references on the inverse side of a\n    relation have to be managed in userland application code. Doctrine\n    cannot magically update your collections to be consistent.\n\n\nIn the case of Users and Bugs we have references back and forth to\nthe assigned and reported bugs from a user, making this relation\nbi-directional. We have to change the code to ensure consistency of\nthe bi-directional reference:\n\n.. code-block:: php\n\n    <?php\n    // src/Bug.php\n    class Bug\n    {\n        // ... (previous code)\n\n        private User $engineer;\n        private User $reporter;\n\n        public function setEngineer(User $engineer): void\n        {\n            $engineer->assignedToBug($this);\n            $this->engineer = $engineer;\n        }\n\n        public function setReporter(User $reporter): void\n        {\n            $reporter->addReportedBug($this);\n            $this->reporter = $reporter;\n        }\n\n        public function getEngineer(): User\n        {\n            return $this->engineer;\n        }\n\n        public function getReporter(): User\n        {\n            return $this->reporter;\n        }\n    }\n\n.. code-block:: php\n\n    <?php\n    // src/User.php\n    class User\n    {\n        // ... (previous code)\n\n        /** @var Collection<int, Bug> */\n        private Collection $reportedBugs;\n        /** @var Collection<int, Bug> */\n        private Collection $assignedBugs;\n\n        public function addReportedBug(Bug $bug): void\n        {\n            $this->reportedBugs[] = $bug;\n        }\n\n        public function assignedToBug(Bug $bug): void\n        {\n            $this->assignedBugs[] = $bug;\n        }\n    }\n\nI chose to name the inverse methods in past-tense, which should\nindicate that the actual assigning has already taken place and the\nmethods are only used for ensuring consistency of the references.\nThis approach is my personal preference, you can choose whatever\nmethod to make this work.\n\nYou can see from ``User#addReportedBug()`` and\n``User#assignedToBug()`` that using this method in userland alone\nwould not add the Bug to the collection of the owning side in\n``Bug#reporter`` or ``Bug#engineer``. Using these methods and\ncalling Doctrine for persistence would not update the Collections'\nrepresentation in the database.\n\nOnly using ``Bug#setEngineer()`` or ``Bug#setReporter()``\ncorrectly saves the relation information.\n\nThe ``Bug#reporter`` and ``Bug#engineer`` properties are\nMany-To-One relations, which point to a User. In a normalized\nrelational model, the foreign key is saved on the Bug's table, hence\nin our object-relation model the Bug is at the owning side of the\nrelation. You should always make sure that the use-cases of your\ndomain model should drive which side is an inverse or owning one in\nyour Doctrine mapping. In our example, whenever a new bug is saved\nor an engineer is assigned to the bug, we don't want to update the\nUser to persist the reference, but the Bug. This is the case with\nthe Bug being at the owning side of the relation.\n\nBugs reference Products by a uni-directional ManyToMany relation in\nthe database that points from Bugs to Products.\n\n.. code-block:: php\n\n    <?php\n    // src/Bug.php\n    class Bug\n    {\n        // ... (previous code)\n\n        /** @var Collection<int, Product> */\n        private Collection $products;\n\n        public function assignToProduct(Product $product): void\n        {\n            $this->products[] = $product;\n        }\n\n        /** @return Collection<int, Product> */\n        public function getProducts(): Collection\n        {\n            return $this->products;\n        }\n    }\n\nWe are now finished with the domain model given the requirements.\nLets add metadata mappings for the ``Bug`` entity, as we did for\nthe ``Product`` before:\n\n.. configuration-block::\n    .. code-block:: attribute\n\n        <?php\n        // src/Bug.php\n\n        use DateTime;\n        use Doctrine\\ORM\\Mapping as ORM;\n\n        #[ORM\\Entity]\n        #[ORM\\Table(name: 'bugs')]\n        class Bug\n        {\n            #[ORM\\Id]\n            #[ORM\\Column(type: 'integer')]\n            #[ORM\\GeneratedValue]\n            private int|null $id = null;\n\n            #[ORM\\Column(type: 'string')]\n            private string $description;\n\n            #[ORM\\Column(type: 'datetime')]\n            private DateTime $created;\n\n            #[ORM\\Column(type: 'string')]\n            private string $status;\n\n            #[ORM\\ManyToOne(targetEntity: User::class, inversedBy: 'assignedBugs')]\n            private User|null $engineer = null;\n\n            #[ORM\\ManyToOne(targetEntity: User::class, inversedBy: 'reportedBugs')]\n            private User|null $reporter;\n\n            /** @var Collection<int, Product> */\n            #[ORM\\ManyToMany(targetEntity: Product::class)]\n            private Collection $products;\n\n            // ... (other code)\n        }\n\n    .. code-block:: xml\n\n        <!-- config/xml/Bug.dcm.xml -->\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n            <entity name=\"Bug\" table=\"bugs\">\n                <id name=\"id\" type=\"integer\">\n                    <generator strategy=\"AUTO\" />\n                </id>\n\n                <field name=\"description\" type=\"text\" />\n                <field name=\"created\" type=\"datetime\" />\n                <field name=\"status\" type=\"string\" />\n\n                <many-to-one target-entity=\"User\" field=\"reporter\" inversed-by=\"reportedBugs\" />\n                <many-to-one target-entity=\"User\" field=\"engineer\" inversed-by=\"assignedBugs\" />\n\n                <many-to-many target-entity=\"Product\" field=\"products\" />\n            </entity>\n        </doctrine-mapping>\n\nHere we have the entity, id and primitive type definitions.\nFor the \"created\" field we have used the ``datetime`` type,\nwhich translates the YYYY-mm-dd HH:mm:ss database format\ninto a PHP DateTime instance and back.\n\nAfter the field definitions, the two qualified references to the\nuser entity are defined. They are created by the ``ManyToOne``\nattribute. The class name of the related entity has to be specified with\nthe ``targetEntity`` parameter, which is enough information for\nthe database mapper to access the foreign table. Since\n``reporter`` and ``engineer`` are on the owning side of a\nbi-directional relation, we also have to specify the ``inversedBy``\nparameter. They have to point to the field names on the inverse\nside of the relationship. We will see in the next example that the ``inversedBy``\nparameter has a counterpart ``mappedBy`` which makes that\nthe inverse side.\n\nThe last definition is for the ``Bug#products`` collection. It\nholds all products where the specific bug occurs. Again\nyou have to define the ``targetEntity`` and ``field`` parameters\non the ``ManyToMany`` attribute.\n\nFinally, we'll add metadata mappings for the ``User`` entity.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        // src/User.php\n\n        use Doctrine\\ORM\\Mapping as ORM;\n\n        #[ORM\\Entity]\n        #[ORM\\Table(name: 'users')]\n        class User\n        {\n            #[ORM\\Id]\n            #[ORM\\GeneratedValue]\n            #[ORM\\Column(type: 'integer')]\n            private int|null $id = null;\n\n            #[ORM\\Column(type: 'string')]\n            private string $name;\n\n            /** @var Collection<int, Bug> An ArrayCollection of Bug objects. */\n            #[ORM\\OneToMany(targetEntity: Bug::class, mappedBy: 'reporter')]\n            private Collection $reportedBugs;\n\n            /** @var Collection<int,Bug> An ArrayCollection of Bug objects. */\n            #[ORM\\OneToMany(targetEntity: Bug::class, mappedBy: 'engineer')]\n            private $assignedBugs;\n\n            // .. (other code)\n        }\n\n    .. code-block:: xml\n\n        <!-- config/xml/User.dcm.xml -->\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                          xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                              https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n             <entity name=\"User\" table=\"users\">\n                 <id name=\"id\" type=\"integer\">\n                     <generator strategy=\"AUTO\" />\n                 </id>\n\n                 <field name=\"name\" type=\"string\" />\n\n                 <one-to-many target-entity=\"Bug\" field=\"reportedBugs\" mapped-by=\"reporter\" />\n                 <one-to-many target-entity=\"Bug\" field=\"assignedBugs\" mapped-by=\"engineer\" />\n             </entity>\n        </doctrine-mapping>\n\nHere are some new things to mention about the ``one-to-many`` tags.\nRemember that we discussed about the inverse and owning side. Now\nboth reportedBugs and assignedBugs are inverse relations, which\nmeans the join details have already been defined on the owning\nside. Therefore we only have to specify the property on the Bug\nclass that holds the owning sides.\n\nUpdate your database schema by running::\n\n    $ php bin/doctrine orm:schema-tool:update --force\n\n\nImplementing more Requirements\n------------------------------\n\nSo far, we've seen the most basic features of the metadata definition language.\nTo explore additional functionality, let's first create new ``User`` entities:\n\n.. code-block:: php\n\n    <?php\n    // create_user.php\n    require_once \"bootstrap.php\";\n\n    $newUsername = $argv[1];\n\n    $user = new User();\n    $user->setName($newUsername);\n\n    $entityManager->persist($user);\n    $entityManager->flush();\n\n    echo \"Created User with ID \" . $user->getId() . \"\\n\";\n\nNow call:\n\n::\n\n    $ php create_user.php beberlei\n\nWe now have the necessary data to create a new Bug entity:\n\n.. code-block:: php\n\n    <?php\n    // create_bug.php <reporter-id> <engineer-id> <product-ids>\n    require_once \"bootstrap.php\";\n\n    $reporterId = $argv[1];\n    $engineerId = $argv[2];\n    $productIds = explode(\",\", $argv[3]);\n\n    $reporter = $entityManager->find(\"User\", $reporterId);\n    $engineer = $entityManager->find(\"User\", $engineerId);\n    if (!$reporter || !$engineer) {\n        echo \"No reporter and/or engineer found for the given id(s).\\n\";\n        exit(1);\n    }\n\n    $bug = new Bug();\n    $bug->setDescription(\"Something does not work!\");\n    $bug->setCreated(new DateTime(\"now\"));\n    $bug->setStatus(\"OPEN\");\n\n    foreach ($productIds as $productId) {\n        $product = $entityManager->find(\"Product\", $productId);\n        $bug->assignToProduct($product);\n    }\n\n    $bug->setReporter($reporter);\n    $bug->setEngineer($engineer);\n\n    $entityManager->persist($bug);\n    $entityManager->flush();\n\n    echo \"Your new Bug Id: \".$bug->getId().\"\\n\";\n\nSince we only have one user and product, probably with the ID of 1, we can\ncall this script as follows:\n\n::\n\n    php create_bug.php 1 1 1\n\nSee how simple it is to relate a Bug, Reporter, Engineer and Products?\nAlso recall that thanks to the :ref:`UnitOfWork pattern <unit-of-work>`, Doctrine will detect\nthese relations and update all of the modified entities in the database\nautomatically when ``flush()`` is called.\n\nQueries for Application Use-Cases\n---------------------------------\n\nList of Bugs\n~~~~~~~~~~~~\n\nUsing the previous examples we can fill up the database quite a\nbit. However, we now need to discuss how to query the underlying\nmapper for the required view representations. When opening the\napplication, bugs can be paginated through a list-view, which is\nthe first read-only use-case:\n\n.. code-block:: php\n\n    <?php\n    // list_bugs.php\n    require_once \"bootstrap.php\";\n\n    $dql = \"SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC\";\n\n    $query = $entityManager->createQuery($dql);\n    $query->setMaxResults(30);\n    $bugs = $query->getResult();\n\n    foreach ($bugs as $bug) {\n        echo $bug->getDescription().\" - \".$bug->getCreated()->format('d.m.Y').\"\\n\";\n        echo \"    Reported by: \".$bug->getReporter()->getName().\"\\n\";\n        echo \"    Assigned to: \".$bug->getEngineer()->getName().\"\\n\";\n        foreach ($bug->getProducts() as $product) {\n            echo \"    Platform: \".$product->getName().\"\\n\";\n        }\n        echo \"\\n\";\n    }\n\nThe DQL Query in this example fetches the 30 most recent bugs with\ntheir respective engineer and reporter in one single SQL statement.\nThe console output of this script is then:\n\n::\n\n    Something does not work! - 02.04.2010\n        Reported by: beberlei\n        Assigned to: beberlei\n        Platform: My Product\n\n.. note::\n\n    **DQL is not SQL**\n\n    You may wonder why we start writing SQL at the beginning of this\n    use-case. Don't we use an ORM to get rid of all the endless\n    hand-writing of SQL? Doctrine introduces DQL which is best\n    described as **object-query-language** and is a dialect of\n    `OQL <https://en.wikipedia.org/wiki/Object_Query_Language>`_ and\n    similar to `HQL <http://www.hibernate.org>`_ or\n    `JPQL <https://en.wikipedia.org/wiki/Java_Persistence_Query_Language>`_.\n    It does not know the concept of columns and tables, but only those\n    of Entity-Class and property. Using the Metadata we defined before\n    it allows for very short distinctive and powerful queries.\n\n\n    An important reason why DQL is favourable to the Query API of most\n    ORMs is its similarity to SQL. The DQL language allows query\n    constructs that most ORMs don't: GROUP BY even with HAVING,\n    Sub-selects, Fetch-Joins of nested classes, mixed results with\n    entities and scalar data such as COUNT() results and much more.\n    Using DQL you should seldom come to the point where you want to\n    throw your ORM into the dumpster, because it doesn't support some\n    the more powerful SQL concepts.\n\n    If you need to build your query dynamically, you can use the ``QueryBuilder`` retrieved\n    by calling ``$entityManager->createQueryBuilder()``. There are more\n    details about this in the relevant part of the documentation.\n\n\n    As a last resort you can still use Native SQL and a description of the\n    result set to retrieve entities from the database. DQL boils down to a\n    Native SQL statement and a ``ResultSetMapping`` instance itself. Using\n    Native SQL you could even use stored procedures for data retrieval, or\n    make use of advanced non-portable database queries like PostgreSQL's\n    recursive queries.\n\n\nArray Hydration of the Bug List\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn the previous use-case we retrieved the results as their\nrespective object instances. We are not limited to retrieving\nobjects only from Doctrine however. For a simple list view like the\nprevious one we only need read access to our entities and can\nswitch the hydration from objects to simple PHP arrays instead.\n\nHydration can be an expensive process so only retrieving what you need can\nyield considerable performance benefits for read-only requests.\n\nImplementing the same list view as before using array hydration we\ncan rewrite our code:\n\n.. code-block:: php\n\n    <?php\n    // list_bugs_array.php\n    require_once \"bootstrap.php\";\n\n    $dql = \"SELECT b, e, r, p FROM Bug b JOIN b.engineer e \".\n           \"JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC\";\n    $query = $entityManager->createQuery($dql);\n    $bugs = $query->getArrayResult();\n\n    foreach ($bugs as $bug) {\n        echo $bug['description'] . \" - \" . $bug['created']->format('d.m.Y').\"\\n\";\n        echo \"    Reported by: \".$bug['reporter']['name'].\"\\n\";\n        echo \"    Assigned to: \".$bug['engineer']['name'].\"\\n\";\n        foreach ($bug['products'] as $product) {\n            echo \"    Platform: \".$product['name'].\"\\n\";\n        }\n        echo \"\\n\";\n    }\n\nThere is one significant difference in the DQL query however, we\nhave to add an additional fetch-join for the products connected to\na bug. The resulting SQL query for this single select statement is\npretty large, however still more efficient to retrieve compared to\nhydrating objects.\n\nFind by Primary Key\n~~~~~~~~~~~~~~~~~~~\n\nThe next Use-Case is displaying a Bug by primary key. This could be\ndone using DQL as in the previous example with a where clause,\nhowever there is a convenience method on the ``EntityManager`` that\nhandles loading by primary key, which we have already seen in the\nwrite scenarios:\n\n.. code-block:: php\n\n    <?php\n    // show_bug.php <id>\n    require_once \"bootstrap.php\";\n\n    $theBugId = $argv[1];\n\n    $bug = $entityManager->find(\"Bug\", (int)$theBugId);\n\n    echo \"Bug: \".$bug->getDescription().\"\\n\";\n    echo \"Engineer: \".$bug->getEngineer()->getName().\"\\n\";\n\nThe output of the engineer’s name is fetched from the database! What is happening?\n\nSince we only retrieved the bug by primary key both the engineer and reporter\nare not immediately loaded from the database but are replaced by LazyLoading\nproxies. These proxies will load behind the scenes, when attempting to access\nany of their un-initialized state.\n\nThe call prints:\n\n::\n\n    $ php show_bug.php 1\n    Bug: Something does not work!\n    Engineer: beberlei\n\n.. warning::\n\n    Lazy loading additional data can be very convenient but the additional\n    queries create an overhead. If you know that certain fields will always\n    (or usually) be required by the query then you will get better performance\n    by explicitly retrieving them all in the first query.\n\n\nDashboard of the User\n---------------------\n\nFor the next use-case we want to retrieve the dashboard view, a\nlist of all open bugs the user reported or was assigned to. This\nwill be achieved using DQL again, this time with some WHERE clauses\nand usage of bound parameters:\n\n.. code-block:: php\n\n    <?php\n    // dashboard.php <user-id>\n    require_once \"bootstrap.php\";\n\n    $theUserId = $argv[1];\n\n    $dql = \"SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r \".\n           \"WHERE b.status = 'OPEN' AND (e.id = ?1 OR r.id = ?1) ORDER BY b.created DESC\";\n\n    $myBugs = $entityManager->createQuery($dql)\n                            ->setParameter(1, $theUserId)\n                            ->setMaxResults(15)\n                            ->getResult();\n\n    echo \"You have created or assigned to \" . count($myBugs) . \" open bugs:\\n\\n\";\n\n    foreach ($myBugs as $bug) {\n        echo $bug->getId() . \" - \" . $bug->getDescription().\"\\n\";\n    }\n\nNumber of Bugs\n--------------\n\nUntil now we only retrieved entities or their array representation.\nDoctrine also supports the retrieval of non-entities through DQL.\nThese values are called \"scalar result values\" and may even be\naggregate values using COUNT, SUM, MIN, MAX or AVG functions.\n\nWe will need this knowledge to retrieve the number of open bugs\ngrouped by product:\n\n.. code-block:: php\n\n    <?php\n    // products.php\n    require_once \"bootstrap.php\";\n\n    $dql = \"SELECT p.id, p.name, count(b.id) AS openBugs FROM Bug b \".\n           \"JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id\";\n    $productBugs = $entityManager->createQuery($dql)->getScalarResult();\n\n    foreach ($productBugs as $productBug) {\n        echo $productBug['name'].\" has \" . $productBug['openBugs'] . \" open bugs!\\n\";\n    }\n\nUpdating Entities\n-----------------\n\nThere is a single use-case missing from the requirements, Engineers\nshould be able to close a bug. This looks like:\n\n.. code-block:: php\n\n    <?php\n    // src/Bug.php\n\n    class Bug\n    {\n        public function close()\n        {\n            $this->status = \"CLOSE\";\n        }\n    }\n\n.. code-block:: php\n\n    <?php\n    // close_bug.php <bug-id>\n    require_once \"bootstrap.php\";\n\n    $theBugId = $argv[1];\n\n    $bug = $entityManager->find(\"Bug\", (int)$theBugId);\n    $bug->close();\n\n    $entityManager->flush();\n\nWhen retrieving the Bug from the database it is inserted into the\nIdentityMap inside the UnitOfWork of Doctrine. This means your Bug\nwith exactly this id can only exist once during the whole request\nno matter how often you call ``EntityManager#find()``. It even\ndetects entities that are hydrated using DQL and are already\npresent in the Identity Map.\n\nWhen flush is called the EntityManager loops over all the entities\nin the identity map and performs a comparison between the values\noriginally retrieved from the database and those values the entity\ncurrently has. If at least one of these properties is different the\nentity is scheduled for an UPDATE against the database. Only the\nchanged columns are updated, which offers a pretty good performance\nimprovement compared to updating all the properties.\n\nEntity Repositories\n-------------------\n\nFor now we have not discussed how to separate the Doctrine query logic from your model.\nIn Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this\nseparation. The similar concept in Doctrine2 is called Entity Repositories, integrating\nthe `repository pattern <https://martinfowler.com/eaaCatalog/repository.html>`_ at the heart of Doctrine.\n\nEvery Entity uses a default repository by default and offers a bunch of convenience\nmethods that you can use to query for instances of that Entity. Take for example\nour Product entity. If we wanted to Query by name, we can use:\n\n.. code-block:: php\n\n    <?php\n    $product = $entityManager->getRepository('Product')\n                             ->findOneBy(array('name' => $productName));\n\nThe method ``findOneBy()`` takes an array of fields or association keys and the values to match against.\n\nIf you want to find all entities matching a condition you can use ``findBy()``, for\nexample querying for all closed bugs:\n\n.. code-block:: php\n\n    <?php\n    $bugs = $entityManager->getRepository('Bug')\n                          ->findBy(array('status' => 'CLOSED'));\n\n    foreach ($bugs as $bug) {\n        // do stuff\n    }\n\nCompared to DQL these query methods are falling short of functionality very fast.\nDoctrine offers you a convenient way to extend the functionalities of the default ``EntityRepository``\nand put all the specialized DQL query logic on it. For this you have to create a subclass\nof ``Doctrine\\ORM\\EntityRepository``, in our case a ``BugRepository`` and group all\nthe previously discussed query functionality in it:\n\n.. code-block:: php\n\n    <?php\n    // src/BugRepository.php\n\n    use Doctrine\\ORM\\EntityRepository;\n\n    class BugRepository extends EntityRepository\n    {\n        public function getRecentBugs($number = 30)\n        {\n            $dql = \"SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC\";\n\n            $query = $this->getEntityManager()->createQuery($dql);\n            $query->setMaxResults($number);\n            return $query->getResult();\n        }\n\n        public function getRecentBugsArray($number = 30)\n        {\n            $dql = \"SELECT b, e, r, p FROM Bug b JOIN b.engineer e \".\n                   \"JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC\";\n            $query = $this->getEntityManager()->createQuery($dql);\n            $query->setMaxResults($number);\n            return $query->getArrayResult();\n        }\n\n        public function getUsersBugs($userId, $number = 15)\n        {\n            $dql = \"SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r \".\n                   \"WHERE b.status = 'OPEN' AND e.id = ?1 OR r.id = ?1 ORDER BY b.created DESC\";\n\n            return $this->getEntityManager()->createQuery($dql)\n                                 ->setParameter(1, $userId)\n                                 ->setMaxResults($number)\n                                 ->getResult();\n        }\n\n        public function getOpenBugsByProduct()\n        {\n            $dql = \"SELECT p.id, p.name, count(b.id) AS openBugs FROM Bug b \".\n                   \"JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id\";\n            return $this->getEntityManager()->createQuery($dql)->getScalarResult();\n        }\n    }\n\nTo be able to use this query logic through ``$this->getEntityManager()->getRepository('Bug')``\nwe have to adjust the metadata slightly.\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n\n        use Doctrine\\ORM\\Mapping as ORM;\n\n        #[ORM\\Entity(repositoryClass: BugRepository::class)]\n        #[ORM\\Table(name: 'bugs')]\n        class Bug\n        {\n            // ...\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n              xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n              xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n              <entity name=\"Bug\" table=\"bugs\" repository-class=\"BugRepository\">\n\n              </entity>\n        </doctrine-mapping>\n\nNow we can remove our query logic in all the places and instead use them through the EntityRepository.\nAs an example here is the code of the first use case \"List of Bugs\":\n\n.. code-block:: php\n\n    <?php\n    // list_bugs_repository.php\n    require_once \"bootstrap.php\";\n\n    $bugs = $entityManager->getRepository('Bug')->getRecentBugs();\n\n    foreach ($bugs as $bug) {\n        echo $bug->getDescription().\" - \".$bug->getCreated()->format('d.m.Y').\"\\n\";\n        echo \"    Reported by: \".$bug->getReporter()->getName().\"\\n\";\n        echo \"    Assigned to: \".$bug->getEngineer()->getName().\"\\n\";\n        foreach ($bug->getProducts() as $product) {\n            echo \"    Platform: \".$product->getName().\"\\n\";\n        }\n        echo \"\\n\";\n    }\n\nUsing EntityRepositories you can avoid coupling your model with specific query logic.\nYou can also re-use query logic easily throughout your application.\n\nThe method ``count()`` takes an array of fields or association keys and the values to match against.\nThis provides you with a convenient and lightweight way to count a resultset when you don't need to\ndeal with it:\n\n.. code-block:: php\n\n    <?php\n    $productCount = $entityManager->getRepository(Product::class)\n                             ->count(['name' => $productName]);\n\nConclusion\n----------\n\nThis tutorial is over here, I hope you had fun. Additional content\nwill be added to this tutorial incrementally, topics will include:\n\n-   More on Association Mappings\n-   Lifecycle Events triggered in the UnitOfWork\n-   Ordering of Collections\n\nAdditional details on all the topics discussed here can be found in\nthe respective manual chapters.\n"
  },
  {
    "path": "docs/en/tutorials/ordered-associations.rst",
    "content": "Ordering To-Many Associations\n-----------------------------\n\nThere are use-cases when you'll want to sort collections when they are\nretrieved from the database. In userland you do this as long as you\nhaven't initially saved an entity with its associations into the\ndatabase. To retrieve a sorted collection from the database you can\nuse the ``#[OrderBy]`` attribute with a collection that specifies\na DQL snippet that is appended to all queries with this\ncollection.\n\nAdditional to any ``#[OneToMany]`` or ``#[ManyToMany]`` attribute you\ncan specify the ``#[OrderBy]`` in the following way:\n\n.. configuration-block::\n\n    .. code-block:: attribute\n\n        <?php\n        #[Entity]\n        class User\n        {\n            // ...\n\n            #[ManyToMany(targetEntity: Group::class)]\n            #[OrderBy([\"name\" => \"ASC\"])]\n            private Collection $groups;\n        }\n\n    .. code-block:: xml\n\n        <doctrine-mapping>\n            <entity name=\"User\">\n                <many-to-many field=\"groups\" target-entity=\"Group\">\n                    <order-by>\n                        <order-by-field name=\"name\" direction=\"ASC\" />\n                    </order-by>\n                </many-to-many>\n            </entity>\n        </doctrine-mapping>\n\nThe DQL Snippet in OrderBy is only allowed to consist of\nunqualified, unquoted field names and of an optional ASC/DESC\npositional statement. Multiple Fields are separated by a comma (,).\nThe referenced field names have to exist on the ``targetEntity``\nclass of the ``#[ManyToMany]`` or ``#[OneToMany]`` attribute.\n\nThe semantics of this feature can be described as follows:\n\n\n-  ``@OrderBy`` acts as an implicit ORDER BY clause for the given\n   fields, that is appended to all the explicitly given ORDER BY\n   items.\n-  All collections of the ordered type are always retrieved in an\n   ordered fashion.\n-  To keep the database impact low, these implicit ORDER BY items\n   are only added to a DQL Query if the collection is fetch joined in\n   the DQL query.\n\nGiven our previously defined example, the following would not add\nORDER BY, since g is not fetch joined:\n\n.. code-block:: sql\n\n    SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10\n\nHowever the following:\n\n.. code-block:: sql\n\n    SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10\n\n...would internally be rewritten to:\n\n.. code-block:: sql\n\n    SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC\n\nYou can reverse the order with an explicit DQL ORDER BY:\n\n.. code-block:: sql\n\n    SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC\n\n...is internally rewritten to:\n\n.. code-block:: sql\n\n    SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC\n"
  },
  {
    "path": "docs/en/tutorials/override-field-association-mappings-in-subclasses.rst",
    "content": "Override Field Association Mappings In Subclasses\n-------------------------------------------------\n\nSometimes there is a need to persist entities but override all or part of the\nmapping metadata. Sometimes also the mapping to override comes from entities\nusing traits where the traits have mapping metadata.\nThis tutorial explains how to override mapping metadata,\ni.e. attributes and associations metadata in particular. The example here shows\nthe overriding of a class that uses a trait but is similar when extending a base\nclass as shown at the end of this tutorial.\n\nSuppose we have a class ``ExampleEntityWithOverride``. This class uses trait ``ExampleTrait``:\n\n.. code-block:: php\n\n    <?php\n\n    #[Entity]\n    #[AttributeOverrides([\n        new AttributeOverride('foo', new Column(\n            name: 'foo_overridden',\n            type: 'integer',\n            length: 140,\n            nullable: false,\n            unique: false,\n        )),\n    ])]\n    #[AssociationOverrides([\n        new AssociationOverride('bar', [\n            new JoinColumn(\n                name: 'example_entity_overridden_bar_id',\n                referencedColumnName: 'id',\n            ),\n        ]),\n    ])]\n    class ExampleEntityWithOverride\n    {\n        use ExampleTrait;\n    }\n\n    #[Entity]\n    class Bar\n    {\n        #[Id, Column(type: 'string')]\n        private $id;\n    }\n\n``#[AttributeOverrides]`` contains metadata override of the attribute and association type. It\nbasically changes the names of the columns mapped for a property ``foo`` and for\nthe association ``bar`` which relates to Bar class shown above. Here is the trait\nwhich has mapping metadata that is overridden by the attribute above:\n\n.. code-block:: php\n\n    <?php\n    /**\n     * Trait class\n     */\n    trait ExampleTrait\n    {\n        #[Id, Column(type: 'integer')]\n        private int|null $id = null;\n\n        #[Column(name: 'trait_foo', type: 'integer', length: 100, nullable: true, unique: true)]\n        protected int $foo;\n\n        #[OneToOne(targetEntity: Bar::class, cascade: ['persist'])]\n        #[JoinColumn(name: 'example_trait_bar_id', referencedColumnName: 'id')]\n        protected Bar|null $bar = null;\n    }\n\nThe case for just extending a class would be just the same but:\n\n.. code-block:: php\n\n    <?php\n    class ExampleEntityWithOverride extends BaseEntityWithSomeMapping\n    {\n        // ...\n    }\n\nOverriding is also supported via XML (:ref:`examples <inheritence_mapping_overrides>`).\n"
  },
  {
    "path": "docs/en/tutorials/pagination.rst",
    "content": "Pagination\n==========\n\nDoctrine ORM ships with a Paginator for DQL queries. It\nhas a very simple API and implements the SPL interfaces ``Countable`` and\n``IteratorAggregate``.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Tools\\Pagination\\Paginator;\n\n    $dql = \"SELECT p, c FROM BlogPost p JOIN p.comments c\";\n    $query = $entityManager->createQuery($dql)\n                           ->setFirstResult(0)\n                           ->setMaxResults(100);\n\n    $paginator = new Paginator($query, fetchJoinCollection: true);\n\n    $c = count($paginator);\n    foreach ($paginator as $post) {\n        echo $post->getHeadline() . \"\\n\";\n    }\n\nPaginating Doctrine queries is not as simple as you might think in the\nbeginning. If you have complex fetch-join scenarios with one-to-many or\nmany-to-many associations using the \"default\" LIMIT functionality of database\nvendors is not sufficient to get the correct results.\n\nBy default the pagination extension does the following steps to compute the\ncorrect result:\n\n- Perform a Count query using `DISTINCT` keyword.\n- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page.\n- Perform a WHERE IN query to get all results for the current page.\n\nThis behavior is only necessary if you actually fetch join a to-many\ncollection. You can disable this behavior by setting the\n``fetchJoinCollection`` argument to ``false``; in that case only 2 instead of the 3 queries\ndescribed are executed. We hope to automate the detection for this in\nthe future.\n\n.. note::\n\n    ``fetchJoinCollection`` argument set to ``true`` might affect results if you use aggregations in your query.\n\nBy using the ``Paginator::HINT_ENABLE_DISTINCT`` you can instruct doctrine that the query to be executed\nwill not produce \"duplicate\" rows (only to-one relations are joined), thus the SQL limit will work as expected.\nIn this way the `DISTINCT` keyword will be omitted and can bring important performance improvements.\n\n.. code-block:: php\n\n    <?php\n    use Doctrine\\ORM\\Tools\\Pagination\\Paginator;\n\n    $dql = \"SELECT u, p FROM User u JOIN u.mainPicture p\";\n    $query = $entityManager->createQuery($dql)\n                           ->setHint(Paginator::HINT_ENABLE_DISTINCT, false)\n                           ->setFirstResult(0)\n                           ->setMaxResults(100);\n"
  },
  {
    "path": "docs/en/tutorials/working-with-indexed-associations/Market.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\StockExchange;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse InvalidArgumentException;\n\n#[Entity]\n#[Table(name: 'exchange_markets')]\nclass Market\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int|null $id = null;\n\n    #[Column(type: 'string')]\n    private string $name;\n\n    /** @var Collection<string, Stock> */\n    #[OneToMany(targetEntity: Stock::class, mappedBy: 'market', indexBy: 'symbol')]\n    private Collection $stocks;\n\n    public function __construct(string $name)\n    {\n        $this->name   = $name;\n        $this->stocks = new ArrayCollection();\n    }\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function addStock(Stock $stock): void\n    {\n        $this->stocks[$stock->getSymbol()] = $stock;\n    }\n\n    public function getStock(string $symbol): Stock\n    {\n        if (! isset($this->stocks[$symbol])) {\n            throw new InvalidArgumentException('Symbol is not traded on this market.');\n        }\n\n        return $this->stocks[$symbol];\n    }\n\n    /** @return array<string, Stock> */\n    public function getStocks(): array\n    {\n        return $this->stocks->toArray();\n    }\n}\n"
  },
  {
    "path": "docs/en/tutorials/working-with-indexed-associations/market.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n\t\t\t  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\StockExchange\\Market\">\n\t<id name=\"id\" type=\"integer\">\n\t    <generator strategy=\"AUTO\" />\n\t</id>\n\n\t<field name=\"name\" type=\"string\"/>\n\n\t<one-to-many target-entity=\"Stock\" mapped-by=\"market\" field=\"stocks\" index-by=\"symbol\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "docs/en/tutorials/working-with-indexed-associations.rst",
    "content": "Working with Indexed Associations\n=================================\n\nDoctrine ORM collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in\nthe first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY``\nwas used. You can index your collections by a value in the related entity.\nThis is a first step towards full ordered hashmap support through the Doctrine ORM.\nThe feature works like an implicit ``INDEX BY`` for the selected association but has several\ndownsides also:\n\n-  You have to manage both the key and field if you want to change the index by field value.\n-  On each request the keys are regenerated from the field value, and not from the previous collection key.\n-  Values of the Index-By keys are never considered during persistence. They only exist for accessing purposes.\n-  Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities\n   with the same index-by field value is undefined.\n\nAs an example we will design a simple stock exchange list view. The domain consists of the entity ``Stock``\nand ``Market`` where each Stock has a symbol and is traded on a single market. Instead of having a numerical\nlist of stocks traded on a market they will be indexed by their symbol, which is unique across all markets.\n\nMapping Indexed Associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can map indexed associations by adding:\n\n    * ``indexBy`` argument to any ``#[OneToMany]`` or ``#[ManyToMany]`` attribute.\n    * ``index-by`` attribute to any ``<one-to-many />`` or ``<many-to-many />`` xml element.\n\nThe code and mappings for the Market entity looks like this:\n\n.. configuration-block::\n    .. literalinclude:: working-with-indexed-associations/Market.php\n        :language: attribute\n\n    .. literalinclude:: working-with-indexed-associations/market.xml\n        :language: xml\n\nInside the ``addStock()`` method you can see how we directly set the key of the association to the symbol,\nso that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)``\nwe pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown.\n\nThe ``Stock`` entity doesn't contain any special instructions that are new, but for completeness\nhere are the code and mappings for it:\n\n.. configuration-block::\n    .. code-block:: attribute\n\n        <?php\n        namespace Doctrine\\Tests\\Models\\StockExchange;\n\n        #[Entity]\n        #[Table(name: 'exchange_stocks')]\n        class Stock\n        {\n            #[Id, Column(type: 'integer'), GeneratedValue]\n            private int|null $id = null;\n\n            #[Column(type: 'string', unique: true)]\n            private string $symbol;\n\n            #[ManyToOne(targetEntity: Market::class, inversedBy: 'stocks')]\n            private Market|null $market;\n\n            public function __construct(string $symbol, Market $market)\n            {\n                $this->symbol = $symbol;\n                $this->market = $market;\n                $market->addStock($this);\n            }\n\n            public function getSymbol(): string\n            {\n                return $this->symbol;\n            }\n        }\n\n    .. code-block:: xml\n\n        <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n              xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n              xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                  https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n            <entity name=\"Doctrine\\Tests\\Models\\StockExchange\\Stock\">\n                <id name=\"id\" type=\"integer\">\n                    <generator strategy=\"AUTO\" />\n                </id>\n\n                <field name=\"symbol\" type=\"string\" unique=\"true\" />\n                <many-to-one target-entity=\"Market\" field=\"market\" inversed-by=\"stocks\" />\n            </entity>\n        </doctrine-mapping>\n\nQuerying indexed associations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we defined the stocks collection to be indexed by symbol, we can take a look at some code\nthat makes use of the indexing.\n\nFirst we will populate our database with two example stocks traded on a single market:\n\n.. code-block:: php\n\n    <?php\n    // $em is the EntityManager\n\n    $market = new Market(\"Some Exchange\");\n    $stock1 = new Stock(\"AAPL\", $market);\n    $stock2 = new Stock(\"GOOG\", $market);\n\n    $em->persist($market);\n    $em->persist($stock1);\n    $em->persist($stock2);\n    $em->flush();\n\nThis code is not particular interesting since the indexing feature is not yet used. In a new request we could\nnow query for the market:\n\n.. code-block:: php\n\n    <?php\n    // $em is the EntityManager\n    $marketId = 1;\n    $symbol = \"AAPL\";\n\n    $market = $em->find(\"Doctrine\\Tests\\Models\\StockExchange\\Market\", $marketId);\n\n    // Access the stocks by symbol now:\n    $stock = $market->getStock($symbol);\n\n    echo $stock->getSymbol(); // will print \"AAPL\"\n\nThe implementation of ``Market::addStock()``, in combination with ``indexBy``, allows us to access the collection\nconsistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not.\n\nThe same applies to DQL queries: The ``indexBy`` configuration acts as implicit \"INDEX BY\" to a join association.\n\n.. code-block:: php\n\n    <?php\n    // $em is the EntityManager\n    $marketId = 1;\n    $symbol = \"AAPL\";\n\n    $dql = \"SELECT m, s FROM Doctrine\\Tests\\Models\\StockExchange\\Market m JOIN m.stocks s WHERE m.id = ?1\";\n    $market = $em->createQuery($dql)\n                 ->setParameter(1, $marketId)\n                 ->getSingleResult();\n\n    // Access the stocks by symbol now:\n    $stock = $market->getStock($symbol);\n\n    echo $stock->getSymbol(); // will print \"AAPL\"\n\nIf you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally,\nindexed associations also work with the ``Collection::slice()`` functionality, even if the association's fetch mode is\nLAZY or EXTRA_LAZY.\n\nOutlook into the Future\n~~~~~~~~~~~~~~~~~~~~~~~\n\nFor the inverse side of a many-to-many associations there will be a way to persist the keys and the order\nas a third and fourth parameter into the join table. This feature is discussed in `#2817 <https://github.com/doctrine/orm/issues/2817>`_\nThis feature cannot be implemented for one-to-many associations, because they are never the owning side.\n"
  },
  {
    "path": "doctrine-mapping.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    targetNamespace=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n    xmlns:orm=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n    elementFormDefault=\"qualified\">\n\n  <xs:annotation>\n    <xs:documentation><![CDATA[\n       This is the XML Schema for the object/relational\n       mapping file used by the Doctrine ORM.\n     ]]></xs:documentation>\n  </xs:annotation>\n\n  <xs:element name=\"doctrine-mapping\">\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"mapped-superclass\" type=\"orm:mapped-superclass\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        <xs:element name=\"entity\" type=\"orm:entity\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        <xs:element name=\"embeddable\" type=\"orm:embeddable\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n      </xs:choice>\n      <xs:anyAttribute namespace=\"##other\"/>\n    </xs:complexType>\n  </xs:element>\n\n  <xs:complexType name=\"emptyType\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"cascade-type\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"cascade-all\" type=\"orm:emptyType\" minOccurs=\"0\"/>\n      <xs:element name=\"cascade-persist\" type=\"orm:emptyType\" minOccurs=\"0\"/>\n      <xs:element name=\"cascade-remove\" type=\"orm:emptyType\" minOccurs=\"0\"/>\n      <xs:element name=\"cascade-refresh\" type=\"orm:emptyType\" minOccurs=\"0\"/>\n      <xs:element name=\"cascade-detach\" type=\"orm:emptyType\" minOccurs=\"0\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:simpleType name=\"lifecycle-callback-type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"prePersist\"/>\n      <xs:enumeration value=\"postPersist\"/>\n      <xs:enumeration value=\"preUpdate\"/>\n      <xs:enumeration value=\"postUpdate\"/>\n      <xs:enumeration value=\"preRemove\"/>\n      <xs:enumeration value=\"postRemove\"/>\n      <xs:enumeration value=\"postLoad\"/>\n      <xs:enumeration value=\"preFlush\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"cache-usage-type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"READ_ONLY\"/>\n      <xs:enumeration value=\"READ_WRITE\"/>\n      <xs:enumeration value=\"NONSTRICT_READ_WRITE\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:complexType name=\"lifecycle-callback\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"type\" type=\"orm:lifecycle-callback-type\" use=\"required\" />\n    <xs:attribute name=\"method\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"lifecycle-callbacks\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"lifecycle-callback\" type=\"orm:lifecycle-callback\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"entity-listener\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"lifecycle-callback\" type=\"orm:lifecycle-callback\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"class\" type=\"orm:fqcn\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"entity-listeners\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"entity-listener\" type=\"orm:entity-listener\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n    </xs:choice>\n  </xs:complexType>\n\n  <xs:complexType name=\"column-result\">\n    <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"field-result\">\n    <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\" />\n    <xs:attribute name=\"column\" type=\"xs:string\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"entity-result\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"field-result\" type=\"orm:field-result\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n    </xs:choice>\n    <xs:attribute name=\"entity-class\" type=\"orm:fqcn\" use=\"required\" />\n    <xs:attribute name=\"discriminator-column\" type=\"xs:string\" use=\"optional\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"cache\">\n    <xs:attribute name=\"usage\" type=\"orm:cache-usage-type\" />\n    <xs:attribute name=\"region\" type=\"xs:string\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"entity\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"cache\" type=\"orm:cache\" minOccurs=\"0\" maxOccurs=\"1\"/>\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:element name=\"indexes\" type=\"orm:indexes\" minOccurs=\"0\"/>\n      <xs:element name=\"unique-constraints\" type=\"orm:unique-constraints\" minOccurs=\"0\"/>\n      <xs:element name=\"discriminator-column\" type=\"orm:discriminator-column\" minOccurs=\"0\"/>\n      <xs:element name=\"discriminator-map\" type=\"orm:discriminator-map\" minOccurs=\"0\"/>\n      <xs:element name=\"lifecycle-callbacks\" type=\"orm:lifecycle-callbacks\" minOccurs=\"0\" maxOccurs=\"1\" />\n      <xs:element name=\"entity-listeners\" type=\"orm:entity-listeners\" minOccurs=\"0\" maxOccurs=\"1\" />\n      <xs:element name=\"id\" type=\"orm:id\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:element name=\"field\" type=\"orm:field\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n      <xs:element name=\"embedded\" type=\"orm:embedded\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n      <xs:element name=\"one-to-one\" type=\"orm:one-to-one\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n      <xs:element name=\"one-to-many\" type=\"orm:one-to-many\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:element name=\"many-to-one\" type=\"orm:many-to-one\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:element name=\"many-to-many\" type=\"orm:many-to-many\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:element name=\"association-overrides\" type=\"orm:association-overrides\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:element name=\"attribute-overrides\" type=\"orm:attribute-overrides\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\" />\n    <xs:attribute name=\"table\" type=\"orm:tablename\" />\n    <xs:attribute name=\"schema\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"repository-class\" type=\"orm:fqcn\"/>\n    <xs:attribute name=\"inheritance-type\" type=\"orm:inheritance-type\"/>\n    <xs:attribute name=\"change-tracking-policy\" type=\"orm:change-tracking-policy\" />\n    <xs:attribute name=\"read-only\" type=\"xs:boolean\" default=\"false\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:simpleType name=\"tablename\" id=\"tablename\">\n    <xs:restriction base=\"xs:token\">\n      <xs:pattern value=\"[a-zA-Z_u01-uff.]+\" id=\"tablename.pattern\">\n      </xs:pattern>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:complexType name=\"object\">\n    <xs:attribute name=\"class\" type=\"xs:string\" use=\"required\"/>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"option\" mixed=\"true\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"1\">\n      <xs:element name=\"object\" type=\"orm:object\"/>\n      <xs:sequence>\n        <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n          <xs:element name=\"option\" type=\"orm:option\"/>\n          <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n        </xs:choice>\n      </xs:sequence>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\"/>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"options\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"option\" type=\"orm:option\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"mapped-superclass\" >\n    <xs:complexContent>\n      <xs:extension base=\"orm:entity\"/>\n    </xs:complexContent>\n  </xs:complexType>\n\n  <xs:complexType name=\"embeddable\">\n    <xs:complexContent>\n      <xs:extension base=\"orm:entity\"/>\n    </xs:complexContent>\n  </xs:complexType>\n\n  <xs:simpleType name=\"change-tracking-policy\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"DEFERRED_IMPLICIT\"/>\n      <xs:enumeration value=\"DEFERRED_EXPLICIT\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"inheritance-type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"SINGLE_TABLE\"/>\n      <xs:enumeration value=\"JOINED\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"generator-strategy\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"NONE\"/>\n      <xs:enumeration value=\"SEQUENCE\"/>\n      <xs:enumeration value=\"IDENTITY\"/>\n      <xs:enumeration value=\"AUTO\"/>\n      <xs:enumeration value=\"CUSTOM\" />\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"fk-action\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"CASCADE\"/>\n      <xs:enumeration value=\"RESTRICT\"/>\n      <xs:enumeration value=\"SET NULL\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"fetch-type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"EAGER\"/>\n      <xs:enumeration value=\"LAZY\"/>\n      <xs:enumeration value=\"EXTRA_LAZY\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"generated-type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"NEVER\"/>\n      <xs:enumeration value=\"INSERT\"/>\n      <xs:enumeration value=\"ALWAYS\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:complexType name=\"field\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"type\" type=\"orm:type\" default=\"string\" />\n    <xs:attribute name=\"column\" type=\"orm:columntoken\" />\n    <xs:attribute name=\"length\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"unique\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"nullable\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"index\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"insertable\" type=\"xs:boolean\" default=\"true\" />\n    <xs:attribute name=\"updatable\" type=\"xs:boolean\" default=\"true\" />\n    <xs:attribute name=\"generated\" type=\"orm:generated-type\" default=\"NEVER\" />\n    <xs:attribute name=\"enum-type\" type=\"xs:string\" />\n    <xs:attribute name=\"version\" type=\"xs:boolean\" />\n    <xs:attribute name=\"column-definition\" type=\"xs:string\" />\n    <xs:attribute name=\"precision\" type=\"xs:integer\" use=\"optional\" />\n    <xs:attribute name=\"scale\" type=\"xs:integer\" use=\"optional\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"embedded\">\n    <xs:sequence>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:sequence>\n    <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\" />\n    <xs:attribute name=\"class\" type=\"orm:fqcn\" use=\"optional\" />\n    <xs:attribute name=\"column-prefix\" type=\"xs:string\" use=\"optional\" />\n    <xs:attribute name=\"use-column-prefix\" type=\"xs:boolean\" default=\"true\" use=\"optional\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"discriminator-column\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"type\" type=\"xs:NMTOKEN\"/>\n    <xs:attribute name=\"field-name\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"length\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"column-definition\" type=\"xs:string\" />\n    <xs:attribute name=\"enum-type\" type=\"xs:string\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"unique-constraint\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"optional\"/>\n    <xs:attribute name=\"columns\" type=\"xs:string\" use=\"optional\"/>\n    <xs:attribute name=\"fields\" type=\"xs:string\" use=\"optional\"/>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"unique-constraints\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"unique-constraint\" type=\"orm:unique-constraint\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"index\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"optional\"/>\n    <xs:attribute name=\"columns\" type=\"xs:string\" use=\"optional\"/>\n    <xs:attribute name=\"fields\" type=\"xs:string\" use=\"optional\"/>\n    <xs:attribute name=\"flags\" type=\"xs:string\" use=\"optional\"/>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"indexes\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"index\" type=\"orm:index\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"discriminator-mapping\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"value\" type=\"orm:type\" use=\"required\"/>\n    <xs:attribute name=\"class\" type=\"orm:fqcn\" use=\"required\"/>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"discriminator-map\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"discriminator-mapping\" type=\"orm:discriminator-mapping\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"generator\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"strategy\" type=\"orm:generator-strategy\" use=\"optional\" default=\"AUTO\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"id\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"generator\" type=\"orm:generator\" minOccurs=\"0\" />\n      <xs:element name=\"sequence-generator\" type=\"orm:sequence-generator\" minOccurs=\"0\" maxOccurs=\"1\" />\n      <xs:element name=\"custom-id-generator\" type=\"orm:custom-id-generator\" minOccurs=\"0\" maxOccurs=\"1\" />\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"type\" type=\"orm:type\" />\n    <xs:attribute name=\"column\" type=\"orm:columntoken\" />\n    <xs:attribute name=\"length\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"association-key\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"column-definition\" type=\"xs:string\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"sequence-generator\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n      <xs:attribute name=\"sequence-name\" type=\"xs:NMTOKEN\" use=\"required\" />\n      <xs:attribute name=\"allocation-size\" type=\"xs:integer\" use=\"optional\" default=\"1\" />\n      <xs:attribute name=\"initial-value\" type=\"xs:integer\" use=\"optional\" default=\"1\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"custom-id-generator\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"class\" type=\"orm:fqcn\" use=\"required\" />\n  </xs:complexType>\n\n  <xs:simpleType name=\"fqcn\" id=\"fqcn\">\n    <xs:restriction base=\"xs:token\">\n      <xs:pattern value=\"[a-zA-Z_u01-uff][a-zA-Z0-9_u01-uff]+\" id=\"fqcn.pattern\">\n      </xs:pattern>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"type\" id=\"type\">\n    <xs:restriction base=\"xs:token\">\n      <xs:pattern value=\"([a-zA-Z_u01-uff][a-zA-Z0-9_u01-uff]+)|(\\c+)\" id=\"type.class.pattern\">\n      </xs:pattern>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:complexType name=\"inverse-join-columns\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"join-column\" type=\"orm:join-column\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"join-column\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"optional\" />\n    <xs:attribute name=\"referenced-column-name\" type=\"xs:NMTOKEN\" use=\"optional\" default=\"id\" />\n    <xs:attribute name=\"unique\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"nullable\" type=\"xs:boolean\" default=\"true\" />\n    <xs:attribute name=\"on-delete\" type=\"orm:fk-action\" />\n    <xs:attribute name=\"column-definition\" type=\"xs:string\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"join-columns\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"join-column\" type=\"orm:join-column\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"join-table\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"join-columns\" type=\"orm:join-columns\" />\n      <xs:element name=\"inverse-join-columns\" type=\"orm:join-columns\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"schema\" type=\"xs:NMTOKEN\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"order-by\">\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n          <xs:element name=\"order-by-field\" type=\"orm:order-by-field\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n          <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n      </xs:choice>\n      <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"order-by-field\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"direction\" type=\"orm:order-by-direction\" default=\"ASC\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:simpleType name=\"order-by-direction\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"ASC\"/>\n      <xs:enumeration value=\"DESC\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:simpleType name=\"columntoken\" id=\"columntoken\">\n    <xs:restriction base=\"xs:token\">\n      <xs:pattern value=\"[-._:A-Za-z0-9`]+\" id=\"columntoken.pattern\"/>\n    </xs:restriction>\n  </xs:simpleType>\n\n  <xs:complexType name=\"many-to-many\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n       <xs:element name=\"cache\" type=\"orm:cache\" minOccurs=\"0\" maxOccurs=\"1\"/>\n      <xs:element name=\"cascade\" type=\"orm:cascade-type\" minOccurs=\"0\" />\n      <xs:element name=\"join-table\" type=\"orm:join-table\" minOccurs=\"0\" />\n      <xs:element name=\"order-by\" type=\"orm:order-by\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"field\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"target-entity\" type=\"xs:string\" use=\"required\" />\n    <xs:attribute name=\"mapped-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"inversed-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"index-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"fetch\" type=\"orm:fetch-type\" default=\"LAZY\" />\n    <xs:attribute name=\"orphan-removal\" type=\"xs:boolean\" default=\"false\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"one-to-many\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n       <xs:element name=\"cache\" type=\"orm:cache\" minOccurs=\"0\" maxOccurs=\"1\"/>\n      <xs:element name=\"cascade\" type=\"orm:cascade-type\" minOccurs=\"0\" />\n      <xs:element name=\"order-by\" type=\"orm:order-by\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"field\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"target-entity\" type=\"xs:string\" use=\"required\" />\n    <xs:attribute name=\"mapped-by\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"index-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"fetch\" type=\"orm:fetch-type\" default=\"LAZY\" />\n    <xs:attribute name=\"orphan-removal\" type=\"xs:boolean\" default=\"false\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"many-to-one\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n       <xs:element name=\"cache\" type=\"orm:cache\" minOccurs=\"0\" maxOccurs=\"1\"/>\n      <xs:element name=\"cascade\" type=\"orm:cascade-type\" minOccurs=\"0\" />\n      <xs:choice minOccurs=\"0\" maxOccurs=\"1\">\n        <xs:element name=\"join-column\" type=\"orm:join-column\"/>\n        <xs:element name=\"join-columns\" type=\"orm:join-columns\"/>\n      </xs:choice>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"field\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"target-entity\" type=\"xs:string\" />\n    <xs:attribute name=\"inversed-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"fetch\" type=\"orm:fetch-type\" default=\"LAZY\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"one-to-one\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n       <xs:element name=\"cache\" type=\"orm:cache\" minOccurs=\"0\" maxOccurs=\"1\"/>\n      <xs:element name=\"cascade\" type=\"orm:cascade-type\" minOccurs=\"0\" />\n      <xs:choice minOccurs=\"0\" maxOccurs=\"1\">\n        <xs:element name=\"join-column\" type=\"orm:join-column\"/>\n        <xs:element name=\"join-columns\" type=\"orm:join-columns\"/>\n      </xs:choice>\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"field\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"target-entity\" type=\"xs:string\" />\n    <xs:attribute name=\"mapped-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"inversed-by\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"fetch\" type=\"orm:fetch-type\" default=\"LAZY\" />\n    <xs:attribute name=\"orphan-removal\" type=\"xs:boolean\" default=\"false\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n  <xs:complexType name=\"association-overrides\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"association-override\" type=\"orm:association-override\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n  </xs:complexType>\n\n  <xs:complexType name=\"association-override\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"join-table\" type=\"orm:join-table\" minOccurs=\"0\" />\n      <xs:element name=\"join-columns\" type=\"orm:join-columns\" minOccurs=\"0\" />\n      <xs:element name=\"inversed-by\" type=\"orm:inversed-by-override\" minOccurs=\"0\" maxOccurs=\"1\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n    <xs:attribute name=\"fetch\" type=\"orm:fetch-type\" use=\"optional\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"inversed-by-override\">\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"attribute-overrides\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"attribute-override\" type=\"orm:attribute-override\" minOccurs=\"1\" maxOccurs=\"unbounded\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n  </xs:complexType>\n\n  <xs:complexType name=\"attribute-override\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"field\" type=\"orm:attribute-override-field\" minOccurs=\"1\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"name\" type=\"xs:NMTOKEN\" use=\"required\" />\n  </xs:complexType>\n\n  <xs:complexType name=\"attribute-override-field\">\n    <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n      <xs:element name=\"options\" type=\"orm:options\" minOccurs=\"0\" />\n      <xs:any minOccurs=\"0\" maxOccurs=\"unbounded\" namespace=\"##other\"/>\n    </xs:choice>\n    <xs:attribute name=\"type\" type=\"orm:type\" default=\"string\" />\n    <xs:attribute name=\"column\" type=\"orm:columntoken\" />\n    <xs:attribute name=\"length\" type=\"xs:NMTOKEN\" />\n    <xs:attribute name=\"unique\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"nullable\" type=\"xs:boolean\" default=\"false\" />\n    <xs:attribute name=\"insertable\" type=\"xs:boolean\" default=\"true\" />\n    <xs:attribute name=\"updateable\" type=\"xs:boolean\" default=\"true\" />\n    <xs:attribute name=\"version\" type=\"xs:boolean\" />\n    <xs:attribute name=\"column-definition\" type=\"xs:string\" />\n    <xs:attribute name=\"precision\" type=\"xs:integer\" use=\"optional\" />\n    <xs:attribute name=\"scale\" type=\"xs:integer\" use=\"optional\" />\n    <xs:anyAttribute namespace=\"##other\"/>\n  </xs:complexType>\n\n</xs:schema>\n"
  },
  {
    "path": "phpbench.json",
    "content": "{\n    \"runner.bootstrap\": \"tests/Tests/TestInit.php\",\n    \"runner.path\": \"tests/Performance\",\n    \"runner.file_pattern\": \"*Bench.php\",\n\n    \"core.extensions\": [\n        \"PhpBench\\\\Extensions\\\\XDebug\\\\XDebugExtension\"\n    ]\n}\n"
  },
  {
    "path": "phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         name=\"PHP_CodeSniffer\"\n         xsi:noNamespaceSchemaLocation=\"vendor/squizlabs/php_codesniffer/phpcs.xsd\">\n    <arg name=\"basepath\" value=\".\"/>\n    <arg name=\"extensions\" value=\"php\"/>\n    <arg name=\"parallel\" value=\"80\"/>\n    <arg name=\"cache\" value=\".phpcs-cache\"/>\n    <arg name=\"colors\"/>\n\n    <!-- Ignore warnings, show progress of the run and show sniff names -->\n    <arg value=\"nps\"/>\n\n    <config name=\"php_version\" value=\"80100\"/>\n\n    <file>src</file>\n    <file>tests</file>\n\n    <exclude-pattern>*/tests/Tests/Proxies/__CG__*</exclude-pattern>\n    <exclude-pattern>*/tests/Tests/ORM/Tools/Export/export/*</exclude-pattern>\n\n    <rule ref=\"Doctrine\">\n        <exclude name=\"SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException\"/>\n        <exclude name=\"SlevomatCodingStandard.ControlStructures.EarlyExit\"/>\n        <exclude name=\"SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming\"/>\n        <exclude name=\"SlevomatCodingStandard.Classes.SuperfluousExceptionNaming\"/>\n        <exclude name=\"SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall\"/>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint\">\n        <!--\n            that class extends another one inside src/ and can therefore not\n            have more native typehints since its parent cannot have them: that\n            would break signature compatibility.\n        -->\n        <exclude-pattern>tests/Tests/Mocks/HydratorMockStatement.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Models/Cache/ComplexAction.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Models/DDC117/DDC117ArticleDetails.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Models/DDC117/DDC117Translation.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC2579Test.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Functional/ValueObjectsTest.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint\">\n        <exclude-pattern>tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint\">\n        <exclude-pattern>tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"PSR1.Classes.ClassDeclaration.MultipleClasses\">\n        <exclude-pattern>src/Mapping/Driver/LoadMappingFileImplementation.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/GetReflectionClassImplementation.php</exclude-pattern>\n        <exclude-pattern>tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Squiz.Classes.ClassFileName.NoMatch\">\n        <exclude-pattern>src/Tools/Console/Helper/EntityManagerHelper.php</exclude-pattern>\n        <exclude-pattern>tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps\">\n        <exclude-pattern>src/Tools/Debug.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Tools/DebugTest.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase\">\n        <exclude-pattern>src/Events.php</exclude-pattern>\n        <exclude-pattern>src/Tools/ToolEvents.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator\">\n        <exclude-pattern>src/Internal/Hydration/AbstractHydrator.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"PSR1.Methods.CamelCapsMethodName.NotCamelCaps\">\n        <exclude-pattern>src/Query/Parser.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName\">\n        <exclude-pattern>src/Mapping/AssociationOverride.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/AssociationOverrides.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/AttributeOverride.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/AttributeOverrides.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Cache.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/ChangeTrackingPolicy.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Column.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/CustomIdGenerator.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/DiscriminatorColumn.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/DiscriminatorMap.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Embeddable.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Embedded.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Entity.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/EntityListeners.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/GeneratedValue.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/HasLifecycleCallbacks.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Id.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Index.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/InheritanceType.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/JoinColumn.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/JoinColumns.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/JoinTable.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/ManyToMany.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/ManyToOne.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/MappedSuperclass.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/OneToMany.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/OneToOne.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/OrderBy.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PostLoad.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PostPersist.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PostRemove.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PostUpdate.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PreFlush.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PrePersist.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PreRemove.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/PreUpdate.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/SequenceGenerator.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Table.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/UniqueConstraint.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/Version.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Commenting.EmptyComment\">\n        <exclude-pattern>src/Cache/DefaultQueryCache.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Commenting.ForbiddenAnnotations\">\n        <properties>\n            <property name=\"forbiddenAnnotations\" type=\"array\">\n                <!--\n                    From Doctrine Coding Standard:\n                    Forbid useless annotations - Git and LICENCE file provide more accurate information\n                -->\n                <element value=\"@api\"/>\n                <element value=\"@author\"/>\n                <element value=\"@category\"/>\n                <element value=\"@copyright\"/>\n                <element value=\"@created\"/>\n                <element value=\"@license\"/>\n                <element value=\"@package\"/>\n                <element value=\"@since\"/>\n                <element value=\"@subpackage\"/>\n                <element value=\"@version\"/>\n\n                <!-- Additionally forbid oldschool PHPUnit annotations to force the usage of attributes -->\n                <element value=\"@covers\"/>\n                <element value=\"@depends\"/>\n                <element value=\"@dataProvider\"/>\n                <element value=\"@group\"/>\n                <element value=\"@requires\"/>\n                <element value=\"@test\"/>\n                <element value=\"@testWith\"/>\n            </property>\n        </properties>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming\">\n        <exclude-pattern>src/EntityManagerInterface.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Classes.SuperfluousTraitNaming.SuperfluousSuffix\">\n        <exclude-pattern>tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php</exclude-pattern>\n    </rule>\n\n    <rule name=\"SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingAnyTypeHint\">\n        <exclude-pattern>*/tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification\">\n        <exclude-pattern>*/tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification\">\n        <exclude-pattern>*/tests/*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification\">\n        <exclude-pattern>*/tests/*</exclude-pattern>\n    </rule>\n\n    <!-- intentionally without namespace -->\n    <rule ref=\"PSR1.Classes.ClassDeclaration.MissingNamespace\">\n        <exclude-pattern>tests/Tests/Models/Global/GlobalNamespaceModel.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php</exclude-pattern>\n    </rule>\n\n    <!-- file with multiple namespaces confuses the sniff -->\n    <rule ref=\"PSR2.Namespaces.UseDeclaration.UseAfterNamespace\">\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC2084Test.php</exclude-pattern>\n    </rule>\n\n    <!-- file with multiple namespaces confuses the sniff -->\n    <rule ref=\"SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses.IncorrectlyOrderedUses\">\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC2084Test.php</exclude-pattern>\n    </rule>\n\n    <!-- intentionally empty blocks -->\n    <rule ref=\"Generic.CodeAnalysis.EmptyStatement.DetectedForeach\">\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC1301Test.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php</exclude-pattern>\n    </rule>\n\n    <!--\n        That file is used in a test as a template to create another one with a\n        different namespace. The use statement has to stay.\n    -->\n    <rule ref=\"SlevomatCodingStandard.Namespaces.UseFromSameNamespace.UseFromSameNamespace\">\n        <exclude-pattern>tests/Tests/Models/DDC1590/DDC1590User.php</exclude-pattern>\n    </rule>\n\n\n    <rule ref=\"Squiz.Classes.ValidClassName.NotCamelCaps\">\n        <!-- we need to test what happens with an stdClass proxy -->\n        <exclude-pattern>tests/Tests/Proxy/DefaultProxyClassNameResolverTest.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Squiz.Commenting.FunctionComment.WrongStyle\">\n        <!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/1961 -->\n        <exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Mocks/DriverMock.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/UnitOfWorkTest.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Query/DeleteSqlGenerationTest.php</exclude-pattern>\n    </rule>\n    <rule ref=\"Squiz.Commenting.FunctionComment.InvalidNoReturn\">\n        <!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2099 -->\n        <exclude-pattern>src/Query/AST/Node.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.NoAssignment\">\n        <exclude-pattern>tests/Tests/ORM/Mapping/php/Doctrine.Tests*</exclude-pattern>\n    </rule>\n\n    <rule ref=\"PSR2.Methods.MethodDeclaration.Underscore\">\n        <exclude-pattern>src/AbstractQuery.php</exclude-pattern>\n        <exclude-pattern>src/Mapping/ClassMetadata.php</exclude-pattern>\n        <exclude-pattern>src/NativeQuery.php</exclude-pattern>\n        <exclude-pattern>src/Query.php</exclude-pattern>\n        <exclude-pattern>src/Query/TreeWalkerAdapter.php</exclude-pattern>\n        <exclude-pattern>src/Tools/Export/Driver/AbstractExporter.php</exclude-pattern>\n        <exclude-pattern>src/Tools/Export/Driver/PhpExporter.php</exclude-pattern>\n        <!-- extending a class from another package -->\n        <exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/Mocks/SchemaManagerMock.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/AbstractQueryTest.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC3634Test.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore\">\n        <!-- the impact of changing this would be too big -->\n        <exclude-pattern>tests/Tests/OrmFunctionalTestCase.php</exclude-pattern>\n    </rule>\n    <rule ref=\"Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps\">\n        <!-- Member variable \"__isCloning\" is not in valid camel caps format -->\n        <exclude-pattern>src/Proxy/ProxyFactory.php</exclude-pattern>\n        <!-- accessing public property __isInitialized__ of a proxy -->\n        <exclude-pattern>tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Namespaces.UnusedUses.MismatchingCaseSensitivity\">\n        <!-- Using @group and Group entity in the same file -->\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC1885Test.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC1843Test.php</exclude-pattern>\n        <exclude-pattern>tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"Generic.CodeAnalysis.EmptyStatement.DetectedElse\">\n        <!-- The missing code needs to be implemented someday -->\n        <exclude-pattern>src/Id/TableGenerator.php</exclude-pattern>\n    </rule>\n    <rule ref=\"Generic.CodeAnalysis.EmptyStatement.DetectedIf\">\n        <!-- The missing code needs to be implemented someday -->\n        <exclude-pattern>src/Id/TableGenerator.php</exclude-pattern>\n    </rule>\n    <rule ref=\"Squiz.Commenting.FunctionComment.ExtraParamComment\">\n        <!-- https://github.com/doctrine/orm/issues/8537 -->\n        <exclude-pattern>src/QueryBuilder.php</exclude-pattern>\n    </rule>\n</ruleset>\n"
  },
  {
    "path": "phpstan-baseline.neon",
    "content": "parameters:\n\tignoreErrors:\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\AbstractQuery\\:\\:getParameter\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\Parameter\\|null but returns Doctrine\\\\ORM\\\\Query\\\\Parameter\\|false\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/AbstractQuery.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$stmt of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:toIterable\\(\\) expects Doctrine\\\\DBAL\\\\Result, Doctrine\\\\DBAL\\\\Result\\|int given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/AbstractQuery.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CacheFactory\\:\\:buildCachedEntityPersister\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CacheFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CacheFactory\\:\\:buildEntityHydrator\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CacheFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with no value type specified in iterable type array\\|\\(Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\)\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/CollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCache\\:\\:buildCollectionCacheKey\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCache.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCache\\:\\:buildEntityCacheKey\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCache.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCache\\:\\:toIdentifierArray\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCache.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCacheFactory\\:\\:buildCachedEntityPersister\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCacheFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCacheFactory\\:\\:buildEntityHydrator\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCacheFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$collection with no value type specified in iterable type array\\|\\(Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\)\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultCollectionHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultCollectionHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumnFieldNames\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:getCacheRegion\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultEntityHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultEntityHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$data of class Doctrine\\\\ORM\\\\Cache\\\\EntityCacheEntry constructor expects array\\<string, mixed\\>, array\\<int\\|string, mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Unable to resolve the template type T in call to method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getReference\\(\\)$#'\n\t\t\tidentifier: argument.templateType\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultEntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:\\$class\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:resolveAssociationEntries\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:getCacheRegion\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:storeEntityCache\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\DefaultQueryCache\\:\\:storeAssociationCache\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$className of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getClassMetadata\\(\\) expects string, class\\-string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$entityName of method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:getEntityPersister\\(\\) expects class\\-string, class\\-string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$entityName of static method Doctrine\\\\ORM\\\\Cache\\\\Exception\\\\NonCacheableEntity\\:\\:fromEntity\\(\\) expects string, class\\-string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$key of method Doctrine\\\\ORM\\\\Cache\\\\Logging\\\\CacheLogger\\:\\:entityCacheHit\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheKey, Doctrine\\\\ORM\\\\Cache\\\\CacheKey given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$key of method Doctrine\\\\ORM\\\\Cache\\\\Logging\\\\CacheLogger\\:\\:entityCacheMiss\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheKey, Doctrine\\\\ORM\\\\Cache\\\\CacheKey given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/DefaultQueryCache.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:buildCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/EntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:loadCacheEntry\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/EntityHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:\\$identifiers\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:contains\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:containsKey\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:count\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:get\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:getSourceEntityMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:getTargetEntityMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:loadCollectionCache\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:loadCriteria\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:slice\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with no value type specified in iterable type array\\|\\(Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\)\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$key of method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:buildCacheEntry\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheKey, Doctrine\\\\ORM\\\\Cache\\\\CacheKey given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$entry of method Doctrine\\\\ORM\\\\Cache\\\\CollectionHydrator\\:\\:loadCacheEntry\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\CollectionCacheEntry, Doctrine\\\\ORM\\\\Cache\\\\CacheEntry given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:\\$sourceEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\AbstractCollectionPersister\\:\\:\\$targetEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/AbstractCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:getSourceEntityMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:getTargetEntityMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:loadCollectionCache\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\CachedCollectionPersister\\:\\:storeCollectionCache\\(\\) has parameter \\$elements with no value type specified in iterable type array\\|\\(Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\)\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/CachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\NonStrictReadWriteCachedCollectionPersister\\:\\:delete\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\NonStrictReadWriteCachedCollectionPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\ReadOnlyCachedCollectionPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Cache\\\\Region\\:\\:lock\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\ReadWriteCachedCollectionPersister\\:\\:delete\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Collection\\\\ReadWriteCachedCollectionPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:\\$class\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\CachedPersister&Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:loadCollectionCache\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\CachedPersister&Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:storeCollectionCache\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:storeEntityCache\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Entity\\\\AbstractEntityPersister\\:\\:__construct\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Entity\\\\AbstractEntityPersister\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Entity\\\\AbstractEntityPersister\\:\\:loadManyToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Entity\\\\AbstractEntityPersister\\:\\:loadOneToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$entry of method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:loadCacheEntry\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheEntry, Doctrine\\\\ORM\\\\Cache\\\\CacheEntry given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/AbstractEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Cache\\\\Region\\:\\:lock\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^If condition is always true\\.$#'\n\t\t\tidentifier: if.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Cache\\\\Persister\\\\Entity\\\\ReadWriteCachedEntityPersister\\:\\:__construct\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:\\$time\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Cache/TimestampQueryCacheValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function is_a\\(\\) with arguments class\\-string\\<Doctrine\\\\ORM\\\\EntityRepository\\>, ''Doctrine\\\\\\\\ORM\\\\\\\\EntityRepository'' and true will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Configuration.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Configuration\\:\\:getDefaultRepositoryClassName\\(\\) return type with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Configuration.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Configuration\\:\\:setDefaultRepositoryClassName\\(\\) has parameter \\$className with generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Configuration.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$className of method Doctrine\\\\ORM\\\\Configuration\\:\\:addCustomNumericFunction\\(\\) expects \\(callable\\(string\\)\\: Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\)\\|class\\-string\\<Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\>, class\\-string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Configuration.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Decorator/EntityManagerDecorator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getRepository\\(\\) return type with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Decorator/EntityManagerDecorator.php\n\n\t\t-\n\t\t\tmessage: '#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#'\n\t\t\tidentifier: method.childReturnType\n\t\t\tcount: 1\n\t\t\tpath: src/Decorator/EntityManagerDecorator.php\n\n\t\t-\n\t\t\tmessage: '#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManagerDecorator\\<Doctrine\\\\ORM\\\\EntityManagerInterface\\>\\:\\:getMetadataFactory\\(\\)$#'\n\t\t\tidentifier: method.childReturnType\n\t\t\tcount: 1\n\t\t\tpath: src/Decorator/EntityManagerDecorator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:checkLockRequirements\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:find\\(\\) should return \\(T of object\\)\\|null but returns object\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:find\\(\\) should return \\(T of object\\)\\|null but returns object\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 3\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:getReference\\(\\) should return \\(T of object\\)\\|null but returns object\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:getReference\\(\\) should return \\(T of object\\)\\|null but returns object\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityManager\\:\\:isUninitializedObject\\(\\) has parameter \\$value with no type specified\\.$#'\n\t\t\tidentifier: missingType.parameter\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$className of method Doctrine\\\\Persistence\\\\Mapping\\\\AbstractClassMetadataFactory\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>\\:\\:getMetadataFor\\(\\) expects class\\-string, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\EntityManager\\:\\:\\$metadataFactory \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) does not accept object\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\EntityManager\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#'\n\t\t\tidentifier: method.childReturnType\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManager.php\n\n\t\t-\n\t\t\tmessage: '#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#'\n\t\t\tidentifier: method.childReturnType\n\t\t\tcount: 1\n\t\t\tpath: src/EntityManagerInterface.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:findBy\\(\\) should return list\\<T of object\\> but returns array\\<mixed\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityRepository.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:findOneBy\\(\\) should return \\(T of object\\)\\|null but returns object\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityRepository.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:matching\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\AbstractLazyCollection\\<int, T of object\\> but returns Doctrine\\\\ORM\\\\LazyCriteriaCollection\\<\\(int\\|string\\), object\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/EntityRepository.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\ListenersInvoker\\:\\:getSubscribedSystems\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/ListenersInvoker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\ListenersInvoker\\:\\:invoke\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/ListenersInvoker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\OnClassMetadataNotFoundEventArgs\\:\\:getFoundMetadata\\(\\) return type with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/OnClassMetadataNotFoundEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\OnClassMetadataNotFoundEventArgs\\:\\:setFoundMetadata\\(\\) has parameter \\$classMetadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/OnClassMetadataNotFoundEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Event\\\\OnClassMetadataNotFoundEventArgs\\:\\:\\$foundMetadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/OnClassMetadataNotFoundEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\PreUpdateEventArgs\\:\\:__construct\\(\\) has parameter \\$changeSet with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/PreUpdateEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Event\\\\PreUpdateEventArgs\\:\\:getEntityChangeSet\\(\\) return type with generic class Doctrine\\\\ORM\\\\PersistentCollection does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/PreUpdateEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Event\\\\PreUpdateEventArgs\\:\\:\\$entityChangeSet with generic class Doctrine\\\\ORM\\\\PersistentCollection does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Event/PreUpdateEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Id\\\\AssignedGenerator\\:\\:generateId\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Id/AssignedGenerator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:gatherRowData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 3\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:gatherRowData\\(\\) return type with generic class ReflectionClass does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:getDiscriminatorValues\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:registerManaged\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter &\\$id by\\-ref type of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:gatherRowData\\(\\) expects array\\<string, string\\>, array\\<string\\> given\\.$#'\n\t\t\tidentifier: parameterByRef.type\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter &\\$nonemptyComponents by\\-ref type of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:gatherRowData\\(\\) expects array\\<string, bool\\>, array\\<bool\\> given\\.$#'\n\t\t\tidentifier: parameterByRef.type\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/AbstractHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ArrayHydrator\\:\\:hydrateAllData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ArrayHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter &\\$result by\\-ref type of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ArrayHydrator\\:\\:hydrateRowData\\(\\) expects array\\<mixed\\>, array\\<mixed\\>\\|null given\\.$#'\n\t\t\tidentifier: parameterByRef.type\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ArrayHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$inversedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 3\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ObjectHydrator\\:\\:hydrateAllData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ObjectHydrator\\:\\:initRelatedCollection\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ObjectHydrator\\:\\:initRelatedCollection\\(\\) return type with generic class Doctrine\\\\ORM\\\\PersistentCollection does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$assoc of method Doctrine\\\\ORM\\\\PersistentCollection\\<\\(int\\|string\\),mixed\\>\\:\\:setOwner\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping&Doctrine\\\\ORM\\\\Mapping\\\\ToManyAssociationMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ObjectHydrator\\:\\:\\$uninitializedCollections with generic class Doctrine\\\\ORM\\\\PersistentCollection does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ScalarColumnHydrator\\:\\:hydrateAllData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ScalarColumnHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\ScalarHydrator\\:\\:hydrateAllData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/ScalarHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\SimpleObjectHydrator\\:\\:hydrateAllData\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/SimpleObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\SimpleObjectHydrator\\:\\:\\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/Hydration/SimpleObjectHydrator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Internal\\\\HydrationCompleteHandler\\:\\:deferPostLoadInvoking\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/HydrationCompleteHandler.php\n\n\t\t-\n\t\t\tmessage: '#^Offset int\\|null might not exist on array\\<int, object\\>\\.$#'\n\t\t\tidentifier: offsetAccess.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/StronglyConnectedComponents.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Internal\\\\StronglyConnectedComponents\\:\\:\\$representingNodes \\(array\\<int, object\\>\\) does not accept array\\<int\\|string, object\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/StronglyConnectedComponents.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Internal\\\\StronglyConnectedComponents\\:\\:\\$states \\(array\\<int, 1\\|2\\|3\\>\\) does not accept non\\-empty\\-array\\<''''\\|int, 1\\|2\\|3\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Internal/StronglyConnectedComponents.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getColumnAlias\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getIdentifierColumnNames\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getJoinTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getReferencedJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getSequenceName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\AnsiQuoteStrategy\\:\\:getTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AnsiQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\JoinTableMapping\\:\\:fromMappingArray\\(\\) expects array\\{name\\: string, quoted\\?\\: bool\\|null, joinColumns\\?\\: array\\<mixed\\>, inverseJoinColumns\\?\\: array\\<mixed\\>, schema\\?\\: string\\|null, options\\?\\: array\\<string, mixed\\>\\}, non\\-empty\\-array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AssociationMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping\\:\\:\\$cache type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AssociationMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Instanceof between Doctrine\\\\ORM\\\\Mapping\\\\AssociationOverride and Doctrine\\\\ORM\\\\Mapping\\\\AssociationOverride will always evaluate to true\\.$#'\n\t\t\tidentifier: instanceof.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AssociationOverrides.php\n\n\t\t-\n\t\t\tmessage: '#^Instanceof between Doctrine\\\\ORM\\\\Mapping\\\\AttributeOverride and Doctrine\\\\ORM\\\\Mapping\\\\AttributeOverride will always evaluate to true\\.$#'\n\t\t\tidentifier: instanceof.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/AttributeOverrides.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\ClassMetadataBuilder\\:\\:__construct\\(\\) has parameter \\$cm with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Builder/ClassMetadataBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\ClassMetadataBuilder\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Builder/ClassMetadataBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$repositoryClassName of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<object\\>\\:\\:setCustomRepositoryClass\\(\\) expects class\\-string\\<Doctrine\\\\ORM\\\\EntityRepository\\>\\|null, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Builder/ClassMetadataBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mapping of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<object\\>\\:\\:mapEmbedded\\(\\) expects array\\{fieldName\\: string, class\\?\\: class\\-string, declaredField\\?\\: string, columnPrefix\\?\\: string\\|false\\|null, originalField\\?\\: string\\}, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Builder/EmbeddedBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\EntityListenerBuilder\\:\\:bindEntityListener\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Builder/EntityListenerBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 4\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^If condition is always true\\.$#'\n\t\t\tidentifier: if.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:inlineEmbeddable\\(\\) has parameter \\$embeddable with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:setAssociationOverride\\(\\) has parameter \\$overrideMapping with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:setCustomRepositoryClass\\(\\) has parameter \\$repositoryClassName with generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$array \\(list\\<string\\>\\) of array_values is already a list, call has no effect\\.$#'\n\t\t\tidentifier: arrayValues.list\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mapping of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:validateAndCompleteTypedAssociationMapping\\(\\) expects array\\{type\\: 1\\|2\\|4\\|8, fieldName\\: string, targetEntity\\?\\: class\\-string\\}, non\\-empty\\-array\\<string, mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping\\:\\:fromMappingArray\\(\\) expects array\\{fieldName\\: string, sourceEntity\\: class\\-string, targetEntity\\: class\\-string, cascade\\?\\: list\\<''all''\\|''detach''\\|''persist''\\|''refresh''\\|''remove''\\>, fetch\\?\\: 2\\|3\\|4\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null, cache\\?\\: array\\<mixed\\>\\|null, \\.\\.\\.\\}, non\\-empty\\-array given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\EmbeddedClassMapping\\:\\:fromMappingArray\\(\\) expects array\\{class\\: class\\-string, columnPrefix\\?\\: string\\|false\\|null, declaredField\\?\\: string\\|null, originalField\\?\\: string\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null\\}, array\\{class\\: string, columnPrefix\\: string\\|false\\|null, declaredField\\: string\\|null, originalField\\: string\\|null\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\:\\:fromMappingArrayAndNamingStrategy\\(\\) expects array\\{fieldName\\: string, sourceEntity\\: class\\-string, targetEntity\\: class\\-string, cascade\\?\\: list\\<''all''\\|''detach''\\|''persist''\\|''refresh''\\|''remove''\\>, fetch\\?\\: 2\\|3\\|4\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null, cache\\?\\: array\\<mixed\\>\\|null, \\.\\.\\.\\}, non\\-empty\\-array given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\:\\:fromMappingArrayAndName\\(\\) expects array\\{fieldName\\: string, sourceEntity\\: class\\-string, targetEntity\\: class\\-string, cascade\\?\\: list\\<''all''\\|''detach''\\|''persist''\\|''refresh''\\|''remove''\\>, fetch\\?\\: 2\\|3\\|4\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null, cache\\?\\: array\\<mixed\\>\\|null, \\.\\.\\.\\}, non\\-empty\\-array given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\ToOneInverseSideMapping\\:\\:fromMappingArrayAndName\\(\\) expects array\\{fieldName\\: string, sourceEntity\\: class\\-string, targetEntity\\: class\\-string, cascade\\?\\: list\\<''all''\\|''detach''\\|''persist''\\|''refresh''\\|''remove''\\>, fetch\\?\\: 2\\|3\\|4\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null, cache\\?\\: array\\<mixed\\>\\|null, \\.\\.\\.\\}, non\\-empty\\-array given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mappingArray of static method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArrayAndName\\(\\) expects array\\{fieldName\\: string, sourceEntity\\: class\\-string, targetEntity\\: class\\-string, cascade\\?\\: list\\<''all''\\|''detach''\\|''persist''\\|''refresh''\\|''remove''\\>, fetch\\?\\: 2\\|3\\|4\\|null, inherited\\?\\: class\\-string\\|null, declared\\?\\: class\\-string\\|null, cache\\?\\: array\\<mixed\\>\\|null, \\.\\.\\.\\}, non\\-empty\\-array given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:\\$customRepositoryClassName with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:\\$table type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$customRepositoryClassName \\(class\\-string\\<Doctrine\\\\ORM\\\\EntityRepository\\>\\|null\\) does not accept class\\-string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$discriminatorMap \\(array\\<int\\|string, class\\-string\\>\\) does not accept array\\<int\\|string, string\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$entityListeners \\(array\\<string, list\\<array\\{class\\: class\\-string, method\\: string\\}\\>\\>\\) does not accept non\\-empty\\-array\\<string, list\\<array\\{class\\: string, method\\: string\\}\\>\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$subClasses \\(list\\<class\\-string\\>\\) does not accept non\\-empty\\-list\\<string\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Strict comparison using \\!\\=\\= between Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping and false will always evaluate to true\\.$#'\n\t\t\tidentifier: notIdentical.alwaysTrue\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#'\n\t\t\tidentifier: generics.variance\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:\\$reflClass\\.$#'\n\t\t\tidentifier: generics.variance\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Template type T is declared as covariant, but occurs in invariant position in return type of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\:\\:getReflectionClass\\(\\)\\.$#'\n\t\t\tidentifier: generics.variance\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^Unable to resolve the template type C in call to method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:fullyQualifiedClassName\\(\\)$#'\n\t\t\tidentifier: argument.templateType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadata.php\n\n\t\t-\n\t\t\tmessage: '#^If condition is always true\\.$#'\n\t\t\tidentifier: if.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addDefaultDiscriminatorMap\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedEmbeddedClasses\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedEmbeddedClasses\\(\\) has parameter \\$subClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedFields\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedFields\\(\\) has parameter \\$subClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedIndexes\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedIndexes\\(\\) has parameter \\$subClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedRelations\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addInheritedRelations\\(\\) has parameter \\$subClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addMappingInheritanceInformation\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addNestedEmbeddedClasses\\(\\) has parameter \\$parentClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:addNestedEmbeddedClasses\\(\\) has parameter \\$subClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:completeIdGeneratorMapping\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:doLoadMetadata\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:doLoadMetadata\\(\\) has parameter \\$parent with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:findAbstractEntityClassesNotListedInDiscriminatorMap\\(\\) has parameter \\$rootEntityClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:inheritIdGeneratorMapping\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:inheritIdGeneratorMapping\\(\\) has parameter \\$parent with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:initializeReflection\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:isEntity\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:newClassMetadataInstance\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:onNotFoundMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:validateRuntimeMetadata\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:validateRuntimeMetadata\\(\\) has parameter \\$parent with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\:\\:wakeupReflection\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$generator of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<object\\>\\:\\:setIdGenerator\\(\\) expects Doctrine\\\\ORM\\\\Id\\\\AbstractIdGenerator, object given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$rootEntityClass of static method Doctrine\\\\ORM\\\\Mapping\\\\MappingException\\:\\:missingInheritanceTypeDeclaration\\(\\) expects class\\-string, class\\-string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ClassMetadataFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\DefaultEntityListenerResolver\\:\\:\\$instances \\(array\\<class\\-string, object\\>\\) does not accept array\\<string, object\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultEntityListenerResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getColumnAlias\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getIdentifierColumnNames\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getJoinTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getReferencedJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getSequenceName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\DefaultQuoteStrategy\\:\\:getTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/DefaultQuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeDriver\\:\\:isRepeatedPropertyDeclaration\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/AttributeDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mapping of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:mapEmbedded\\(\\) expects array\\{fieldName\\: string, class\\?\\: class\\-string, declaredField\\?\\: string, columnPrefix\\?\\: string\\|false\\|null, originalField\\?\\: string\\}, array\\{fieldName\\: string, cache\\?\\: array\\{usage\\: int, region\\: string\\|null\\}, class\\: string\\|null, columnPrefix\\: bool\\|string\\|null\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/AttributeDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Unable to resolve the template type C in call to method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:fullyQualifiedClassName\\(\\)$#'\n\t\t\tidentifier: argument.templateType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/AttributeDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\ForeignKeyConstraint'' and ''getReferencedColumn…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\ForeignKeyConstraint'' and ''getReferencedTableN…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\ForeignKeyConstraint'' and ''getReferencingColum…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\Index'' and ''getIndexedColumns'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\Table'' and ''getPrimaryKeyConstr…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with Doctrine\\\\DBAL\\\\Schema\\\\Index and ''getType'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with Doctrine\\\\DBAL\\\\Schema\\\\Table and ''getPrimaryKeyConstr…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Instanceof between Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\> and Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata will always evaluate to true\\.$#'\n\t\t\tidentifier: instanceof.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:__construct\\(\\) has parameter \\$sm with generic class Doctrine\\\\DBAL\\\\Schema\\\\AbstractSchemaManager but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:buildFieldMappings\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:buildIndexes\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:buildToOneAssociationMappings\\(\\) has parameter \\$metadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:getAssetName\\(\\) has parameter \\$asset with generic class Doctrine\\\\DBAL\\\\Schema\\\\AbstractAsset but does not specify its types\\: N$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:getClassNameForTable\\(\\) should return class\\-string but returns string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 2\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$columnName of method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:getFieldNameForColumn\\(\\) expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 4\n\t\t\tpath: src/Mapping/Driver/DatabaseDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\SimplifiedXmlDriver\\:\\:__construct\\(\\) has parameter \\$fileExtension with no type specified\\.$#'\n\t\t\tidentifier: missingType.parameter\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/SimplifiedXmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\SimplifiedXmlDriver\\:\\:__construct\\(\\) has parameter \\$prefixes with no type specified\\.$#'\n\t\t\tidentifier: missingType.parameter\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/SimplifiedXmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:cacheToArray\\(\\) should return array\\{usage\\: int\\|null, region\\?\\: string\\} but returns array\\{usage\\: ''''\\|''0''\\|int\\|null, region\\: string\\|null\\}\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:columnToArray\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columnDef of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:setDiscriminatorColumn\\(\\) expects array\\{name\\: string\\|null, fieldName\\?\\: string\\|null, type\\?\\: string\\|null, length\\?\\: int\\|null, columnDefinition\\?\\: string\\|null, enumType\\?\\: class\\-string\\<BackedEnum\\>\\|null, options\\?\\: array\\<string, mixed\\>\\|null\\}\\|Doctrine\\\\ORM\\\\Mapping\\\\DiscriminatorColumnMapping\\|null, array\\{name\\: string\\|null, type\\: string, length\\: int, columnDefinition\\: string\\|null, enumType\\: string\\|null, options\\?\\: array\\<int\\|string, array\\<int\\|string, mixed\\>\\|bool\\|object\\|string\\>\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$data of function simplexml_load_string expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$mapping of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:mapEmbedded\\(\\) expects array\\{fieldName\\: string, class\\?\\: class\\-string, declaredField\\?\\: string, columnPrefix\\?\\: string\\|false\\|null, originalField\\?\\: string\\}, array\\{fieldName\\: string, class\\: string\\|null, columnPrefix\\: string\\|false\\|null\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$repositoryClassName of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:setCustomRepositoryClass\\(\\) expects class\\-string\\<Doctrine\\\\ORM\\\\EntityRepository\\>\\|null, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$repositoryClassName of method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:setCustomRepositoryClass\\(\\) expects class\\-string\\<Doctrine\\\\ORM\\\\EntityRepository\\>\\|null, string\\|null given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\<T of object\\>\\:\\:\\$table \\(array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\?\\: array\\<string, mixed\\>, quoted\\?\\: bool\\}\\) does not accept array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\: array\\<int\\|string, array\\<int\\|string, mixed\\>\\|bool\\|object\\|string\\>, quoted\\?\\: bool\\}\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/Driver/XmlDriver.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\:\\:\\$declared \\(class\\-string\\|null\\) does not accept string\\|null\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/FieldMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\:\\:\\$enumType \\(class\\-string\\<BackedEnum\\>\\|null\\) does not accept string\\|null\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/FieldMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\:\\:\\$inherited \\(class\\-string\\|null\\) does not accept string\\|null\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/FieldMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\:\\:\\$options type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/FieldMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\JoinTableMapping\\:\\:\\$options \\(array\\<string, mixed\\>\\) does not accept array\\<string, mixed\\>\\|bool\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/JoinTableMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\JoinTableMapping\\:\\:\\$quoted \\(bool\\|null\\) does not accept array\\<string, mixed\\>\\|bool\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/JoinTableMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Mapping\\\\JoinTableMapping\\:\\:\\$schema \\(string\\|null\\) does not accept array\\<string, mixed\\>\\|bool\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/JoinTableMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\LegacyReflectionFields\\:\\:__construct\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/LegacyReflectionFields.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$class of method Doctrine\\\\Persistence\\\\Mapping\\\\ReflectionService\\:\\:getAccessibleProperty\\(\\) expects class\\-string, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/LegacyReflectionFields.php\n\n\t\t-\n\t\t\tmessage: '#^Strict comparison using \\!\\=\\= between array\\<string, string\\> and null will always evaluate to true\\.$#'\n\t\t\tidentifier: notIdentical.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ManyToManyOwningSideMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\MappedSuperclass\\:\\:__construct\\(\\) has parameter \\$repositoryClass with generic class Doctrine\\\\ORM\\\\EntityRepository but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/MappedSuperclass.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\PropertyAccessors\\\\EnumPropertyAccessor\\:\\:toEnum\\(\\) should return array\\<BackedEnum\\>\\|BackedEnum but returns array\\<BackedEnum\\|int\\|string\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/PropertyAccessors/EnumPropertyAccessor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(BackedEnum\\|int\\|string\\)\\: mixed\\)\\|null, array\\{class\\-string\\<BackedEnum\\>, ''from''\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/PropertyAccessors/EnumPropertyAccessor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getColumnAlias\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getIdentifierColumnNames\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getReferencedJoinColumnName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getSequenceName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getTableName\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/QuoteStrategy.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ReflectionEnumProperty\\:\\:getValue\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ReflectionEnumProperty.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArray\\(\\) should return static\\(Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\) but returns Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ToOneOwningSideMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArrayAndName\\(\\) has parameter \\$table with no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ToOneOwningSideMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArrayAndName\\(\\) should return static\\(Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\) but returns Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Mapping/ToOneOwningSideMapping.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function is_int\\(\\) with string will always evaluate to false\\.$#'\n\t\t\tidentifier: function.impossibleType\n\t\t\tcount: 1\n\t\t\tpath: src/NativeQuery.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\NativeQuery\\:\\:_doExecute\\(\\) never returns int so it can be removed from the return type\\.$#'\n\t\t\tidentifier: return.unusedType\n\t\t\tcount: 1\n\t\t\tpath: src/NativeQuery.php\n\n\t\t-\n\t\t\tmessage: '#^Result of && is always false\\.$#'\n\t\t\tidentifier: booleanAnd.alwaysFalse\n\t\t\tcount: 1\n\t\t\tpath: src/NativeQuery.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\ORMInvalidArgumentException\\:\\:invalidAssociation\\(\\) has parameter \\$targetClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/ORMInvalidArgumentException.php\n\n\t\t-\n\t\t\tmessage: '#^Call to method Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping\\:\\:isToMany\\(\\) will always evaluate to true\\.$#'\n\t\t\tidentifier: method.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\PersistentCollection\\:\\:__construct\\(\\) has parameter \\$typeClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\PersistentCollection\\:\\:get\\(\\) should return T\\|null but returns object\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\PersistentCollection\\:\\:getTypeClass\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\PersistentCollection\\:\\:matching\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\Collection\\<TKey of \\(int\\|string\\), T\\> but returns Doctrine\\\\Common\\\\Collections\\\\ArrayCollection\\<\\(int\\|string\\), mixed\\>\\|Doctrine\\\\ORM\\\\LazyCriteriaCollection\\<\\(int\\|string\\), object\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\PersistentCollection\\:\\:matching\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\Collection\\<TKey of \\(int\\|string\\), T\\> but returns Doctrine\\\\Common\\\\Collections\\\\ReadableCollection\\<TKey of \\(int\\|string\\), T\\>&Doctrine\\\\Common\\\\Collections\\\\Selectable\\<TKey of \\(int\\|string\\), T\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$key of method Doctrine\\\\ORM\\\\PersistentCollection\\<TKey of \\(int\\|string\\),T\\>\\:\\:set\\(\\) expects TKey of \\(int\\|string\\), int\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$callback of function array_walk expects callable\\(object, int\\)\\: mixed, array\\{Doctrine\\\\Common\\\\Collections\\\\Collection\\<TKey of \\(int\\|string\\), T\\>&Doctrine\\\\Common\\\\Collections\\\\Selectable\\<TKey of \\(int\\|string\\), T\\>, ''add''\\} given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/PersistentCollection.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:contains\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:containsKey\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:count\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:delete\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:get\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:loadCriteria\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:slice\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\CollectionPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/CollectionPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTableColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$relationToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$relationToTargetKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function assert\\(\\) with true will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to method Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping\\:\\:isIndexed\\(\\) will always evaluate to true\\.$#'\n\t\t\tidentifier: method.alreadyNarrowedType\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Instanceof between Doctrine\\\\ORM\\\\Mapping\\\\InverseSideMapping&Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyAssociationMapping and Doctrine\\\\ORM\\\\Mapping\\\\InverseSideMapping will always evaluate to true\\.$#'\n\t\t\tidentifier: instanceof.alwaysTrue\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:collectJoinTableColumnParameters\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:contains\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:containsKey\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:count\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:delete\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:generateFilterConditionSQL\\(\\) has parameter \\$targetEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:get\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getDeleteRowSQL\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getDeleteRowSQLParameters\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getDeleteSQL\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getDeleteSQLParameters\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getInsertRowSQL\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getInsertRowSQLParameters\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getJoinTableRestrictions\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getJoinTableRestrictionsWithKey\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getMapping\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:getOrderingSql\\(\\) has parameter \\$targetClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:loadCriteria\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:slice\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\ManyToManyPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$association of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinTableName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/ManyToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:contains\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:containsKey\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:count\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:delete\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:deleteEntityCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:deleteJoinedEntityCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:get\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:getMapping\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:loadCriteria\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:slice\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:update\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columns of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getColumnDeclarationListSQL\\(\\) expects list\\<array\\{name\\: string, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type, default\\: mixed, notnull\\?\\: bool, autoincrement\\: bool, columnDefinition\\: non\\-empty\\-string\\|null, comment\\: string, charset\\?\\: non\\-empty\\-string\\|null, \\.\\.\\.\\}\\>, array\\<string, array\\{name\\: string, notnull\\: true, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type\\}\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Collection/OneToManyPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\AbstractEntityInheritancePersister\\:\\:getSelectColumnSQL\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/AbstractEntityInheritancePersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 5\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 4\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:indexBy\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:__construct\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:expandToManyParameters\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:extractIdentifierTypes\\(\\) has parameter \\$versionedClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:fetchVersionAndNotUpsertableValues\\(\\) has parameter \\$versionedClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:generateFilterConditionSQL\\(\\) has parameter \\$targetEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:getClassIdentifiersTypes\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:getSelectColumnAssociationSQL\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:getSelectColumnSQL\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:loadCollectionFromStatement\\(\\) has parameter \\$coll with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:loadManyToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\BasicEntityPersister\\:\\:loadOneToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$association of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinTableName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$lockMode of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:appendLockHint\\(\\) expects Doctrine\\\\DBAL\\\\LockMode, Doctrine\\\\DBAL\\\\LockMode\\:\\:NONE\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:OPTIMISTIC\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:PESSIMISTIC_READ\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:PESSIMISTIC_WRITE\\|int given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$hints of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:hydrateAll\\(\\) expects array\\<string, string\\>, array\\<string, Doctrine\\\\ORM\\\\PersistentCollection\\|true\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$hints of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:hydrateAll\\(\\) expects array\\<string, string\\>, array\\<string, true\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 4\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\CachedPersisterContext\\:\\:\\$sqlTableAliases \\(array\\<class\\-string, string\\>\\) does not accept array\\<string, string\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Strict comparison using \\!\\=\\= between mixed and null will always evaluate to true\\.$#'\n\t\t\tidentifier: notIdentical.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/BasicEntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\CachedPersisterContext\\:\\:__construct\\(\\) has parameter \\$class with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/CachedPersisterContext.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/EntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:loadManyToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/EntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\EntityPersister\\:\\:loadOneToManyCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/EntityPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Persisters/Entity/JoinedSubclassPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/JoinedSubclassPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\JoinedSubclassPersister\\:\\:fetchVersionAndNotUpsertableValues\\(\\) has parameter \\$versionedClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/JoinedSubclassPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\JoinedSubclassPersister\\:\\:getVersionedClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/JoinedSubclassPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$lockMode of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:appendLockHint\\(\\) expects Doctrine\\\\DBAL\\\\LockMode, Doctrine\\\\DBAL\\\\LockMode\\:\\:NONE\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:OPTIMISTIC\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:PESSIMISTIC_READ\\|Doctrine\\\\DBAL\\\\LockMode\\:\\:PESSIMISTIC_WRITE\\|int given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/JoinedSubclassPersister.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/SingleTablePersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\SingleTablePersister\\:\\:generateFilterConditionSQL\\(\\) has parameter \\$targetEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/Entity/SingleTablePersister.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\SqlExpressionVisitor\\:\\:__construct\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/SqlExpressionVisitor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Persisters\\\\SqlValueVisitor\\:\\:getParamsAndTypes\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Persisters/SqlValueVisitor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$className of static method Doctrine\\\\ORM\\\\Proxy\\\\Autoloader\\:\\:resolveFile\\(\\) expects class\\-string, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/Autoloader.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 of closure expects class\\-string, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/Autoloader.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\DefaultProxyClassNameResolver\\:\\:resolveClassName\\(\\) should return class\\-string\\<T of object\\> but returns class\\-string\\<Doctrine\\\\Persistence\\\\Proxy\\<T of object\\>\\>\\|class\\-string\\<T of object\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/DefaultProxyClassNameResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\DefaultProxyClassNameResolver\\:\\:resolveClassName\\(\\) should return class\\-string\\<T of object\\> but returns string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/DefaultProxyClassNameResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$isEmbeddedClass\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$isMappedSuperclass\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined static method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyGhost\\(\\)\\.$#'\n\t\t\tidentifier: staticMethod.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function is_bool\\(\\) with bool will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Comparison operation \"\\<\" between 0\\|1\\|2\\|3\\|4 and 0 is always false\\.$#'\n\t\t\tidentifier: smaller.alwaysFalse\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Comparison operation \"\\>\" between 0\\|1\\|2\\|3\\|4 and 4 is always false\\.$#'\n\t\t\tidentifier: greater.alwaysFalse\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyInitializer\\(\\) has Doctrine\\\\ORM\\\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\\.$#'\n\t\t\tidentifier: throws.unusedType\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyInitializer\\(\\) has parameter \\$classMetadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyInitializer\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyInitializer\\(\\) return type with generic interface Doctrine\\\\ORM\\\\Proxy\\\\InternalProxy does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:generateProxyClass\\(\\) has parameter \\$class with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:generateProxyClasses\\(\\) has parameter \\$classes with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:generateSerializeImpl\\(\\) has parameter \\$class with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:generateUseLazyGhostTrait\\(\\) has parameter \\$class with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:loadProxyClass\\(\\) has parameter \\$class with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:skipClass\\(\\) has parameter \\$metadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$class of method Doctrine\\\\ORM\\\\Utility\\\\IdentifierFlattener\\:\\:flattenIdentifier\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$filename of function filemtime expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$newScope of static method Closure\\:\\:bind\\(\\) expects ''static''\\|class\\-string\\|object\\|null, string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Result of \\|\\| is always false\\.$#'\n\t\t\tidentifier: booleanOr.alwaysFalse\n\t\t\tcount: 1\n\t\t\tpath: src/Proxy/ProxyFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\:\\:processParameterMappings\\(\\) return type has no value type specified in iterable type array\\.$#'\n\t\t\tidentifier: missingType.iterableValue\n\t\t\tcount: 1\n\t\t\tpath: src/Query.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$parameters of method Doctrine\\\\ORM\\\\AbstractQuery\\:\\:toIterable\\(\\) expects array\\<mixed\\>\\|Doctrine\\\\Common\\\\Collections\\\\ArrayCollection\\<int, Doctrine\\\\ORM\\\\Query\\\\Parameter\\>, iterable\\<\\(int\\|string\\), mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$sqlParams of method Doctrine\\\\ORM\\\\Query\\:\\:evictResultSetCache\\(\\) expects array\\<string, mixed\\>, list\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\BitAndFunction\\:\\:\\$firstArithmetic \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/BitAndFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\BitAndFunction\\:\\:\\$secondArithmetic \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/BitAndFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\BitOrFunction\\:\\:\\$firstArithmetic \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/BitOrFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\BitOrFunction\\:\\:\\$secondArithmetic \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/BitOrFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\:\\:\\$value\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateAddFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\DateAddFunction\\:\\:\\$firstDateExpression \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateAddFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\DateAddFunction\\:\\:\\$intervalExpression \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateAddFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\DateDiffFunction\\:\\:\\$date1 \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateDiffFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\DateDiffFunction\\:\\:\\$date2 \\(Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\) does not accept Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateDiffFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\:\\:\\$value\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/DateSubFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Query/AST/Functions/IdentityFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$joinColumn of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinColumnName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\JoinColumnMapping, Doctrine\\\\ORM\\\\Mapping\\\\JoinColumnMapping\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/IdentityFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\|true given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/LocateFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/SizeFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/SizeFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/SizeFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$association of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinTableName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/Functions/SizeFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$mode of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getTrimExpression\\(\\) expects Doctrine\\\\DBAL\\\\Platforms\\\\TrimMode, Doctrine\\\\DBAL\\\\Platforms\\\\TrimMode\\:\\:BOTH\\|Doctrine\\\\DBAL\\\\Platforms\\\\TrimMode\\:\\:LEADING\\|Doctrine\\\\DBAL\\\\Platforms\\\\TrimMode\\:\\:TRAILING\\|Doctrine\\\\DBAL\\\\Platforms\\\\TrimMode\\:\\:UNSPECIFIED\\|int given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Query/AST/Functions/TrimFunction.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkJoinPathExpression\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/JoinClassPathExpression.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkJoinVariableDeclaration\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/JoinVariableDeclaration.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkWhenClauseExpression\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/SimpleWhenClause.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkWhenClauseExpression\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/AST/WhenClause.php\n\n\t\t-\n\t\t\tmessage: '#^Argument of an invalid type list\\<string\\>\\|string supplied for foreach, only iterables are supported\\.$#'\n\t\t\tidentifier: foreach.nonIterable\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableDeleteExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Cannot assign new offset to list\\<string\\>\\|string\\.$#'\n\t\t\tidentifier: offsetAssign.dimType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableDeleteExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Exec\\\\MultiTableDeleteExecutor\\:\\:execute\\(\\) should return int but returns int\\|string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableDeleteExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columns of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getColumnDeclarationListSQL\\(\\) expects list\\<array\\{name\\: string, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type, default\\: mixed, notnull\\?\\: bool, autoincrement\\: bool, columnDefinition\\: non\\-empty\\-string\\|null, comment\\: string, charset\\?\\: non\\-empty\\-string\\|null, \\.\\.\\.\\}\\>, array\\<string, array\\{name\\: string, notnull\\: true, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type\\}\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableDeleteExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Argument of an invalid type list\\<string\\>\\|string supplied for foreach, only iterables are supported\\.$#'\n\t\t\tidentifier: foreach.nonIterable\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Exec\\\\MultiTableUpdateExecutor\\:\\:execute\\(\\) should return int but returns int\\|string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columns of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getColumnDeclarationListSQL\\(\\) expects list\\<array\\{name\\: string, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type, default\\: mixed, notnull\\?\\: bool, autoincrement\\: bool, columnDefinition\\: non\\-empty\\-string\\|null, comment\\: string, charset\\?\\: non\\-empty\\-string\\|null, \\.\\.\\.\\}\\>, array\\<string, array\\{name\\: string, notnull\\: true, type\\: Doctrine\\\\DBAL\\\\Types\\\\Type\\}\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$types of method Doctrine\\\\DBAL\\\\Connection\\:\\:executeStatement\\(\\) expects array\\<int\\<0, max\\>\\|string, Doctrine\\\\DBAL\\\\ArrayParameterType\\|Doctrine\\\\DBAL\\\\ParameterType\\|Doctrine\\\\DBAL\\\\Types\\\\Type\\|string\\>, list\\<Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:ASCII\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:BINARY\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:INTEGER\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:STRING\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:ASCII\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:BINARY\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:BOOLEAN\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:INTEGER\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:LARGE_OBJECT\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:NULL\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:STRING\\|Doctrine\\\\DBAL\\\\Types\\\\Type\\|int\\|string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/MultiTableUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$sql of method Doctrine\\\\DBAL\\\\Connection\\:\\:executeQuery\\(\\) expects string, list\\<string\\>\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/SingleSelectExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Exec\\\\SingleTableDeleteUpdateExecutor\\:\\:execute\\(\\) should return int but returns int\\|string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/SingleTableDeleteUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$sql of method Doctrine\\\\DBAL\\\\Connection\\:\\:executeStatement\\(\\) expects string, list\\<string\\>\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Exec/SingleTableDeleteUpdateExecutor.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Func\\:\\:getArguments\\(\\) should return list\\<mixed\\> but returns array\\<mixed\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Expr/Func.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\ParameterTypeInferer\\:\\:inferType\\(\\) never returns int so it can be removed from the return type\\.$#'\n\t\t\tidentifier: return.unusedType\n\t\t\tcount: 1\n\t\t\tpath: src/Query/ParameterTypeInferer.php\n\n\t\t-\n\t\t\tmessage: '#^@readonly property cannot have a default value\\.$#'\n\t\t\tidentifier: property.readOnlyByPhpDocDefaultValue\n\t\t\tcount: 3\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method object\\:\\:parse\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:CustomFunctionsReturningStrings\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode but returns object\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:getMetadataForDqlAlias\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$AST of method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:processDeferredNewObjectExpressions\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SelectStatement, Doctrine\\\\ORM\\\\Query\\\\AST\\\\DeleteStatement\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\SelectStatement\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\UpdateStatement given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$expected of method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:syntaxError\\(\\) expects string, int\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 3\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$expression of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\ParenthesisExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$stringPattern of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\LikeExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\InputParameter\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\Literal\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\PathExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Strict comparison using \\=\\=\\= between 102 and 102 will always evaluate to true\\.$#'\n\t\t\tidentifier: identical.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Unreachable statement \\- code above always terminates\\.$#'\n\t\t\tidentifier: deadCode.unreachable\n\t\t\tcount: 2\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Using nullsafe property access \"\\?\\-\\>position\" on left side of \\?\\? is unnecessary\\. Use \\-\\> instead\\.$#'\n\t\t\tidentifier: nullsafe.neverNull\n\t\t\tcount: 1\n\t\t\tpath: src/Query/Parser.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Query/ResultSetMappingBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\ResultSetMappingBuilder\\:\\:isInheritanceSupported\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/ResultSetMappingBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Instanceof between Doctrine\\\\ORM\\\\Query\\\\AST\\\\DeleteStatement and Doctrine\\\\ORM\\\\Query\\\\AST\\\\DeleteStatement will always evaluate to true\\.$#'\n\t\t\tidentifier: instanceof.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlOutputWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$sourceToTargetKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 3\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 6\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Cannot assign new offset to list\\<string\\>\\|string\\.$#'\n\t\t\tidentifier: offsetAssign.dimType\n\t\t\tcount: 2\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Match arm comparison between 3 and 3 is always true\\.$#'\n\t\t\tidentifier: match.alwaysTrue\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:generateClassTableInheritanceJoins\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:generateFilterConditionSQL\\(\\) has parameter \\$targetEntity with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:getChildDiscriminatorsFromClassMetadata\\(\\) has parameter \\$rootClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:getMetadataForDqlAlias\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$association of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getJoinTableName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$condTerm of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkConditionalTerm\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalFactor\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalPrimary\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalTerm, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Phase2OptimizableConditional given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$identVariable of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkEntityIdentificationVariable\\(\\) expects string, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$fieldName of method Doctrine\\\\ORM\\\\Query\\\\ResultSetMapping\\:\\:addIndexBy\\(\\) expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:\\$selectedClasses with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/SqlWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Query\\\\TreeWalkerAdapter\\:\\:getMetadataForDqlAlias\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Query/TreeWalkerAdapter.php\n\n\t\t-\n\t\t\tmessage: '#^Argument of an invalid type array\\<int\\|string, array\\<int\\|string, object\\>\\|object\\|string\\>\\|object\\|string\\|false supplied for foreach, only iterables are supported\\.$#'\n\t\t\tidentifier: foreach.nonIterable\n\t\t\tcount: 1\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:getParameter\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\Parameter\\|null but returns Doctrine\\\\ORM\\\\Query\\\\Parameter\\|false\\|null\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<''join''\\|int\\<0, max\\>, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, array\\{Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx\\|Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Orx\\}\\|Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<''join''\\|int\\<0, max\\>, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, array\\{Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Composite\\}\\|Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<''join''\\|int\\<0, max\\>, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, non\\-empty\\-array\\<string, Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Join\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/QueryBuilder.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Repository\\\\DefaultRepositoryFactory\\:\\:createRepository\\(\\) return type with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Repository/DefaultRepositoryFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Repository\\\\DefaultRepositoryFactory\\:\\:\\$repositoryList with generic class Doctrine\\\\ORM\\\\EntityRepository does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Repository/DefaultRepositoryFactory.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/GenerateProxiesCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$filename of function file_exists expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/GenerateProxiesCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$filename of function is_writable expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/GenerateProxiesCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\Command\\\\MappingDescribeCommand\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/MappingDescribeCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$entityListeners of method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\Command\\\\MappingDescribeCommand\\:\\:formatEntityListeners\\(\\) expects list\\<object\\>, array\\<string, list\\<array\\{class\\: class\\-string, method\\: string\\}\\>\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/MappingDescribeCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(class\\-string\\)\\: bool\\)\\|null, Closure\\(mixed\\)\\: \\(0\\|1\\|false\\) given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/MappingDescribeCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:createSchema\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/SchemaTool/CreateCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getCreateSchemaSql\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/SchemaTool/CreateCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:dropSchema\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/SchemaTool/DropCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getDropSchemaSQL\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Tools/Console/Command/SchemaTool/DropCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getUpdateSchemaSql\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/Command/SchemaTool/UpdateCommand.php\n\n\t\t-\n\t\t\tmessage: '#^Class Doctrine\\\\ORM\\\\Tools\\\\Console\\\\MetadataFilter extends generic class FilterIterator but does not specify its types\\: TKey, TValue, TIterator$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/MetadataFilter.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\MetadataFilter\\:\\:__construct\\(\\) has parameter \\$metadata with generic class ArrayIterator but does not specify its types\\: TKey, TValue$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/MetadataFilter.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\MetadataFilter\\:\\:filter\\(\\) has parameter \\$metadatas with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/MetadataFilter.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\MetadataFilter\\:\\:filter\\(\\) return type with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/MetadataFilter.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\MetadataFilter\\:\\:getInnerIterator\\(\\) return type with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Console/MetadataFilter.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$string of function html_entity_decode expects string, string\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Debug.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/DebugUnitOfWorkListener.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 14\n\t\t\tpath: src/Tools/DebugUnitOfWorkListener.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Event\\\\GenerateSchemaTableEventArgs\\:\\:__construct\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Event/GenerateSchemaTableEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Event\\\\GenerateSchemaTableEventArgs\\:\\:getClassMetadata\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Event/GenerateSchemaTableEventArgs.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/CountOutputWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/LimitSubqueryOutputWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Pagination\\\\LimitSubqueryOutputWalker\\:\\:walkSelectStatement\\(\\) should return string but returns list\\<string\\>\\|string\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/LimitSubqueryOutputWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/LimitSubqueryOutputWalker.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\Pagination\\\\Paginator\\:\\:count\\(\\) should return int\\<0, max\\> but returns int\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/Paginator.php\n\n\t\t-\n\t\t\tmessage: '#^PHPDoc tag @var for variable \\$parameters contains generic interface Doctrine\\\\Common\\\\Collections\\\\Collection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/Paginator.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$parameters of method Doctrine\\\\ORM\\\\AbstractQuery\\:\\:setParameters\\(\\) expects array\\<mixed\\>\\|Doctrine\\\\Common\\\\Collections\\\\ArrayCollection\\<int, Doctrine\\\\ORM\\\\Query\\\\Parameter\\>, Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\<Doctrine\\\\ORM\\\\Query\\\\Parameter\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/Pagination/Paginator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\ResolveTargetEntityListener\\:\\:remapAssociation\\(\\) has parameter \\$classMetadata with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/ResolveTargetEntityListener.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 3\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method getColumns\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\Index\\:\n\t\t\t\tUse \\{@see getIndexedColumns\\(\\)\\} instead\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method getForeignColumns\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint\\:\n\t\t\t\tUse \\{@see getReferencedColumnNames\\(\\)\\} instead\\.\n\n\t\t\t\tReturns the names of the referenced table columns\n\t\t\t\tthe foreign key constraint is associated with\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method getForeignTableName\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint\\:\n\t\t\t\tUse \\{@see getReferencedTableName\\(\\)\\} instead\\.\n\n\t\t\t\tReturns the name of the referenced table\n\t\t\t\tthe foreign key constraint is associated with\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method getLocalColumns\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint\\:\n\t\t\t\tUse \\{@see getReferencingColumnNames\\(\\)\\} instead\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method getPrimaryKey\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\n\t\t\t\tUse \\{@see getPrimaryKeyConstraint\\(\\)\\} instead\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method removeForeignKey\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\n\t\t\t\tUse \\{@link dropForeignKey\\(\\)\\} instead\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '''\n\t\t\t\t#^Call to deprecated method setPrimaryKey\\(\\) of class Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\n\t\t\t\tUse \\{@see addPrimaryKeyConstraint\\(\\)\\} instead\\.$#\n\t\t\t'''\n\t\t\tidentifier: method.deprecated\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function is_numeric\\(\\) with int\\<0, max\\> will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with ''Doctrine\\\\\\\\DBAL\\\\\\\\Schema\\\\\\\\Index'' and ''getIndexedColumns'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Call to function method_exists\\(\\) with Doctrine\\\\DBAL\\\\Schema\\\\Table and ''getPrimaryKeyConstr…'' will always evaluate to true\\.$#'\n\t\t\tidentifier: function.alreadyNarrowedType\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:addDiscriminatorColumnDefinition\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:createSchema\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:dropSchema\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:gatherColumn\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:gatherColumns\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:gatherRelationJoinColumns\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:gatherRelationsSql\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getAssetName\\(\\) has parameter \\$asset with generic class Doctrine\\\\DBAL\\\\Schema\\\\AbstractAsset but does not specify its types\\: N$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getCreateSchemaSql\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getDefiningClass\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getDefiningClass\\(\\) return type with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getDropSchemaSQL\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getIndexColumns\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getSchemaFromMetadata\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getUpdateSchemaSql\\(\\) has parameter \\$classes with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:processingNotRequired\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Negated boolean expression is always false\\.$#'\n\t\t\tidentifier: booleanNot.alwaysFalse\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$classes of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:getUpdateSchemaSql\\(\\) expects list\\<Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columnNames of method Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\\:addIndex\\(\\) expects non\\-empty\\-list\\<string\\>, list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#1 \\$columnNames of method Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\\:addUniqueIndex\\(\\) expects non\\-empty\\-list\\<string\\>, array\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$columns of class Doctrine\\\\DBAL\\\\Schema\\\\Index constructor expects non\\-empty\\-list\\<string\\>, list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$localColumnNames of method Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\\:addForeignKeyConstraint\\(\\) expects non\\-empty\\-list\\<string\\>, list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$primaryKeyColumns of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:addPrimaryKeyConstraint\\(\\) expects non\\-empty\\-array\\<non\\-empty\\-string\\>, list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$primaryKeyColumns of method Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:addPrimaryKeyConstraint\\(\\) expects non\\-empty\\-array\\<non\\-empty\\-string\\>, non\\-empty\\-list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$foreignColumnNames of method Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\\:addForeignKeyConstraint\\(\\) expects non\\-empty\\-list\\<string\\>, list\\<string\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\Tools\\\\SchemaTool\\:\\:\\$schemaManager with generic class Doctrine\\\\DBAL\\\\Schema\\\\AbstractSchemaManager does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaTool.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$inversedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 6\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 9\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$relationToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$relationToTargetKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Call to an undefined method Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:orderBy\\(\\)\\.$#'\n\t\t\tidentifier: method.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaValidator\\:\\:validateClass\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Tools\\\\SchemaValidator\\:\\:validatePropertiesTypes\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Tools/SchemaValidator.php\n\n\t\t-\n\t\t\tmessage: '#^ Parameter \\#3 \\$changeSet of class Doctrine\\\\ORM\\\\Event\\\\PreUpdateEventArgs constructor is passed by reference, so it expects variables only$#'\n\t\t\tidentifier: argument.byRef\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$inversedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 3\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 3\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$targetToSourceKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:convertSingleFieldIdentifierToPHPValue\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:eagerLoadCollections\\(\\) has parameter \\$collections with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:getEntityChangeSet\\(\\) return type with generic class Doctrine\\\\ORM\\\\PersistentCollection does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:hasMissingIdsWhichAreForeignKeys\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:isCollectionScheduledForDeletion\\(\\) has parameter \\$coll with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:loadCollection\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:normalizeIdentifier\\(\\) has parameter \\$targetClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:scheduleCollectionDeletion\\(\\) has parameter \\$coll with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:scheduleCollectionForBatchLoading\\(\\) has parameter \\$collection with generic class Doctrine\\\\ORM\\\\PersistentCollection but does not specify its types\\: TKey, T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:scheduleCollectionForBatchLoading\\(\\) has parameter \\$sourceClass with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$assoc of method Doctrine\\\\ORM\\\\PersistentCollection\\<\\(int\\|string\\),mixed\\>\\:\\:setOwner\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping&Doctrine\\\\ORM\\\\Mapping\\\\ToManyAssociationMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 4\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$assoc of method Doctrine\\\\ORM\\\\PersistentCollection\\<\\*NEVER\\*,\\*NEVER\\*\\>\\:\\:setOwner\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\AssociationMapping&Doctrine\\\\ORM\\\\Mapping\\\\ToManyAssociationMapping, Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#2 \\$length of function array_chunk expects int\\<1, max\\>, int given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 2\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#3 \\$collection of class Doctrine\\\\ORM\\\\PersistentCollection constructor expects Doctrine\\\\Common\\\\Collections\\\\Collection\\<\\(int\\|string\\), mixed\\>&Doctrine\\\\Common\\\\Collections\\\\Selectable\\<\\(int\\|string\\), mixed\\>, Doctrine\\\\Common\\\\Collections\\\\Collection given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter \\#5 \\$invoke of method Doctrine\\\\ORM\\\\Event\\\\ListenersInvoker\\:\\:invoke\\(\\) expects int\\<0, 7\\>, int\\<min, \\-1\\>\\|int\\<1, max\\> given\\.$#'\n\t\t\tidentifier: argument.type\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Parameter &\\$visited by\\-ref type of method Doctrine\\\\ORM\\\\UnitOfWork\\:\\:cascadeDetach\\(\\) expects array\\<int, object\\>, array\\<mixed\\> given\\.$#'\n\t\t\tidentifier: parameterByRef.type\n\t\t\tcount: 2\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Property Doctrine\\\\ORM\\\\UnitOfWork\\:\\:\\$entityChangeSets \\(array\\<int, array\\<string, array\\{mixed, mixed\\}\\>\\>\\) does not accept non\\-empty\\-array\\<int, array\\<string, array\\{mixed, mixed\\}\\|Doctrine\\\\ORM\\\\PersistentCollection\\>\\>\\.$#'\n\t\t\tidentifier: assign.propertyType\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Unable to resolve the template type T in call to method static method Symfony\\\\Component\\\\VarExporter\\\\Hydrator\\:\\:hydrate\\(\\)$#'\n\t\t\tidentifier: argument.templateType\n\t\t\tcount: 1\n\t\t\tpath: src/UnitOfWork.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/HierarchyDiscriminatorResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$subClasses\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/HierarchyDiscriminatorResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\HierarchyDiscriminatorResolver\\:\\:resolveDiscriminatorsForClass\\(\\) has parameter \\$rootClassMetadata with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/HierarchyDiscriminatorResolver.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/IdentifierFlattener.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\IdentifierFlattener\\:\\:__construct\\(\\) has parameter \\$metadataFactory with generic interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/IdentifierFlattener.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\IdentifierFlattener\\:\\:flattenIdentifier\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/IdentifierFlattener.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\JoinTableMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$joinTable\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 2\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToManyAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneInverseSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$mappedBy\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$relationToTargetKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Access to an undefined property Doctrine\\\\ORM\\\\Mapping\\\\ManyToManyOwningSideMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\:\\:\\$sourceToTargetKeyColumns\\.$#'\n\t\t\tidentifier: property.notFound\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getTypeOfColumn\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getTypeOfField\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:inferParameterTypes\\(\\) has parameter \\$class with generic class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata but does not specify its types\\: T$#'\n\t\t\tidentifier: missingType.generics\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n\n\t\t-\n\t\t\tmessage: '#^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:inferParameterTypes\\(\\) should return list\\<Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:ASCII\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:BINARY\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:INTEGER\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:STRING\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:ASCII\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:BINARY\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:BOOLEAN\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:INTEGER\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:LARGE_OBJECT\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:NULL\\|Doctrine\\\\DBAL\\\\ParameterType\\:\\:STRING\\|string\\> but returns list\\<Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:ASCII\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:BINARY\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:INTEGER\\|Doctrine\\\\DBAL\\\\ArrayParameterType\\:\\:STRING\\|int\\>\\.$#'\n\t\t\tidentifier: return.type\n\t\t\tcount: 1\n\t\t\tpath: src/Utility/PersisterHelper.php\n"
  },
  {
    "path": "phpstan-dbal3.neon",
    "content": "includes:\n    - phpstan-baseline.neon\n    - phpstan-params.neon\n\nparameters:\n    reportUnmatchedIgnoredErrors: false # Some errors in the baseline only apply to DBAL 4\n    ignoreErrors:\n        # Symfony cache supports passing a key prefix to the clear method.\n        - '/^Method Psr\\\\Cache\\\\CacheItemPoolInterface\\:\\:clear\\(\\) invoked with 1 parameter, 0 required\\.$/'\n\n        # We can be certain that those values are not matched.\n        -\n            message: '~^Match expression does not handle remaining values:~'\n            path: src/Utility/PersisterHelper.php\n\n        # DBAL 4 compatibility\n        -\n            message: '~^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\TrimFunction::getTrimMode\\(\\) never returns .* so it can be removed from the return type\\.$~'\n            path: src/Query/AST/Functions/TrimFunction.php\n\n        -\n            message: '~.*getTrimExpression.*expects int.*~'\n            path: src/Query/AST/Functions/TrimFunction.php\n\n        -\n            message: '~^Call to static method unquoted\\(\\) on an unknown class Doctrine\\\\DBAL\\\\Schema\\\\Name\\\\Identifier\\.$~'\n            path: src/Tools/SchemaTool.php\n\n        -\n            message: '~^Instantiated class Doctrine\\\\DBAL\\\\Schema\\\\Name\\\\UnqualifiedName not found\\.$~'\n            path: src/Tools/SchemaTool.php\n\n        -\n            message: '~^Call to an undefined method Doctrine\\\\DBAL\\\\Schema\\\\Table::addPrimaryKeyConstraint\\(\\)\\.$~'\n            path: src/Tools/SchemaTool.php\n\n        -\n            message: '~^Call to static method quoted\\(\\) on an unknown class Doctrine\\\\DBAL\\\\Schema\\\\Name\\\\Identifier\\.$~'\n            path: src/Tools/SchemaTool.php\n\n        -\n            message: '~^Call to an undefined method Doctrine\\\\DBAL\\\\Schema\\\\Table\\:\\:getObjectName\\(\\)\\.$~'\n            path: src/Mapping/Driver/DatabaseDriver.php\n\n        -\n            message: '~^Call to an undefined method Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint::get.*\\.$~'\n            identifier: method.notFound\n\n        -\n            message: '~createComparator~'\n            identifier: arguments.count\n\n        -\n            message: '~UnqualifiedName~'\n            identifier: class.notFound\n\n        -\n            message: '~IndexedColumn~'\n            identifier: class.notFound\n\n        -\n            message: '~PrimaryKeyConstraint~'\n            identifier: class.notFound\n\n        -\n            message: '~IndexType~'\n            identifier: class.notFound\n\n        -\n            message: '~dropForeignKey~'\n            identifier: method.notFound\n\n        -\n            message: '~getIndexedColumns~'\n            identifier: method.notFound\n\n        -\n            message: '~getPrimaryKeyConstraint~'\n            identifier: method.notFound\n\n        -\n            message: '~PrimaryKeyConstraint~'\n            identifier: class.notFound\n            path: src/Tools/SchemaTool.php\n\n        -\n            message: '~^Call to method toString.*UnqualifiedName\\.$~'\n            path: src/Tools/SchemaTool.php\n\n        - '~^Call to method getObjectName\\(\\) on an unknown class Doctrine\\\\DBAL\\\\Schema\\\\NamedObject\\.$~'\n\n        - '~^Class Doctrine\\\\DBAL\\\\Platforms\\\\SQLitePlatform not found\\.$~'\n\n        - '~^Class Doctrine\\\\DBAL\\\\Schema\\\\NamedObject not found\\.$~'\n\n        -\n            message: '~sort~'\n            identifier: argument.unresolvableType\n            path: src/Mapping/Driver/DatabaseDriver.php\n\n        -\n            message: '#^Parameter \\#1 \\$asset of static method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\DatabaseDriver\\:\\:getAssetName\\(\\) expects Doctrine\\\\DBAL\\\\Schema\\\\AbstractAsset, Doctrine\\\\DBAL\\\\Schema\\\\Column\\|false given\\.$#'\n            identifier: argument.type\n            path: src/Mapping/Driver/DatabaseDriver.php\n\n        -\n            message: '#^Instantiated class Doctrine\\\\DBAL\\\\Schema\\\\DefaultExpression\\\\\\w+ not found\\.$#'\n            identifier: class.notFound\n            path: src/Tools/SchemaTool.php\n\n\n        # To be removed in 4.0\n        -\n            message: '#Negated boolean expression is always false\\.#'\n            paths:\n                - src/Mapping/Driver/AttributeDriver.php\n\n        -\n            message: '~^Call to deprecated method getEventManager\\(\\) of class Doctrine\\\\DBAL\\\\Connection\\.$~'\n            path: src/EntityManager.php\n        -\n            message: '~deprecated class Doctrine\\\\DBAL\\\\Tools\\\\Console\\\\Command\\\\ReservedWordsCommand\\:~'\n            path: src/Tools/Console/ConsoleRunner.php\n\n        # Compatibility with Persistence 3\n        -\n            message: '#Expression on left side of \\?\\? is not nullable.#'\n            path: src/Mapping/Driver/AttributeDriver.php\n\n        -\n            message: '~^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getArrayBindingType\\(\\) never returns .* so it can be removed from the return type\\.$~'\n            path: src/Utility/PersisterHelper.php\n\n        -\n            message: '~inferParameterTypes.*should return~'\n            path: src/Utility/PersisterHelper.php\n\n        -\n            message: '~.*appendLockHint.*expects.*LockMode given~'\n            paths:\n                - src/Persisters/Entity/BasicEntityPersister.php\n                - src/Persisters/Entity/JoinedSubclassPersister.php\n\n        -\n            message: '~.*executeStatement.*expects~'\n            path: src/Query/Exec/MultiTableUpdateExecutor.php\n\n        -\n            message: '~method_exists.*getEventManager~'\n            path: src/EntityManager.php\n\n        -\n            message: '~method_exists.*getIdentitySequence~'\n            path: src/Mapping/ClassMetadataFactory.php\n\n        -\n            message: '~expand(Criteria)?Parameters.*should return array~'\n            path: src/Persisters/Entity/BasicEntityPersister.php\n\n        -\n            message: '~inferType.*never returns~'\n            path: src/Query/ParameterTypeInferer.php\n"
  },
  {
    "path": "phpstan-params.neon",
    "content": "parameters:\n    level: 7\n    paths:\n        - src\n        - tests/StaticAnalysis\n    excludePaths:\n        - src/Mapping/Driver/AttributeReader.php\n    earlyTerminatingMethodCalls:\n        Doctrine\\ORM\\Query\\Parser:\n            - syntaxError\n    phpVersion: 80400\n"
  },
  {
    "path": "phpstan.neon",
    "content": "includes:\n    - phpstan-baseline.neon\n    - phpstan-params.neon\n\nparameters:\n    ignoreErrors:\n        # Symfony cache supports passing a key prefix to the clear method.\n        - '/^Method Psr\\\\Cache\\\\CacheItemPoolInterface\\:\\:clear\\(\\) invoked with 1 parameter, 0 required\\.$/'\n\n        # We can be certain that those values are not matched.\n        -\n            message: '~^Match expression does not handle remaining values:~'\n            path: src/Utility/PersisterHelper.php\n\n        # The return type is already narrow enough.\n        - '~^Method Doctrine\\\\ORM\\\\Query\\\\ParameterTypeInferer\\:\\:inferType\\(\\) never returns ''[a-z_]+'' so it can be removed from the return type\\.$~'\n        - '~^Method Doctrine\\\\ORM\\\\Query\\\\ParameterTypeInferer\\:\\:inferType\\(\\) never returns Doctrine\\\\DBAL\\\\(?:Array)?ParameterType\\:\\:[A-Z_]+ so it can be removed from the return type\\.$~'\n\n        # DBAL 4 compatibility\n        -\n            message: '~^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\TrimFunction::getTrimMode\\(\\) never returns .* so it can be removed from the return type\\.$~'\n            path: src/Query/AST/Functions/TrimFunction.php\n        -\n            message: '~^Method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getArrayBindingType\\(\\) never returns .* so it can be removed from the return type\\.$~'\n            path: src/Utility/PersisterHelper.php\n\n        # Compatibility with DBAL 3\n        # See https://github.com/doctrine/dbal/pull/3480\n        -\n            message: '~^Result of method Doctrine\\\\DBAL\\\\Connection::commit\\(\\) \\(void\\) is used\\.$~'\n            path: src/UnitOfWork.php\n        -\n            message: '~^Strict comparison using === between null and false will always evaluate to false\\.$~'\n            path: src/UnitOfWork.php\n        -\n            message: '~^Variable \\$e on left side of \\?\\? always exists and is not nullable\\.$~'\n            path: src/UnitOfWork.php\n\n        -\n            message: '~^Parameter #2 \\$command of static method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\ConsoleRunner::addCommandToApplication\\(\\) expects Symfony\\\\Component\\\\Console\\\\Command\\\\Command, Doctrine\\\\DBAL\\\\Tools\\\\Console\\\\Command\\\\ReservedWordsCommand given\\.$~'\n            path: src/Tools/Console/ConsoleRunner.php\n\n        -\n            message: '~Strict comparison using \\=\\=\\= between callable\\(\\)\\: mixed and null will always evaluate to false\\.~'\n            path: src/Tools/SchemaTool.php\n\n        # To be removed in 4.0\n        -\n            message: '#Negated boolean expression is always false\\.#'\n            paths:\n                - src/Mapping/Driver/AttributeDriver.php\n\n        # Compatibility with Persistence 3\n        -\n            message: '#Expression on left side of \\?\\? is not nullable.#'\n            path: src/Mapping/Driver/AttributeDriver.php\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Use this configuration file as a template to run the tests against any dbms.\n    Procedure:\n        1) Save a copy of this file with a name of your choosing. It doesn't matter\n           where you place it as long as you know where it is.\n           i.e. \"mysqlconf.xml\" (It needs the ending .xml).\n        2) Edit the file and fill in your settings (database name, type, username, etc.)\n           Just change the \"value\"s, not the names of the var elements.\n        3) To run the tests against the database type the following from within the\n           tests/ folder: phpunit -c <filename> ...\n           Example: phpunit -c mysqlconf.xml AllTests\n-->\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"vendor/phpunit/phpunit/phpunit.xsd\"\n         colors=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         displayDetailsOnTestsThatTriggerDeprecations=\"true\"\n         displayDetailsOnTestsThatTriggerNotices=\"true\"\n         displayDetailsOnTestsThatTriggerWarnings=\"true\"\n         failOnNotice=\"true\"\n         failOnWarning=\"true\"\n         failOnRisky=\"true\"\n         bootstrap=\"./tests/Tests/TestInit.php\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <testsuites>\n        <testsuite name=\"Doctrine ORM Test Suite\">\n            <directory>./tests/Tests/ORM</directory>\n        </testsuite>\n    </testsuites>\n\n    <groups>\n        <exclude>\n            <group>performance</group>\n            <group>locking_functional</group>\n        </exclude>\n    </groups>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\"/>\n        <!-- \"Real\" test database -->\n        <var name=\"db_driver\" value=\"pdo_sqlite\"/>\n        <var name=\"db_memory\" value=\"true\"/>\n        <!-- to use another database driver / credentials, provide them like so:\n          <var name=\"db_driver\" value=\"pdo_mysql\"/>\n          <var name=\"db_host\" value=\"localhost\" />\n          <var name=\"db_user\" value=\"root\" />\n          <var name=\"db_password\" value=\"\" />\n          <var name=\"db_dbname\" value=\"doctrine_tests\" />\n          <var name=\"db_port\" value=\"3306\"/>-->\n        <!--<var name=\"db_event_subscribers\" value=\"Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit\">-->\n        <!--\n         At the start of each test run, we will drop and recreate the test database.\n\n         By default we assume that the `db_` config above has unrestricted access to the provided database\n         platform.\n\n         If you prefer, you can provide a restricted user above and a separate `privileged_db` config\n         block to provide details of a privileged connection to use for the setup / teardown actions.\n\n         Note that these configurations are not merged - if you specify a `privileged_db_driver` then\n         you must also specify all the other options that your driver requires.\n\n        <var name=\"privileged_db_driver\" value=\"pdo_mysql\"/>\n        <var name=\"privileged_db_host\" value=\"localhost\" />\n        <var name=\"privileged_db_user\" value=\"root\" />\n        <var name=\"privileged_db_password\" value=\"\" />\n        <var name=\"privileged_db_dbname\" value=\"doctrine_tests_tmp\" />\n        <var name=\"privileged_db_port\" value=\"3306\"/>\n        -->\n        <env name=\"COLUMNS\" value=\"120\"/>\n        <env name=\"DOCTRINE_DEPRECATIONS\" value=\"trigger\"/>\n    </php>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory>src</directory>\n        </include>\n    </source>\n</phpunit>\n"
  },
  {
    "path": "src/AbstractQuery.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse BackedEnum;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\ORM\\Cache\\TimestampCacheKey;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException as ORMMappingException;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Persistence\\Mapping\\MappingException;\nuse LogicException;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Traversable;\n\nuse function array_map;\nuse function array_shift;\nuse function assert;\nuse function count;\nuse function is_array;\nuse function is_numeric;\nuse function is_object;\nuse function is_scalar;\nuse function is_string;\nuse function iterator_to_array;\nuse function ksort;\nuse function reset;\nuse function serialize;\nuse function sha1;\n\n/**\n * Base contract for ORM queries. Base class for Query and NativeQuery.\n *\n * @link    www.doctrine-project.org\n */\nabstract class AbstractQuery\n{\n    /* Hydration mode constants */\n\n    /**\n     * Hydrates an object graph. This is the default behavior.\n     */\n    public const HYDRATE_OBJECT = 1;\n\n    /**\n     * Hydrates an array graph.\n     */\n    public const HYDRATE_ARRAY = 2;\n\n    /**\n     * Hydrates a flat, rectangular result set with scalar values.\n     */\n    public const HYDRATE_SCALAR = 3;\n\n    /**\n     * Hydrates a single scalar value.\n     */\n    public const HYDRATE_SINGLE_SCALAR = 4;\n\n    /**\n     * Very simple object hydrator (optimized for performance).\n     */\n    public const HYDRATE_SIMPLEOBJECT = 5;\n\n    /**\n     * Hydrates scalar column value.\n     */\n    public const HYDRATE_SCALAR_COLUMN = 6;\n\n    /**\n     * The parameter map of this query.\n     *\n     * @var ArrayCollection|Parameter[]\n     * @phpstan-var ArrayCollection<int, Parameter>\n     */\n    protected ArrayCollection $parameters;\n\n    /**\n     * The user-specified ResultSetMapping to use.\n     */\n    protected ResultSetMapping|null $resultSetMapping = null;\n\n    /**\n     * The map of query hints.\n     *\n     * @phpstan-var array<string, mixed>\n     */\n    protected array $hints = [];\n\n    /**\n     * The hydration mode.\n     *\n     * @phpstan-var string|AbstractQuery::HYDRATE_*\n     */\n    protected string|int $hydrationMode = self::HYDRATE_OBJECT;\n\n    protected QueryCacheProfile|null $queryCacheProfile = null;\n\n    /**\n     * Whether or not expire the result cache.\n     */\n    protected bool $expireResultCache = false;\n\n    protected QueryCacheProfile|null $hydrationCacheProfile = null;\n\n    /**\n     * Whether to use second level cache, if available.\n     */\n    protected bool $cacheable = false;\n\n    protected bool $hasCache = false;\n\n    /**\n     * Second level cache region name.\n     */\n    protected string|null $cacheRegion = null;\n\n    /**\n     * Second level query cache mode.\n     *\n     * @phpstan-var Cache::MODE_*|null\n     */\n    protected int|null $cacheMode = null;\n\n    protected CacheLogger|null $cacheLogger = null;\n\n    protected int $lifetime = 0;\n\n    /**\n     * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.\n     */\n    public function __construct(\n        /**\n         * The entity manager used by this query object.\n         */\n        protected EntityManagerInterface $em,\n    ) {\n        $this->parameters = new ArrayCollection();\n        $this->hints      = $em->getConfiguration()->getDefaultQueryHints();\n        $this->hasCache   = $this->em->getConfiguration()->isSecondLevelCacheEnabled();\n\n        if ($this->hasCache) {\n            $this->cacheLogger = $em->getConfiguration()\n                ->getSecondLevelCacheConfiguration()\n                ->getCacheLogger();\n        }\n    }\n\n    /**\n     * Enable/disable second level query (result) caching for this query.\n     *\n     * @return $this\n     */\n    public function setCacheable(bool $cacheable): static\n    {\n        $this->cacheable = $cacheable;\n\n        return $this;\n    }\n\n    /** @return bool TRUE if the query results are enabled for second level cache, FALSE otherwise. */\n    public function isCacheable(): bool\n    {\n        return $this->cacheable;\n    }\n\n    /** @return $this */\n    public function setCacheRegion(string $cacheRegion): static\n    {\n        $this->cacheRegion = $cacheRegion;\n\n        return $this;\n    }\n\n    /**\n     * Obtain the name of the second level query cache region in which query results will be stored\n     *\n     * @return string|null The cache region name; NULL indicates the default region.\n     */\n    public function getCacheRegion(): string|null\n    {\n        return $this->cacheRegion;\n    }\n\n    /** @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise. */\n    protected function isCacheEnabled(): bool\n    {\n        return $this->cacheable && $this->hasCache;\n    }\n\n    public function getLifetime(): int\n    {\n        return $this->lifetime;\n    }\n\n    /**\n     * Sets the life-time for this query into second level cache.\n     *\n     * @return $this\n     */\n    public function setLifetime(int $lifetime): static\n    {\n        $this->lifetime = $lifetime;\n\n        return $this;\n    }\n\n    /** @phpstan-return Cache::MODE_*|null */\n    public function getCacheMode(): int|null\n    {\n        return $this->cacheMode;\n    }\n\n    /**\n     * @phpstan-param Cache::MODE_* $cacheMode\n     *\n     * @return $this\n     */\n    public function setCacheMode(int $cacheMode): static\n    {\n        $this->cacheMode = $cacheMode;\n\n        return $this;\n    }\n\n    /**\n     * Gets the SQL query that corresponds to this query object.\n     * The returned SQL syntax depends on the connection driver that is used\n     * by this query object at the time of this method call.\n     *\n     * @return list<string>|string SQL query\n     */\n    abstract public function getSQL(): string|array;\n\n    /**\n     * Retrieves the associated EntityManager of this Query instance.\n     */\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    /**\n     * Frees the resources used by the query object.\n     *\n     * Resets Parameters, Parameter Types and Query Hints.\n     */\n    public function free(): void\n    {\n        $this->parameters = new ArrayCollection();\n\n        $this->hints = $this->em->getConfiguration()->getDefaultQueryHints();\n    }\n\n    /**\n     * Get all defined parameters.\n     *\n     * @phpstan-return ArrayCollection<int, Parameter>\n     */\n    public function getParameters(): ArrayCollection\n    {\n        return $this->parameters;\n    }\n\n    /**\n     * Gets a query parameter.\n     *\n     * @param int|string $key The key (index or name) of the bound parameter.\n     *\n     * @return Parameter|null The value of the bound parameter, or NULL if not available.\n     */\n    public function getParameter(int|string $key): Parameter|null\n    {\n        $key = Parameter::normalizeName($key);\n\n        $filteredParameters = $this->parameters->filter(\n            static fn (Parameter $parameter): bool => $parameter->getName() === $key,\n        );\n\n        return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;\n    }\n\n    /**\n     * Sets a collection of query parameters.\n     *\n     * @param ArrayCollection|mixed[] $parameters\n     * @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters\n     *\n     * @return $this\n     */\n    public function setParameters(ArrayCollection|array $parameters): static\n    {\n        if (is_array($parameters)) {\n            /** @phpstan-var ArrayCollection<int, Parameter> $parameterCollection */\n            $parameterCollection = new ArrayCollection();\n\n            foreach ($parameters as $key => $value) {\n                $parameterCollection->add(new Parameter($key, $value));\n            }\n\n            $parameters = $parameterCollection;\n        }\n\n        $this->parameters = $parameters;\n\n        return $this;\n    }\n\n    /**\n     * Sets a query parameter.\n     *\n     * @param string|int                                       $key   The parameter position or name.\n     * @param mixed                                            $value The parameter value.\n     * @param ParameterType|ArrayParameterType|string|int|null $type  The parameter type. If specified, the given value\n     *                                                                will be run through the type conversion of this\n     *                                                                type. This is usually not needed for strings and\n     *                                                                numeric types.\n     *\n     * @return $this\n     */\n    public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static\n    {\n        $existingParameter = $this->getParameter($key);\n\n        if ($existingParameter !== null) {\n            $existingParameter->setValue($value, $type);\n\n            return $this;\n        }\n\n        $this->parameters->add(new Parameter($key, $value, $type));\n\n        return $this;\n    }\n\n    /**\n     * Processes an individual parameter value.\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function processParameterValue(mixed $value): mixed\n    {\n        if (is_scalar($value)) {\n            return $value;\n        }\n\n        if ($value instanceof Collection) {\n            $value = iterator_to_array($value);\n        }\n\n        if (is_array($value)) {\n            $value = $this->processArrayParameterValue($value);\n\n            return $value;\n        }\n\n        if ($value instanceof ClassMetadata) {\n            return $value->name;\n        }\n\n        if ($value instanceof BackedEnum) {\n            return $value->value;\n        }\n\n        if (! is_object($value)) {\n            return $value;\n        }\n\n        try {\n            $class = DefaultProxyClassNameResolver::getClass($value);\n            $value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value);\n\n            if ($value === null) {\n                throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);\n            }\n        } catch (MappingException | ORMMappingException) {\n            /* Silence any mapping exceptions. These can occur if the object in\n               question is not a mapped entity, in which case we just don't do\n               any preparation on the value.\n               Depending on MappingDriver, either MappingException or\n               ORMMappingException is thrown. */\n\n            $value = $this->potentiallyProcessIterable($value);\n        }\n\n        return $value;\n    }\n\n    /**\n     * If no mapping is detected, trying to resolve the value as a Traversable\n     */\n    private function potentiallyProcessIterable(mixed $value): mixed\n    {\n        if ($value instanceof Traversable) {\n            $value = iterator_to_array($value);\n            $value = $this->processArrayParameterValue($value);\n        }\n\n        return $value;\n    }\n\n    /**\n     * Process a parameter value which was previously identified as an array\n     *\n     * @param mixed[] $value\n     *\n     * @return mixed[]\n     */\n    private function processArrayParameterValue(array $value): array\n    {\n        foreach ($value as $key => $paramValue) {\n            $paramValue  = $this->processParameterValue($paramValue);\n            $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;\n        }\n\n        return $value;\n    }\n\n    /**\n     * Sets the ResultSetMapping that should be used for hydration.\n     *\n     * @return $this\n     */\n    public function setResultSetMapping(ResultSetMapping $rsm): static\n    {\n        $this->translateNamespaces($rsm);\n        $this->resultSetMapping = $rsm;\n\n        return $this;\n    }\n\n    /**\n     * Gets the ResultSetMapping used for hydration.\n     */\n    protected function getResultSetMapping(): ResultSetMapping|null\n    {\n        return $this->resultSetMapping;\n    }\n\n    /**\n     * Allows to translate entity namespaces to full qualified names.\n     */\n    private function translateNamespaces(ResultSetMapping $rsm): void\n    {\n        $translate = fn ($alias): string => $this->em->getClassMetadata($alias)->getName();\n\n        $rsm->aliasMap         = array_map($translate, $rsm->aliasMap);\n        $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses);\n    }\n\n    /**\n     * Set a cache profile for hydration caching.\n     *\n     * If no result cache driver is set in the QueryCacheProfile, the default\n     * result cache driver is used from the configuration.\n     *\n     * Important: Hydration caching does NOT register entities in the\n     * UnitOfWork when retrieved from the cache. Never use result cached\n     * entities for requests that also flush the EntityManager. If you want\n     * some form of caching with UnitOfWork registration you should use\n     * {@see AbstractQuery::setResultCacheProfile()}.\n     *\n     * @return $this\n     *\n     * @example\n     * $lifetime = 100;\n     * $resultKey = \"abc\";\n     * $query->setHydrationCacheProfile(new QueryCacheProfile());\n     * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));\n     */\n    public function setHydrationCacheProfile(QueryCacheProfile|null $profile): static\n    {\n        if ($profile === null) {\n            $this->hydrationCacheProfile = null;\n\n            return $this;\n        }\n\n        if (! $profile->getResultCache()) {\n            $defaultHydrationCacheImpl = $this->em->getConfiguration()->getHydrationCache();\n            if ($defaultHydrationCacheImpl) {\n                $profile = $profile->setResultCache($defaultHydrationCacheImpl);\n            }\n        }\n\n        $this->hydrationCacheProfile = $profile;\n\n        return $this;\n    }\n\n    public function getHydrationCacheProfile(): QueryCacheProfile|null\n    {\n        return $this->hydrationCacheProfile;\n    }\n\n    /**\n     * Set a cache profile for the result cache.\n     *\n     * If no result cache driver is set in the QueryCacheProfile, the default\n     * result cache driver is used from the configuration.\n     *\n     * @return $this\n     */\n    public function setResultCacheProfile(QueryCacheProfile|null $profile): static\n    {\n        if ($profile === null) {\n            $this->queryCacheProfile = null;\n\n            return $this;\n        }\n\n        if (! $profile->getResultCache()) {\n            $defaultResultCache = $this->em->getConfiguration()->getResultCache();\n            if ($defaultResultCache) {\n                $profile = $profile->setResultCache($defaultResultCache);\n            }\n        }\n\n        $this->queryCacheProfile = $profile;\n\n        return $this;\n    }\n\n    /**\n     * Defines a cache driver to be used for caching result sets and implicitly enables caching.\n     */\n    public function setResultCache(CacheItemPoolInterface|null $resultCache): static\n    {\n        if ($resultCache === null) {\n            if ($this->queryCacheProfile) {\n                $this->queryCacheProfile = new QueryCacheProfile($this->queryCacheProfile->getLifetime(), $this->queryCacheProfile->getCacheKey());\n            }\n\n            return $this;\n        }\n\n        $this->queryCacheProfile = $this->queryCacheProfile\n            ? $this->queryCacheProfile->setResultCache($resultCache)\n            : new QueryCacheProfile(0, null, $resultCache);\n\n        return $this;\n    }\n\n    /**\n     * Enables caching of the results of this query, for given or default amount of seconds\n     * and optionally specifies which ID to use for the cache entry.\n     *\n     * @param int|null    $lifetime      How long the cache entry is valid, in seconds.\n     * @param string|null $resultCacheId ID to use for the cache entry.\n     *\n     * @return $this\n     */\n    public function enableResultCache(int|null $lifetime = null, string|null $resultCacheId = null): static\n    {\n        $this->setResultCacheLifetime($lifetime);\n        $this->setResultCacheId($resultCacheId);\n\n        return $this;\n    }\n\n    /**\n     * Disables caching of the results of this query.\n     *\n     * @return $this\n     */\n    public function disableResultCache(): static\n    {\n        $this->queryCacheProfile = null;\n\n        return $this;\n    }\n\n    /**\n     * Defines how long the result cache will be active before expire.\n     *\n     * @param int|null $lifetime How long the cache entry is valid, in seconds.\n     *\n     * @return $this\n     */\n    public function setResultCacheLifetime(int|null $lifetime): static\n    {\n        $lifetime = (int) $lifetime;\n\n        if ($this->queryCacheProfile) {\n            $this->queryCacheProfile = $this->queryCacheProfile->setLifetime($lifetime);\n\n            return $this;\n        }\n\n        $this->queryCacheProfile = new QueryCacheProfile($lifetime);\n\n        $cache = $this->em->getConfiguration()->getResultCache();\n        if (! $cache) {\n            return $this;\n        }\n\n        $this->queryCacheProfile = $this->queryCacheProfile->setResultCache($cache);\n\n        return $this;\n    }\n\n    /**\n     * Defines if the result cache is active or not.\n     *\n     * @param bool $expire Whether or not to force resultset cache expiration.\n     *\n     * @return $this\n     */\n    public function expireResultCache(bool $expire = true): static\n    {\n        $this->expireResultCache = $expire;\n\n        return $this;\n    }\n\n    /**\n     * Retrieves if the resultset cache is active or not.\n     */\n    public function getExpireResultCache(): bool\n    {\n        return $this->expireResultCache;\n    }\n\n    public function getQueryCacheProfile(): QueryCacheProfile|null\n    {\n        return $this->queryCacheProfile;\n    }\n\n    /**\n     * Change the default fetch mode of an association for this query.\n     *\n     * @param class-string $class\n     * @phpstan-param Mapping\\ClassMetadata::FETCH_EAGER|Mapping\\ClassMetadata::FETCH_LAZY $fetchMode\n     */\n    public function setFetchMode(string $class, string $assocName, int $fetchMode): static\n    {\n        $this->hints['fetchMode'][$class][$assocName] = $fetchMode;\n\n        return $this;\n    }\n\n    /**\n     * Defines the processing mode to be used during hydration / result set transformation.\n     *\n     * @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.\n     *                                  One of the Query::HYDRATE_* constants.\n     * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode\n     *\n     * @return $this\n     */\n    public function setHydrationMode(string|int $hydrationMode): static\n    {\n        $this->hydrationMode = $hydrationMode;\n\n        return $this;\n    }\n\n    /**\n     * Gets the hydration mode currently used by the query.\n     *\n     * @phpstan-return string|AbstractQuery::HYDRATE_*\n     */\n    public function getHydrationMode(): string|int\n    {\n        return $this->hydrationMode;\n    }\n\n    /**\n     * Gets the list of results for the query.\n     *\n     * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).\n     *\n     * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode\n     */\n    public function getResult(string|int $hydrationMode = self::HYDRATE_OBJECT): mixed\n    {\n        return $this->execute(null, $hydrationMode);\n    }\n\n    /**\n     * Gets the array of results for the query.\n     *\n     * Alias for execute(null, HYDRATE_ARRAY).\n     *\n     * @return mixed[]\n     */\n    public function getArrayResult(): array\n    {\n        return $this->execute(null, self::HYDRATE_ARRAY);\n    }\n\n    /**\n     * Gets one-dimensional array of results for the query.\n     *\n     * Alias for execute(null, HYDRATE_SCALAR_COLUMN).\n     *\n     * @return mixed[]\n     */\n    public function getSingleColumnResult(): array\n    {\n        return $this->execute(null, self::HYDRATE_SCALAR_COLUMN);\n    }\n\n    /**\n     * Gets the scalar results for the query.\n     *\n     * Alias for execute(null, HYDRATE_SCALAR).\n     *\n     * @return mixed[]\n     */\n    public function getScalarResult(): array\n    {\n        return $this->execute(null, self::HYDRATE_SCALAR);\n    }\n\n    /**\n     * Get exactly one result or null.\n     *\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode\n     *\n     * @throws NonUniqueResultException\n     */\n    public function getOneOrNullResult(string|int|null $hydrationMode = null): mixed\n    {\n        try {\n            $result = $this->execute(null, $hydrationMode);\n        } catch (NoResultException) {\n            return null;\n        }\n\n        if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {\n            return null;\n        }\n\n        if (! is_array($result)) {\n            return $result;\n        }\n\n        if (count($result) > 1) {\n            throw new NonUniqueResultException();\n        }\n\n        return array_shift($result);\n    }\n\n    /**\n     * Gets the single result of the query.\n     *\n     * Enforces the presence as well as the uniqueness of the result.\n     *\n     * If the result is not unique, a NonUniqueResultException is thrown.\n     * If there is no result, a NoResultException is thrown.\n     *\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode\n     *\n     * @throws NonUniqueResultException If the query result is not unique.\n     * @throws NoResultException        If the query returned no result.\n     */\n    public function getSingleResult(string|int|null $hydrationMode = null): mixed\n    {\n        $result = $this->execute(null, $hydrationMode);\n\n        if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {\n            throw new NoResultException();\n        }\n\n        if (! is_array($result)) {\n            return $result;\n        }\n\n        if (count($result) > 1) {\n            throw new NonUniqueResultException();\n        }\n\n        return array_shift($result);\n    }\n\n    /**\n     * Gets the single scalar result of the query.\n     *\n     * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).\n     *\n     * @return bool|float|int|string|null The scalar result.\n     *\n     * @throws NoResultException        If the query returned no result.\n     * @throws NonUniqueResultException If the query result is not unique.\n     */\n    public function getSingleScalarResult(): mixed\n    {\n        return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);\n    }\n\n    /**\n     * Sets a query hint. If the hint name is not recognized, it is silently ignored.\n     *\n     * @return $this\n     */\n    public function setHint(string $name, mixed $value): static\n    {\n        $this->hints[$name] = $value;\n\n        return $this;\n    }\n\n    /**\n     * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.\n     *\n     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.\n     */\n    public function getHint(string $name): mixed\n    {\n        return $this->hints[$name] ?? false;\n    }\n\n    public function hasHint(string $name): bool\n    {\n        return isset($this->hints[$name]);\n    }\n\n    /**\n     * Return the key value map of query hints that are currently set.\n     *\n     * @return array<string,mixed>\n     */\n    public function getHints(): array\n    {\n        return $this->hints;\n    }\n\n    /**\n     * Executes the query and returns an iterable that can be used to incrementally\n     * iterate over the result.\n     *\n     * @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null    $hydrationMode\n     *\n     * @return iterable<mixed>\n     */\n    public function toIterable(\n        ArrayCollection|array $parameters = [],\n        string|int|null $hydrationMode = null,\n    ): iterable {\n        if ($hydrationMode !== null) {\n            $this->setHydrationMode($hydrationMode);\n        }\n\n        if (count($parameters) !== 0) {\n            $this->setParameters($parameters);\n        }\n\n        $rsm = $this->getResultSetMapping();\n        if ($rsm === null) {\n            throw new LogicException('Uninitialized result set mapping.');\n        }\n\n        $stmt = $this->_doExecute();\n\n        return $this->em->newHydrator($this->hydrationMode)->toIterable($stmt, $rsm, $this->hints);\n    }\n\n    /**\n     * Executes the query.\n     *\n     * @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null         $hydrationMode\n     */\n    public function execute(\n        ArrayCollection|array|null $parameters = null,\n        string|int|null $hydrationMode = null,\n    ): mixed {\n        if ($this->cacheable && $this->isCacheEnabled()) {\n            return $this->executeUsingQueryCache($parameters, $hydrationMode);\n        }\n\n        return $this->executeIgnoreQueryCache($parameters, $hydrationMode);\n    }\n\n    /**\n     * Execute query ignoring second level cache.\n     *\n     * @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null         $hydrationMode\n     */\n    private function executeIgnoreQueryCache(\n        ArrayCollection|array|null $parameters = null,\n        string|int|null $hydrationMode = null,\n    ): mixed {\n        if ($hydrationMode !== null) {\n            $this->setHydrationMode($hydrationMode);\n        }\n\n        if (! empty($parameters)) {\n            $this->setParameters($parameters);\n        }\n\n        $setCacheEntry = static function ($data): void {\n        };\n\n        if ($this->hydrationCacheProfile !== null) {\n            [$cacheKey, $realCacheKey] = $this->getHydrationCacheId();\n\n            $cache     = $this->getHydrationCache();\n            $cacheItem = $cache->getItem($cacheKey);\n            $result    = $cacheItem->isHit() ? $cacheItem->get() : [];\n\n            if (isset($result[$realCacheKey])) {\n                return $result[$realCacheKey];\n            }\n\n            if (! $result) {\n                $result = [];\n            }\n\n            $setCacheEntry = static function ($data) use ($cache, $result, $cacheItem, $realCacheKey): void {\n                $cache->save($cacheItem->set($result + [$realCacheKey => $data]));\n            };\n        }\n\n        $stmt = $this->_doExecute();\n\n        if (is_numeric($stmt)) {\n            $setCacheEntry($stmt);\n\n            return $stmt;\n        }\n\n        $rsm = $this->getResultSetMapping();\n        if ($rsm === null) {\n            throw new LogicException('Uninitialized result set mapping.');\n        }\n\n        $data = $this->em->newHydrator($this->hydrationMode)->hydrateAll($stmt, $rsm, $this->hints);\n\n        $setCacheEntry($data);\n\n        return $data;\n    }\n\n    private function getHydrationCache(): CacheItemPoolInterface\n    {\n        assert($this->hydrationCacheProfile !== null);\n\n        $cache = $this->hydrationCacheProfile->getResultCache();\n        assert($cache !== null);\n\n        return $cache;\n    }\n\n    /**\n     * Load from second level cache or executes the query and put into cache.\n     *\n     * @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters\n     * @phpstan-param string|AbstractQuery::HYDRATE_*|null         $hydrationMode\n     */\n    private function executeUsingQueryCache(\n        ArrayCollection|array|null $parameters = null,\n        string|int|null $hydrationMode = null,\n    ): mixed {\n        $rsm = $this->getResultSetMapping();\n        if ($rsm === null) {\n            throw new LogicException('Uninitialized result set mapping.');\n        }\n\n        $queryCache = $this->em->getCache()->getQueryCache($this->cacheRegion);\n        $queryKey   = new QueryCacheKey(\n            $this->getHash(),\n            $this->lifetime,\n            $this->cacheMode ?: Cache::MODE_NORMAL,\n            $this->getTimestampKey(),\n        );\n\n        $result = $queryCache->get($queryKey, $rsm, $this->hints);\n\n        if ($result !== null) {\n            if ($this->cacheLogger) {\n                $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);\n            }\n\n            return $result;\n        }\n\n        $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);\n        $cached = $queryCache->put($queryKey, $rsm, $result, $this->hints);\n\n        if ($this->cacheLogger) {\n            $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);\n\n            if ($cached) {\n                $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);\n            }\n        }\n\n        return $result;\n    }\n\n    private function getTimestampKey(): TimestampCacheKey|null\n    {\n        assert($this->resultSetMapping !== null);\n        $entityName = reset($this->resultSetMapping->aliasMap);\n\n        if (empty($entityName)) {\n            return null;\n        }\n\n        $metadata = $this->em->getClassMetadata($entityName);\n\n        return new TimestampCacheKey($metadata->rootEntityName);\n    }\n\n    /**\n     * Get the result cache id to use to store the result set cache entry.\n     * Will return the configured id if it exists otherwise a hash will be\n     * automatically generated for you.\n     *\n     * @return string[] ($key, $hash)\n     * @phpstan-return array{string, string} ($key, $hash)\n     */\n    protected function getHydrationCacheId(): array\n    {\n        $parameters = [];\n        $types      = [];\n\n        foreach ($this->getParameters() as $parameter) {\n            $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());\n            $types[$parameter->getName()]      = $parameter->getType();\n        }\n\n        $sql = $this->getSQL();\n        assert(is_string($sql));\n        $queryCacheProfile      = $this->getHydrationCacheProfile();\n        $hints                  = $this->getHints();\n        $hints['hydrationMode'] = $this->getHydrationMode();\n\n        ksort($hints);\n        assert($queryCacheProfile !== null);\n\n        return $queryCacheProfile->generateCacheKeys($sql, $parameters, $types, $hints);\n    }\n\n    /**\n     * Set the result cache id to use to store the result set cache entry.\n     * If this is not explicitly set by the developer then a hash is automatically\n     * generated for you.\n     */\n    public function setResultCacheId(string|null $id): static\n    {\n        if (! $this->queryCacheProfile) {\n            return $this->setResultCacheProfile(new QueryCacheProfile(0, $id));\n        }\n\n        $this->queryCacheProfile = $this->queryCacheProfile->setCacheKey($id);\n\n        return $this;\n    }\n\n    /**\n     * Executes the query and returns the resulting Statement object.\n     *\n     * @return Result|int The executed database statement that holds\n     *                    the results, or an integer indicating how\n     *                    many rows were affected.\n     */\n    abstract protected function _doExecute(): Result|int;\n\n    /**\n     * Cleanup Query resource when clone is called.\n     */\n    public function __clone()\n    {\n        $this->parameters = new ArrayCollection();\n\n        $this->hints = [];\n        $this->hints = $this->em->getConfiguration()->getDefaultQueryHints();\n    }\n\n    /**\n     * Generates a string of currently query to use for the cache second level cache.\n     */\n    protected function getHash(): string\n    {\n        $query = $this->getSQL();\n        assert(is_string($query));\n        $hints  = $this->getHints();\n        $params = array_map(function (Parameter $parameter) {\n            $value = $parameter->getValue();\n\n            // Small optimization\n            // Does not invoke processParameterValue for scalar value\n            if (is_scalar($value)) {\n                return $value;\n            }\n\n            return $this->processParameterValue($value);\n        }, $this->parameters->getValues());\n\n        ksort($hints);\n\n        return sha1($query . '-' . serialize($params) . '-' . serialize($hints));\n    }\n}\n"
  },
  {
    "path": "src/Cache/AssociationCacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nclass AssociationCacheEntry implements CacheEntry\n{\n    /**\n     * @param array<string, mixed> $identifier The entity identifier.\n     * @param class-string         $class      The entity class name\n     */\n    public function __construct(\n        public readonly string $class,\n        public readonly array $identifier,\n    ) {\n    }\n\n    /**\n     * Creates a new AssociationCacheEntry\n     *\n     * This method allow Doctrine\\Common\\Cache\\PhpFileCache compatibility\n     *\n     * @param array<string, mixed> $values array containing property values\n     */\n    public static function __set_state(array $values): self\n    {\n        return new self($values['class'], $values['identifier']);\n    }\n}\n"
  },
  {
    "path": "src/Cache/CacheConfiguration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\n\n/**\n * Configuration container for second-level cache.\n */\nclass CacheConfiguration\n{\n    private CacheFactory|null $cacheFactory          = null;\n    private RegionsConfiguration|null $regionsConfig = null;\n    private CacheLogger|null $cacheLogger            = null;\n    private QueryCacheValidator|null $queryValidator = null;\n\n    public function getCacheFactory(): CacheFactory|null\n    {\n        return $this->cacheFactory;\n    }\n\n    public function setCacheFactory(CacheFactory $factory): void\n    {\n        $this->cacheFactory = $factory;\n    }\n\n    public function getCacheLogger(): CacheLogger|null\n    {\n         return $this->cacheLogger;\n    }\n\n    public function setCacheLogger(CacheLogger $logger): void\n    {\n        $this->cacheLogger = $logger;\n    }\n\n    public function getRegionsConfiguration(): RegionsConfiguration\n    {\n        return $this->regionsConfig ??= new RegionsConfiguration();\n    }\n\n    public function setRegionsConfiguration(RegionsConfiguration $regionsConfig): void\n    {\n        $this->regionsConfig = $regionsConfig;\n    }\n\n    public function getQueryValidator(): QueryCacheValidator\n    {\n        return $this->queryValidator ??= new TimestampQueryCacheValidator(\n            $this->cacheFactory->getTimestampRegion(),\n        );\n    }\n\n    public function setQueryValidator(QueryCacheValidator $validator): void\n    {\n        $this->queryValidator = $validator;\n    }\n}\n"
  },
  {
    "path": "src/Cache/CacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Cache entry interface\n *\n * <b>IMPORTANT NOTE:</b>\n *\n * Fields of classes that implement CacheEntry are public for performance reason.\n */\ninterface CacheEntry\n{\n}\n"
  },
  {
    "path": "src/Cache/CacheException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nuse function sprintf;\n\n/**\n * Exception for cache.\n */\nclass CacheException extends LogicException implements ORMException\n{\n    public static function updateReadOnlyCollection(string $sourceEntity, string $fieldName): self\n    {\n        return new self(sprintf('Cannot update a readonly collection \"%s#%s\"', $sourceEntity, $fieldName));\n    }\n\n    public static function nonCacheableEntity(string $entityName): self\n    {\n        return new self(sprintf('Entity \"%s\" not configured as part of the second-level cache.', $entityName));\n    }\n}\n"
  },
  {
    "path": "src/Cache/CacheFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\n\n/**\n * Contract for building second level cache regions components.\n */\ninterface CacheFactory\n{\n    /**\n     * Build an entity persister for the given entity metadata.\n     */\n    public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister;\n\n    /** Build a collection persister for the given relation mapping. */\n    public function buildCachedCollectionPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        AssociationMapping $mapping,\n    ): CachedCollectionPersister;\n\n    /**\n     * Build a query cache based on the given region name\n     */\n    public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache;\n\n    /**\n     * Build an entity hydrator\n     */\n    public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator;\n\n    /**\n     * Build a collection hydrator\n     */\n    public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator;\n\n    /**\n     * Build a cache region\n     *\n     * @param array<string,mixed> $cache The cache configuration.\n     */\n    public function getRegion(array $cache): Region;\n\n    /**\n     * Build timestamp cache region\n     */\n    public function getTimestampRegion(): TimestampRegion;\n\n    /**\n     * Build \\Doctrine\\ORM\\Cache\n     */\n    public function createCache(EntityManagerInterface $entityManager): Cache;\n}\n"
  },
  {
    "path": "src/Cache/CacheKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Defines entity / collection / query key to be stored in the cache region.\n * Allows multiple roles to be stored in the same cache region.\n */\nabstract class CacheKey\n{\n    public function __construct(public readonly string $hash)\n    {\n    }\n}\n"
  },
  {
    "path": "src/Cache/CollectionCacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nclass CollectionCacheEntry implements CacheEntry\n{\n    /** @param CacheKey[] $identifiers List of entity identifiers hold by the collection */\n    public function __construct(public readonly array $identifiers)\n    {\n    }\n\n    /**\n     * Creates a new CollectionCacheEntry\n     *\n     * This method allows for Doctrine\\Common\\Cache\\PhpFileCache compatibility\n     *\n     * @param array<string, mixed> $values array containing property values\n     */\n    public static function __set_state(array $values): CollectionCacheEntry\n    {\n        return new self($values['identifiers']);\n    }\n}\n"
  },
  {
    "path": "src/Cache/CollectionCacheKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function implode;\nuse function ksort;\nuse function str_replace;\nuse function strtolower;\n\n/**\n * Defines entity collection roles to be stored in the cache region.\n */\nclass CollectionCacheKey extends CacheKey\n{\n    /**\n     * The owner entity identifier\n     *\n     * @var array<string, mixed>\n     */\n    public readonly array $ownerIdentifier;\n\n    /**\n     * @param class-string         $entityClass     The owner entity class.\n     * @param array<string, mixed> $ownerIdentifier The identifier of the owning entity.\n     */\n    public function __construct(\n        public readonly string $entityClass,\n        public readonly string $association,\n        array $ownerIdentifier,\n        string $filterHash = '',\n    ) {\n        ksort($ownerIdentifier);\n\n        $this->ownerIdentifier = $ownerIdentifier;\n\n        $filterHash = $filterHash === '' ? '' : '_' . $filterHash;\n\n        parent::__construct(str_replace('\\\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association . $filterHash);\n    }\n}\n"
  },
  {
    "path": "src/Cache/CollectionHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\n\n/**\n * Hydrator cache entry for collections\n */\ninterface CollectionHydrator\n{\n    /** @param mixed[]|Collection $collection The collection. */\n    public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry;\n\n    /** @return mixed[]|null */\n    public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null;\n}\n"
  },
  {
    "path": "src/Cache/ConcurrentRegion.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Defines contract for concurrently managed data region.\n * It should be able to lock an specific cache entry in an atomic operation.\n *\n * When a entry is locked another process should not be able to read or write the entry.\n * All evict operation should not consider locks, even though an entry is locked evict should be able to delete the entry and its lock.\n */\ninterface ConcurrentRegion extends Region\n{\n    /**\n     * Attempts to read lock the mapping for the given key.\n     *\n     * @param CacheKey $key The key of the item to lock.\n     *\n     * @return Lock|null A lock instance or NULL if the lock already exists.\n     *\n     * @throws LockException Indicates a problem accessing the region.\n     */\n    public function lock(CacheKey $key): Lock|null;\n\n    /**\n     * Attempts to read unlock the mapping for the given key.\n     *\n     * @param CacheKey $key  The key of the item to unlock.\n     * @param Lock     $lock The lock previously obtained from {@link readLock}\n     *\n     * @throws LockException Indicates a problem accessing the region.\n     */\n    public function unlock(CacheKey $key, Lock $lock): bool;\n}\n"
  },
  {
    "path": "src/Cache/DefaultCache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function is_array;\nuse function is_object;\n\n/**\n * Provides an API for querying/managing the second level cache regions.\n */\nclass DefaultCache implements Cache\n{\n    private readonly UnitOfWork $uow;\n    private readonly CacheFactory $cacheFactory;\n\n    /**\n     * @var QueryCache[]\n     * @phpstan-var array<string, QueryCache>\n     */\n    private array $queryCaches = [];\n\n    private QueryCache|null $defaultQueryCache = null;\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->uow          = $em->getUnitOfWork();\n        $this->cacheFactory = $em->getConfiguration()\n            ->getSecondLevelCacheConfiguration()\n            ->getCacheFactory();\n    }\n\n    public function getEntityCacheRegion(string $className): Region|null\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getEntityPersister($metadata->rootEntityName);\n\n        if (! ($persister instanceof CachedPersister)) {\n            return null;\n        }\n\n        return $persister->getCacheRegion();\n    }\n\n    public function getCollectionCacheRegion(string $className, string $association): Region|null\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));\n\n        if (! ($persister instanceof CachedPersister)) {\n            return null;\n        }\n\n        return $persister->getCacheRegion();\n    }\n\n    public function containsEntity(string $className, mixed $identifier): bool\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getEntityPersister($metadata->rootEntityName);\n\n        if (! ($persister instanceof CachedPersister)) {\n            return false;\n        }\n\n        return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier));\n    }\n\n    public function evictEntity(string $className, mixed $identifier): void\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getEntityPersister($metadata->rootEntityName);\n\n        if (! ($persister instanceof CachedPersister)) {\n            return;\n        }\n\n        $persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier));\n    }\n\n    public function evictEntityRegion(string $className): void\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getEntityPersister($metadata->rootEntityName);\n\n        if (! ($persister instanceof CachedPersister)) {\n            return;\n        }\n\n        $persister->getCacheRegion()->evictAll();\n    }\n\n    public function evictEntityRegions(): void\n    {\n        $metadatas = $this->em->getMetadataFactory()->getAllMetadata();\n\n        foreach ($metadatas as $metadata) {\n            $persister = $this->uow->getEntityPersister($metadata->rootEntityName);\n\n            if (! ($persister instanceof CachedPersister)) {\n                continue;\n            }\n\n            $persister->getCacheRegion()->evictAll();\n        }\n    }\n\n    public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));\n\n        if (! ($persister instanceof CachedPersister)) {\n            return false;\n        }\n\n        return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));\n    }\n\n    public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));\n\n        if (! ($persister instanceof CachedPersister)) {\n            return;\n        }\n\n        $persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));\n    }\n\n    public function evictCollectionRegion(string $className, string $association): void\n    {\n        $metadata  = $this->em->getClassMetadata($className);\n        $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));\n\n        if (! ($persister instanceof CachedPersister)) {\n            return;\n        }\n\n        $persister->getCacheRegion()->evictAll();\n    }\n\n    public function evictCollectionRegions(): void\n    {\n        $metadatas = $this->em->getMetadataFactory()->getAllMetadata();\n\n        foreach ($metadatas as $metadata) {\n            foreach ($metadata->associationMappings as $association) {\n                if (! $association->isToMany()) {\n                    continue;\n                }\n\n                $persister = $this->uow->getCollectionPersister($association);\n\n                if (! ($persister instanceof CachedPersister)) {\n                    continue;\n                }\n\n                $persister->getCacheRegion()->evictAll();\n            }\n        }\n    }\n\n    public function containsQuery(string $regionName): bool\n    {\n        return isset($this->queryCaches[$regionName]);\n    }\n\n    public function evictQueryRegion(string|null $regionName = null): void\n    {\n        if ($regionName === null && $this->defaultQueryCache !== null) {\n            $this->defaultQueryCache->clear();\n\n            return;\n        }\n\n        if (isset($this->queryCaches[$regionName])) {\n            $this->queryCaches[$regionName]->clear();\n        }\n    }\n\n    public function evictQueryRegions(): void\n    {\n        $this->getQueryCache()->clear();\n\n        foreach ($this->queryCaches as $queryCache) {\n            $queryCache->clear();\n        }\n    }\n\n    public function getQueryCache(string|null $regionName = null): QueryCache\n    {\n        if ($regionName === null) {\n            return $this->defaultQueryCache ??= $this->cacheFactory->buildQueryCache($this->em);\n        }\n\n        return $this->queryCaches[$regionName] ??= $this->cacheFactory->buildQueryCache($this->em, $regionName);\n    }\n\n    private function buildEntityCacheKey(ClassMetadata $metadata, mixed $identifier): EntityCacheKey\n    {\n        if (! is_array($identifier)) {\n            $identifier = $this->toIdentifierArray($metadata, $identifier);\n        }\n\n        return new EntityCacheKey($metadata->rootEntityName, $identifier);\n    }\n\n    private function buildCollectionCacheKey(\n        ClassMetadata $metadata,\n        string $association,\n        mixed $ownerIdentifier,\n    ): CollectionCacheKey {\n        if (! is_array($ownerIdentifier)) {\n            $ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);\n        }\n\n        return new CollectionCacheKey($metadata->rootEntityName, $association, $ownerIdentifier);\n    }\n\n    /** @return array<string, mixed> */\n    private function toIdentifierArray(ClassMetadata $metadata, mixed $identifier): array\n    {\n        if (is_object($identifier)) {\n            $class = DefaultProxyClassNameResolver::getClass($identifier);\n            if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {\n                $identifier = $this->uow->getSingleIdentifierValue($identifier)\n                    ?? throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);\n            }\n        }\n\n        return [$metadata->identifier[0] => $identifier];\n    }\n}\n"
  },
  {
    "path": "src/Cache/DefaultCacheFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region\\DefaultRegion;\nuse Doctrine\\ORM\\Cache\\Region\\FileLockRegion;\nuse Doctrine\\ORM\\Cache\\Region\\UpdateTimestampCache;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse InvalidArgumentException;\nuse LogicException;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nuse function assert;\nuse function sprintf;\n\nuse const DIRECTORY_SEPARATOR;\n\nclass DefaultCacheFactory implements CacheFactory\n{\n    private TimestampRegion|null $timestampRegion = null;\n\n    /** @var Region[] */\n    private array $regions = [];\n\n    private string|null $fileLockRegionDirectory = null;\n\n    public function __construct(private readonly RegionsConfiguration $regionsConfig, private readonly CacheItemPoolInterface $cacheItemPool)\n    {\n    }\n\n    public function setFileLockRegionDirectory(string $fileLockRegionDirectory): void\n    {\n        $this->fileLockRegionDirectory = $fileLockRegionDirectory;\n    }\n\n    public function getFileLockRegionDirectory(): string|null\n    {\n        return $this->fileLockRegionDirectory;\n    }\n\n    public function setRegion(Region $region): void\n    {\n        $this->regions[$region->getName()] = $region;\n    }\n\n    public function setTimestampRegion(TimestampRegion $region): void\n    {\n        $this->timestampRegion = $region;\n    }\n\n    public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister\n    {\n        assert($metadata->cache !== null);\n        $region = $this->getRegion($metadata->cache);\n        $usage  = $metadata->cache['usage'];\n\n        if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {\n            return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata);\n        }\n\n        if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) {\n            return new NonStrictReadWriteCachedEntityPersister($persister, $region, $em, $metadata);\n        }\n\n        if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {\n            if (! $region instanceof ConcurrentRegion) {\n                throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));\n            }\n\n            return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);\n        }\n\n        throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));\n    }\n\n    public function buildCachedCollectionPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        AssociationMapping $mapping,\n    ): CachedCollectionPersister {\n        assert(isset($mapping->cache));\n        $usage  = $mapping->cache['usage'];\n        $region = $this->getRegion($mapping->cache);\n\n        if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {\n            return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping);\n        }\n\n        if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) {\n            return new NonStrictReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);\n        }\n\n        if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {\n            if (! $region instanceof ConcurrentRegion) {\n                throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));\n            }\n\n            return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);\n        }\n\n        throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));\n    }\n\n    public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache\n    {\n        return new DefaultQueryCache(\n            $em,\n            $this->getRegion(\n                [\n                    'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME,\n                    'usage'  => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE,\n                ],\n            ),\n        );\n    }\n\n    public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator\n    {\n        return new DefaultCollectionHydrator($em);\n    }\n\n    public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator\n    {\n        return new DefaultEntityHydrator($em);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getRegion(array $cache): Region\n    {\n        if (isset($this->regions[$cache['region']])) {\n            return $this->regions[$cache['region']];\n        }\n\n        $name     = $cache['region'];\n        $lifetime = $this->regionsConfig->getLifetime($cache['region']);\n        $region   = new DefaultRegion($name, $this->cacheItemPool, $lifetime);\n\n        if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {\n            if (\n                $this->fileLockRegionDirectory === '' ||\n                $this->fileLockRegionDirectory === null\n            ) {\n                throw new LogicException(\n                    'If you want to use a \"READ_WRITE\" cache an implementation of \"Doctrine\\ORM\\Cache\\ConcurrentRegion\" is required, ' .\n                    'The default implementation provided by doctrine is \"Doctrine\\ORM\\Cache\\Region\\FileLockRegion\" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ',\n                );\n            }\n\n            $directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region'];\n            $region    = new FileLockRegion($region, $directory, (string) $this->regionsConfig->getLockLifetime($cache['region']));\n        }\n\n        return $this->regions[$cache['region']] = $region;\n    }\n\n    public function getTimestampRegion(): TimestampRegion\n    {\n        if ($this->timestampRegion === null) {\n            $name     = Cache::DEFAULT_TIMESTAMP_REGION_NAME;\n            $lifetime = $this->regionsConfig->getLifetime($name);\n\n            $this->timestampRegion = new UpdateTimestampCache($name, $this->cacheItemPool, $lifetime);\n        }\n\n        return $this->timestampRegion;\n    }\n\n    public function createCache(EntityManagerInterface $entityManager): Cache\n    {\n        return new DefaultCache($entityManager);\n    }\n}\n"
  },
  {
    "path": "src/Cache/DefaultCollectionHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function assert;\n\n/**\n * Default hydrator cache for collections\n */\nclass DefaultCollectionHydrator implements CollectionHydrator\n{\n    private readonly UnitOfWork $uow;\n\n    /** @var array<string,mixed> */\n    private static array $hints = [Query::HINT_CACHE_ENABLED => true];\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->uow = $em->getUnitOfWork();\n    }\n\n    public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry\n    {\n        $data = [];\n\n        foreach ($collection as $index => $entity) {\n            $data[$index] = new EntityCacheKey($metadata->rootEntityName, $this->uow->getEntityIdentifier($entity));\n        }\n\n        return new CollectionCacheEntry($data);\n    }\n\n    public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null\n    {\n        $assoc           = $metadata->associationMappings[$key->association];\n        $targetPersister = $this->uow->getEntityPersister($assoc->targetEntity);\n        assert($targetPersister instanceof CachedPersister);\n        $targetRegion = $targetPersister->getCacheRegion();\n        $list         = [];\n\n        /** @var EntityCacheEntry[]|null $entityEntries */\n        $entityEntries = $targetRegion->getMultiple($entry);\n\n        if ($entityEntries === null) {\n            return null;\n        }\n\n        foreach ($entityEntries as $index => $entityEntry) {\n            $entity = $this->uow->createEntity(\n                $entityEntry->class,\n                $entityEntry->resolveAssociationEntries($this->em),\n                self::$hints,\n            );\n\n            $collection->hydrateSet($index, $entity);\n\n            $list[$index] = $entity;\n        }\n\n        $this->uow->hydrationComplete();\n\n        return $list;\n    }\n}\n"
  },
  {
    "path": "src/Cache/DefaultEntityHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\n\nuse function assert;\nuse function is_array;\nuse function is_object;\nuse function reset;\n\n/**\n * Default hydrator cache for entities\n */\nclass DefaultEntityHydrator implements EntityHydrator\n{\n    private readonly UnitOfWork $uow;\n    private readonly IdentifierFlattener $identifierFlattener;\n\n    /** @var array<string,mixed> */\n    private static array $hints = [Query::HINT_CACHE_ENABLED => true];\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->uow                 = $em->getUnitOfWork();\n        $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());\n    }\n\n    public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry\n    {\n        $data = $this->uow->getOriginalEntityData($entity);\n        $data = [...$data, ...$metadata->getIdentifierValues($entity)]; // why update has no identifier values ?\n\n        if ($metadata->requiresFetchAfterChange) {\n            if ($metadata->isVersioned) {\n                assert($metadata->versionField !== null);\n                $data[$metadata->versionField] = $metadata->getFieldValue($entity, $metadata->versionField);\n            }\n\n            foreach ($metadata->fieldMappings as $name => $fieldMapping) {\n                if (isset($fieldMapping->generated)) {\n                    $data[$name] = $metadata->getFieldValue($entity, $name);\n                }\n            }\n        }\n\n        foreach ($metadata->associationMappings as $name => $assoc) {\n            if (! isset($data[$name])) {\n                continue;\n            }\n\n            if (! $assoc->isToOne()) {\n                unset($data[$name]);\n\n                continue;\n            }\n\n            if (! isset($assoc->cache)) {\n                $targetClassMetadata = $this->em->getClassMetadata($assoc->targetEntity);\n                $owningAssociation   = $this->em->getMetadataFactory()->getOwningSide($assoc);\n                $associationIds      = $this->identifierFlattener->flattenIdentifier(\n                    $targetClassMetadata,\n                    $targetClassMetadata->getIdentifierValues($data[$name]),\n                );\n\n                unset($data[$name]);\n\n                foreach ($associationIds as $fieldName => $fieldValue) {\n                    if (isset($targetClassMetadata->fieldMappings[$fieldName])) {\n                        assert($owningAssociation->isToOneOwningSide());\n                        $fieldMapping = $targetClassMetadata->fieldMappings[$fieldName];\n\n                        $data[$owningAssociation->targetToSourceKeyColumns[$fieldMapping->columnName]] = $fieldValue;\n\n                        continue;\n                    }\n\n                    $targetAssoc = $targetClassMetadata->associationMappings[$fieldName];\n\n                    assert($assoc->isToOneOwningSide());\n                    foreach ($assoc->targetToSourceKeyColumns as $referencedColumn => $localColumn) {\n                        if (isset($targetAssoc->sourceToTargetKeyColumns[$referencedColumn])) {\n                            $data[$localColumn] = $fieldValue;\n                        }\n                    }\n                }\n\n                continue;\n            }\n\n            if (! isset($assoc->id)) {\n                $targetClass = DefaultProxyClassNameResolver::getClass($data[$name]);\n                $targetId    = $this->uow->getEntityIdentifier($data[$name]);\n                $data[$name] = new AssociationCacheEntry($targetClass, $targetId);\n\n                continue;\n            }\n\n            // handle association identifier\n            $targetId = is_object($data[$name]) && $this->uow->isInIdentityMap($data[$name])\n                ? $this->uow->getEntityIdentifier($data[$name])\n                : $data[$name];\n\n            // @TODO - fix it !\n            // handle UnitOfWork#createEntity hash generation\n            if (! is_array($targetId)) {\n                assert($assoc->isToOneOwningSide());\n                $data[reset($assoc->joinColumnFieldNames)] = $targetId;\n\n                $targetEntity = $this->em->getClassMetadata($assoc->targetEntity);\n                $targetId     = [$targetEntity->identifier[0] => $targetId];\n            }\n\n            $data[$name] = new AssociationCacheEntry($assoc->targetEntity, $targetId);\n        }\n\n        return new EntityCacheEntry($metadata->name, $data);\n    }\n\n    public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null\n    {\n        $data  = $entry->data;\n        $hints = self::$hints;\n\n        if ($entity !== null) {\n            $hints[Query::HINT_REFRESH]        = true;\n            $hints[Query::HINT_REFRESH_ENTITY] = $entity;\n        }\n\n        foreach ($metadata->associationMappings as $name => $assoc) {\n            if (! isset($assoc->cache) || ! isset($data[$name])) {\n                continue;\n            }\n\n            $assocClass  = $data[$name]->class;\n            $assocId     = $data[$name]->identifier;\n            $isEagerLoad = ($assoc->fetch === ClassMetadata::FETCH_EAGER || ($assoc->isOneToOne() && ! $assoc->isOwningSide()));\n\n            if (! $isEagerLoad) {\n                $data[$name] = $this->em->getReference($assocClass, $assocId);\n\n                continue;\n            }\n\n            $assocMetadata  = $this->em->getClassMetadata($assoc->targetEntity);\n            $assocKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocId);\n            $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity);\n            $assocRegion    = $assocPersister->getCacheRegion();\n            $assocEntry     = $assocRegion->get($assocKey);\n\n            if ($assocEntry === null) {\n                return null;\n            }\n\n            $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), $hints);\n        }\n\n        if ($entity !== null) {\n            $this->uow->registerManaged($entity, $key->identifier, $data);\n        }\n\n        $result = $this->uow->createEntity($entry->class, $data, $hints);\n\n        $this->uow->hydrationComplete();\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "src/Cache/DefaultQueryCache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\Exception\\FeatureNotImplemented;\nuse Doctrine\\ORM\\Cache\\Exception\\NonCacheableEntity;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function array_map;\nuse function array_shift;\nuse function array_unshift;\nuse function assert;\nuse function count;\nuse function is_array;\nuse function key;\nuse function reset;\n\n/**\n * Default query cache implementation.\n */\nclass DefaultQueryCache implements QueryCache\n{\n    private readonly UnitOfWork $uow;\n    private readonly QueryCacheValidator $validator;\n    protected CacheLogger|null $cacheLogger = null;\n\n    /** @var array<string,mixed> */\n    private static array $hints = [Query::HINT_CACHE_ENABLED => true];\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        private readonly Region $region,\n    ) {\n        $cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration();\n\n        $this->uow         = $em->getUnitOfWork();\n        $this->cacheLogger = $cacheConfig->getCacheLogger();\n        $this->validator   = $cacheConfig->getQueryValidator();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null\n    {\n        if (! ($key->cacheMode & Cache::MODE_GET)) {\n            return null;\n        }\n\n        $cacheEntry = $this->region->get($key);\n\n        if (! $cacheEntry instanceof QueryCacheEntry) {\n            return null;\n        }\n\n        if (! $this->validator->isValid($key, $cacheEntry)) {\n            $this->region->evict($key);\n\n            return null;\n        }\n\n        $result      = [];\n        $entityName  = reset($rsm->aliasMap);\n        $hasRelation = ! empty($rsm->relationMap);\n        $persister   = $this->uow->getEntityPersister($entityName);\n        assert($persister instanceof CachedEntityPersister);\n\n        $region     = $persister->getCacheRegion();\n        $regionName = $region->getName();\n\n        $cm = $this->em->getClassMetadata($entityName);\n\n        $generateKeys = static fn (array $entry): EntityCacheKey => new EntityCacheKey($cm->rootEntityName, $entry['identifier']);\n\n        $cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $cacheEntry->result));\n        $entries   = $region->getMultiple($cacheKeys) ?? [];\n\n        // @TODO - move to cache hydration component\n        foreach ($cacheEntry->result as $index => $entry) {\n            $entityEntry = $entries[$index] ?? null;\n\n            if (! $entityEntry instanceof EntityCacheEntry) {\n                $this->cacheLogger?->entityCacheMiss($regionName, $cacheKeys->identifiers[$index]);\n\n                return null;\n            }\n\n            $this->cacheLogger?->entityCacheHit($regionName, $cacheKeys->identifiers[$index]);\n\n            if (! $hasRelation) {\n                $result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);\n\n                continue;\n            }\n\n            $data = $entityEntry->data;\n\n            foreach ($entry['associations'] as $name => $assoc) {\n                $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']);\n                assert($assocPersister instanceof CachedEntityPersister);\n\n                $assocRegion   = $assocPersister->getCacheRegion();\n                $assocMetadata = $this->em->getClassMetadata($assoc['targetEntity']);\n\n                if ($assoc['type'] & ClassMetadata::TO_ONE) {\n                    $assocKey   = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']);\n                    $assocEntry = $assocRegion->get($assocKey);\n\n                    if ($assocEntry === null) {\n                        $this->cacheLogger?->entityCacheMiss($assocRegion->getName(), $assocKey);\n\n                        $this->uow->hydrationComplete();\n\n                        return null;\n                    }\n\n                    $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints);\n\n                    $this->cacheLogger?->entityCacheHit($assocRegion->getName(), $assocKey);\n\n                    continue;\n                }\n\n                if (! isset($assoc['list']) || empty($assoc['list'])) {\n                    continue;\n                }\n\n                $generateKeys = static fn (array $id): EntityCacheKey => new EntityCacheKey($assocMetadata->rootEntityName, $id);\n\n                $collection   = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());\n                $assocKeys    = new CollectionCacheEntry(array_map($generateKeys, $assoc['list']));\n                $assocEntries = $assocRegion->getMultiple($assocKeys);\n\n                foreach ($assoc['list'] as $assocIndex => $assocId) {\n                    $assocEntry = is_array($assocEntries) ? ($assocEntries[$assocIndex] ?? null) : null;\n\n                    if ($assocEntry === null) {\n                        $this->cacheLogger?->entityCacheMiss($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);\n\n                        $this->uow->hydrationComplete();\n\n                        return null;\n                    }\n\n                    $element = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints);\n\n                    $collection->hydrateSet($assocIndex, $element);\n\n                    $this->cacheLogger?->entityCacheHit($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);\n                }\n\n                $data[$name] = $collection;\n\n                $collection->setInitialized(true);\n            }\n\n            foreach ($data as $fieldName => $unCachedAssociationData) {\n                // In some scenarios, such as EAGER+ASSOCIATION+ID+CACHE, the\n                // cache key information in `$cacheEntry` will not contain details\n                // for fields that are associations.\n                //\n                // This means that `$data` keys for some associations that may\n                // actually not be cached will not be converted to actual association\n                // data, yet they contain L2 cache AssociationCacheEntry objects.\n                //\n                // We need to unwrap those associations into proxy references,\n                // since we don't have actual data for them except for identifiers.\n                if ($unCachedAssociationData instanceof AssociationCacheEntry) {\n                    $data[$fieldName] = $this->em->getReference(\n                        $unCachedAssociationData->class,\n                        $unCachedAssociationData->identifier,\n                    );\n                }\n            }\n\n            $result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints);\n        }\n\n        $this->uow->hydrationComplete();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool\n    {\n        if ($rsm->scalarMappings) {\n            throw FeatureNotImplemented::scalarResults();\n        }\n\n        if (count($rsm->entityMappings) > 1) {\n            throw FeatureNotImplemented::multipleRootEntities();\n        }\n\n        if (! $rsm->isSelect) {\n            throw FeatureNotImplemented::nonSelectStatements();\n        }\n\n        if (($hints[SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) {\n            throw FeatureNotImplemented::partialEntities();\n        }\n\n        if (! ($key->cacheMode & Cache::MODE_PUT)) {\n            return false;\n        }\n\n        $data       = [];\n        $entityName = reset($rsm->aliasMap);\n        $rootAlias  = key($rsm->aliasMap);\n        $persister  = $this->uow->getEntityPersister($entityName);\n\n        if (! $persister instanceof CachedEntityPersister) {\n            throw NonCacheableEntity::fromEntity($entityName);\n        }\n\n        $region = $persister->getCacheRegion();\n\n        $cm = $this->em->getClassMetadata($entityName);\n\n        foreach ($result as $index => $entity) {\n            $identifier = $this->uow->getEntityIdentifier($entity);\n            $entityKey  = new EntityCacheKey($cm->rootEntityName, $identifier);\n\n            if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey)) {\n                // Cancel put result if entity put fail\n                if (! $persister->storeEntityCache($entity, $entityKey)) {\n                    return false;\n                }\n            }\n\n            $data[$index]['identifier']   = $identifier;\n            $data[$index]['associations'] = [];\n\n            // @TODO - move to cache hydration components\n            foreach ($rsm->relationMap as $alias => $name) {\n                $parentAlias = $rsm->parentAliasMap[$alias];\n                $parentClass = $rsm->aliasMap[$parentAlias];\n                $metadata    = $this->em->getClassMetadata($parentClass);\n                $assoc       = $metadata->associationMappings[$name];\n                $assocValue  = $this->getAssociationValue($rsm, $alias, $entity);\n\n                if ($assocValue === null) {\n                    continue;\n                }\n\n                // root entity association\n                if ($rootAlias === $parentAlias) {\n                    // Cancel put result if association put fail\n                    $assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue);\n                    if ($assocInfo === null) {\n                        return false;\n                    }\n\n                    $data[$index]['associations'][$name] = $assocInfo;\n\n                    continue;\n                }\n\n                // store single nested association\n                if (! is_array($assocValue)) {\n                    // Cancel put result if association put fail\n                    if ($this->storeAssociationCache($key, $assoc, $assocValue) === null) {\n                        return false;\n                    }\n\n                    continue;\n                }\n\n                // store array of nested association\n                foreach ($assocValue as $aVal) {\n                    // Cancel put result if association put fail\n                    if ($this->storeAssociationCache($key, $assoc, $aVal) === null) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        return $this->region->put($key, new QueryCacheEntry($data));\n    }\n\n    /**\n     * @return mixed[]|null\n     * @phpstan-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null\n     */\n    private function storeAssociationCache(QueryCacheKey $key, AssociationMapping $assoc, mixed $assocValue): array|null\n    {\n        $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity);\n        $assocMetadata  = $assocPersister->getClassMetadata();\n        $assocRegion    = $assocPersister->getCacheRegion();\n\n        // Handle *-to-one associations\n        if ($assoc->isToOne()) {\n            $assocIdentifier = $this->uow->getEntityIdentifier($assocValue);\n            $entityKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);\n\n            if (! $this->uow->isUninitializedObject($assocValue) && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {\n                // Entity put fail\n                if (! $assocPersister->storeEntityCache($assocValue, $entityKey)) {\n                    return null;\n                }\n            }\n\n            return [\n                'targetEntity'  => $assocMetadata->rootEntityName,\n                'identifier'    => $assocIdentifier,\n                'type'          => $assoc->type(),\n            ];\n        }\n\n        // Handle *-to-many associations\n        $list = [];\n\n        foreach ($assocValue as $assocItemIndex => $assocItem) {\n            $assocIdentifier = $this->uow->getEntityIdentifier($assocItem);\n            $entityKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);\n\n            if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {\n                // Entity put fail\n                if (! $assocPersister->storeEntityCache($assocItem, $entityKey)) {\n                    return null;\n                }\n            }\n\n            $list[$assocItemIndex] = $assocIdentifier;\n        }\n\n        return [\n            'targetEntity'  => $assocMetadata->rootEntityName,\n            'type'          => $assoc->type(),\n            'list'          => $list,\n        ];\n    }\n\n    /** @phpstan-return list<mixed>|object|null */\n    private function getAssociationValue(\n        ResultSetMapping $rsm,\n        string $assocAlias,\n        object $entity,\n    ): array|object|null {\n        $path  = [];\n        $alias = $assocAlias;\n\n        while (isset($rsm->parentAliasMap[$alias])) {\n            $parent = $rsm->parentAliasMap[$alias];\n            $field  = $rsm->relationMap[$alias];\n            $class  = $rsm->aliasMap[$parent];\n\n            array_unshift($path, [\n                'field'  => $field,\n                'class'  => $class,\n            ]);\n\n            $alias = $parent;\n        }\n\n        return $this->getAssociationPathValue($entity, $path);\n    }\n\n    /**\n     * @phpstan-param array<array-key, array{field: string, class: string}> $path\n     *\n     * @phpstan-return list<mixed>|object|null\n     */\n    private function getAssociationPathValue(mixed $value, array $path): array|object|null\n    {\n        $mapping  = array_shift($path);\n        $metadata = $this->em->getClassMetadata($mapping['class']);\n        $assoc    = $metadata->associationMappings[$mapping['field']];\n        $value    = $metadata->getFieldValue($value, $mapping['field']);\n\n        if ($value === null) {\n            return null;\n        }\n\n        if ($path === []) {\n            return $value;\n        }\n\n        // Handle *-to-one associations\n        if ($assoc->isToOne()) {\n            return $this->getAssociationPathValue($value, $path);\n        }\n\n        $values = [];\n\n        foreach ($value as $item) {\n            $values[] = $this->getAssociationPathValue($item, $path);\n        }\n\n        return $values;\n    }\n\n    public function clear(): bool\n    {\n        return $this->region->evictAll();\n    }\n\n    public function getRegion(): Region\n    {\n        return $this->region;\n    }\n}\n"
  },
  {
    "path": "src/Cache/EntityCacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\nuse function array_map;\n\nclass EntityCacheEntry implements CacheEntry\n{\n    /**\n     * @param class-string        $class The entity class name\n     * @param array<string,mixed> $data  The entity map data\n     */\n    public function __construct(\n        public readonly string $class,\n        public readonly array $data,\n    ) {\n    }\n\n    /**\n     * Creates a new EntityCacheEntry\n     *\n     * This method allows Doctrine\\Common\\Cache\\PhpFileCache compatibility\n     *\n     * @param array<string,mixed> $values array containing property values\n     */\n    public static function __set_state(array $values): self\n    {\n        return new self($values['class'], $values['data']);\n    }\n\n    /**\n     * Retrieves the entity data resolving cache entries\n     *\n     * @return array<string, mixed>\n     */\n    public function resolveAssociationEntries(EntityManagerInterface $em): array\n    {\n        return array_map(static function ($value) use ($em) {\n            if (! ($value instanceof AssociationCacheEntry)) {\n                return $value;\n            }\n\n            return $em->getReference($value->class, $value->identifier);\n        }, $this->data);\n    }\n}\n"
  },
  {
    "path": "src/Cache/EntityCacheKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function implode;\nuse function ksort;\nuse function str_replace;\nuse function strtolower;\n\n/**\n * Defines entity classes roles to be stored in the cache region.\n */\nclass EntityCacheKey extends CacheKey\n{\n    /**\n     * The entity identifier\n     *\n     * @var array<string, mixed>\n     */\n    public readonly array $identifier;\n\n    /**\n     * @param class-string         $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.\n     * @param array<string, mixed> $identifier  The entity identifier\n     */\n    public function __construct(\n        public readonly string $entityClass,\n        array $identifier,\n    ) {\n        ksort($identifier);\n\n        $this->identifier = $identifier;\n\n        parent::__construct(str_replace('\\\\', '.', strtolower($entityClass) . '_' . implode(' ', $identifier)));\n    }\n}\n"
  },
  {
    "path": "src/Cache/EntityHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * Hydrator cache entry for entities\n */\ninterface EntityHydrator\n{\n    /**\n     * @param ClassMetadata  $metadata The entity metadata.\n     * @param EntityCacheKey $key      The entity cache key.\n     * @param object         $entity   The entity.\n     */\n    public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry;\n\n    /**\n     * @param ClassMetadata    $metadata The entity metadata.\n     * @param EntityCacheKey   $key      The entity cache key.\n     * @param EntityCacheEntry $entry    The entity cache entry.\n     * @param object|null      $entity   The entity to load the cache into. If not specified, a new entity is created.\n     */\n    public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null;\n}\n"
  },
  {
    "path": "src/Cache/Exception/CacheException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nuse Doctrine\\ORM\\Cache\\CacheException as BaseCacheException;\n\n/**\n * Exception for cache.\n */\nclass CacheException extends BaseCacheException\n{\n}\n"
  },
  {
    "path": "src/Cache/Exception/CannotUpdateReadOnlyCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nuse function sprintf;\n\nclass CannotUpdateReadOnlyCollection extends CacheException\n{\n    public static function fromEntityAndField(string $sourceEntity, string $fieldName): self\n    {\n        return new self(sprintf(\n            'Cannot update a readonly collection \"%s#%s\"',\n            $sourceEntity,\n            $fieldName,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Exception/CannotUpdateReadOnlyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nuse function sprintf;\n\nclass CannotUpdateReadOnlyEntity extends CacheException\n{\n    public static function fromEntity(string $entityName): self\n    {\n        return new self(sprintf('Cannot update a readonly entity \"%s\"', $entityName));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Exception/FeatureNotImplemented.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nclass FeatureNotImplemented extends CacheException\n{\n    public static function scalarResults(): self\n    {\n        return new self('Second level cache does not support scalar results.');\n    }\n\n    public static function multipleRootEntities(): self\n    {\n        return new self('Second level cache does not support multiple root entities.');\n    }\n\n    public static function nonSelectStatements(): self\n    {\n        return new self('Second-level cache query supports only select statements.');\n    }\n\n    public static function partialEntities(): self\n    {\n        return new self('Second level cache does not support partial entities.');\n    }\n}\n"
  },
  {
    "path": "src/Cache/Exception/NonCacheableEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nuse function sprintf;\n\nclass NonCacheableEntity extends CacheException\n{\n    public static function fromEntity(string $entityName): self\n    {\n        return new self(sprintf(\n            'Entity \"%s\" not configured as part of the second-level cache.',\n            $entityName,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Exception/NonCacheableEntityAssociation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Exception;\n\nuse function sprintf;\n\nclass NonCacheableEntityAssociation extends CacheException\n{\n    public static function fromEntityAndField(string $entityName, string $field): self\n    {\n        return new self(sprintf(\n            'Entity association field \"%s#%s\" not configured as part of the second-level cache.',\n            $entityName,\n            $field,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Lock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function time;\nuse function uniqid;\n\nclass Lock\n{\n    public int $time;\n\n    public function __construct(\n        public string $value,\n        int|null $time = null,\n    ) {\n        $this->time = $time ?? time();\n    }\n\n    public static function createLockRead(): Lock\n    {\n        return new self(uniqid((string) time(), true));\n    }\n}\n"
  },
  {
    "path": "src/Cache/LockException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\n\n/**\n * Lock exception for cache.\n */\nclass LockException extends CacheException\n{\n}\n"
  },
  {
    "path": "src/Cache/Logging/CacheLogger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Logging;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\n\n/**\n * Interface for logging.\n */\ninterface CacheLogger\n{\n    /**\n     * Log an entity put into second level cache.\n     */\n    public function entityCachePut(string $regionName, EntityCacheKey $key): void;\n\n    /**\n     * Log an entity get from second level cache resulted in a hit.\n     */\n    public function entityCacheHit(string $regionName, EntityCacheKey $key): void;\n\n    /**\n     * Log an entity get from second level cache resulted in a miss.\n     */\n    public function entityCacheMiss(string $regionName, EntityCacheKey $key): void;\n\n    /**\n     * Log an entity put into second level cache.\n     */\n    public function collectionCachePut(string $regionName, CollectionCacheKey $key): void;\n\n    /**\n     * Log an entity get from second level cache resulted in a hit.\n     */\n    public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void;\n\n    /**\n     * Log an entity get from second level cache resulted in a miss.\n     */\n    public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void;\n\n    /**\n     * Log a query put into the query cache.\n     */\n    public function queryCachePut(string $regionName, QueryCacheKey $key): void;\n\n    /**\n     * Log a query get from the query cache resulted in a hit.\n     */\n    public function queryCacheHit(string $regionName, QueryCacheKey $key): void;\n\n    /**\n     * Log a query get from the query cache resulted in a miss.\n     */\n    public function queryCacheMiss(string $regionName, QueryCacheKey $key): void;\n}\n"
  },
  {
    "path": "src/Cache/Logging/CacheLoggerChain.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Logging;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\n\nclass CacheLoggerChain implements CacheLogger\n{\n    /** @var array<string, CacheLogger> */\n    private array $loggers = [];\n\n    public function setLogger(string $name, CacheLogger $logger): void\n    {\n        $this->loggers[$name] = $logger;\n    }\n\n    public function getLogger(string $name): CacheLogger|null\n    {\n        return $this->loggers[$name] ?? null;\n    }\n\n    /** @return array<string, CacheLogger> */\n    public function getLoggers(): array\n    {\n        return $this->loggers;\n    }\n\n    public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->collectionCacheHit($regionName, $key);\n        }\n    }\n\n    public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->collectionCacheMiss($regionName, $key);\n        }\n    }\n\n    public function collectionCachePut(string $regionName, CollectionCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->collectionCachePut($regionName, $key);\n        }\n    }\n\n    public function entityCacheHit(string $regionName, EntityCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->entityCacheHit($regionName, $key);\n        }\n    }\n\n    public function entityCacheMiss(string $regionName, EntityCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->entityCacheMiss($regionName, $key);\n        }\n    }\n\n    public function entityCachePut(string $regionName, EntityCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->entityCachePut($regionName, $key);\n        }\n    }\n\n    public function queryCacheHit(string $regionName, QueryCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->queryCacheHit($regionName, $key);\n        }\n    }\n\n    public function queryCacheMiss(string $regionName, QueryCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->queryCacheMiss($regionName, $key);\n        }\n    }\n\n    public function queryCachePut(string $regionName, QueryCacheKey $key): void\n    {\n        foreach ($this->loggers as $logger) {\n            $logger->queryCachePut($regionName, $key);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Cache/Logging/StatisticsCacheLogger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Logging;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\n\nuse function array_sum;\n\n/**\n * Provide basic second level cache statistics.\n */\nclass StatisticsCacheLogger implements CacheLogger\n{\n    /** @var array<string, int> */\n    private array $cacheMissCountMap = [];\n\n    /** @var array<string, int> */\n    private array $cacheHitCountMap = [];\n\n    /** @var array<string, int> */\n    private array $cachePutCountMap = [];\n\n    public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void\n    {\n        $this->cacheMissCountMap[$regionName]\n            = ($this->cacheMissCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void\n    {\n        $this->cacheHitCountMap[$regionName]\n            = ($this->cacheHitCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function collectionCachePut(string $regionName, CollectionCacheKey $key): void\n    {\n        $this->cachePutCountMap[$regionName]\n            = ($this->cachePutCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function entityCacheMiss(string $regionName, EntityCacheKey $key): void\n    {\n        $this->cacheMissCountMap[$regionName]\n            = ($this->cacheMissCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function entityCacheHit(string $regionName, EntityCacheKey $key): void\n    {\n        $this->cacheHitCountMap[$regionName]\n            = ($this->cacheHitCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function entityCachePut(string $regionName, EntityCacheKey $key): void\n    {\n        $this->cachePutCountMap[$regionName]\n            = ($this->cachePutCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function queryCacheHit(string $regionName, QueryCacheKey $key): void\n    {\n        $this->cacheHitCountMap[$regionName]\n            = ($this->cacheHitCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function queryCacheMiss(string $regionName, QueryCacheKey $key): void\n    {\n        $this->cacheMissCountMap[$regionName]\n            = ($this->cacheMissCountMap[$regionName] ?? 0) + 1;\n    }\n\n    public function queryCachePut(string $regionName, QueryCacheKey $key): void\n    {\n        $this->cachePutCountMap[$regionName]\n            = ($this->cachePutCountMap[$regionName] ?? 0) + 1;\n    }\n\n    /**\n     * Get the number of entries successfully retrieved from cache.\n     *\n     * @param string $regionName The name of the cache region.\n     */\n    public function getRegionHitCount(string $regionName): int\n    {\n        return $this->cacheHitCountMap[$regionName] ?? 0;\n    }\n\n    /**\n     * Get the number of cached entries *not* found in cache.\n     *\n     * @param string $regionName The name of the cache region.\n     */\n    public function getRegionMissCount(string $regionName): int\n    {\n        return $this->cacheMissCountMap[$regionName] ?? 0;\n    }\n\n    /**\n     * Get the number of cacheable entries put in cache.\n     *\n     * @param string $regionName The name of the cache region.\n     */\n    public function getRegionPutCount(string $regionName): int\n    {\n        return $this->cachePutCountMap[$regionName] ?? 0;\n    }\n\n    /** @return array<string, int> */\n    public function getRegionsMiss(): array\n    {\n        return $this->cacheMissCountMap;\n    }\n\n    /** @return array<string, int> */\n    public function getRegionsHit(): array\n    {\n        return $this->cacheHitCountMap;\n    }\n\n    /** @return array<string, int> */\n    public function getRegionsPut(): array\n    {\n        return $this->cachePutCountMap;\n    }\n\n    /**\n     * Clear region statistics\n     *\n     * @param string $regionName The name of the cache region.\n     */\n    public function clearRegionStats(string $regionName): void\n    {\n        $this->cachePutCountMap[$regionName]  = 0;\n        $this->cacheHitCountMap[$regionName]  = 0;\n        $this->cacheMissCountMap[$regionName] = 0;\n    }\n\n    /**\n     * Clear all statistics\n     */\n    public function clearStats(): void\n    {\n        $this->cachePutCountMap  = [];\n        $this->cacheHitCountMap  = [];\n        $this->cacheMissCountMap = [];\n    }\n\n    /**\n     * Get the total number of put in cache.\n     */\n    public function getPutCount(): int\n    {\n        return array_sum($this->cachePutCountMap);\n    }\n\n    /**\n     * Get the total number of entries successfully retrieved from cache.\n     */\n    public function getHitCount(): int\n    {\n        return array_sum($this->cacheHitCountMap);\n    }\n\n    /**\n     * Get the total number of cached entries *not* found in cache.\n     */\n    public function getMissCount(): int\n    {\n        return array_sum($this->cacheMissCountMap);\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/CachedPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister;\n\nuse Doctrine\\ORM\\Cache\\Region;\n\n/**\n * Interface for persister that support second level cache.\n */\ninterface CachedPersister\n{\n    /**\n     * Perform whatever processing is encapsulated here after completion of the transaction.\n     */\n    public function afterTransactionComplete(): void;\n\n    /**\n     * Perform whatever processing is encapsulated here after completion of the rolled-back.\n     */\n    public function afterTransactionRolledBack(): void;\n\n    public function getCacheRegion(): Region;\n}\n"
  },
  {
    "path": "src/Cache/Persister/Collection/AbstractCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionHydrator;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function array_values;\nuse function assert;\nuse function count;\n\nabstract class AbstractCollectionPersister implements CachedCollectionPersister\n{\n    protected UnitOfWork $uow;\n    protected ClassMetadataFactory $metadataFactory;\n    protected ClassMetadata $sourceEntity;\n    protected ClassMetadata $targetEntity;\n\n    /** @var mixed[] */\n    protected array $queuedCache = [];\n\n    protected string $regionName;\n    protected FilterCollection $filters;\n    protected CollectionHydrator $hydrator;\n    protected CacheLogger|null $cacheLogger;\n\n    public function __construct(\n        protected CollectionPersister $persister,\n        protected Region $region,\n        EntityManagerInterface $em,\n        protected AssociationMapping $association,\n    ) {\n        $configuration = $em->getConfiguration();\n        $cacheConfig   = $configuration->getSecondLevelCacheConfiguration();\n        $cacheFactory  = $cacheConfig->getCacheFactory();\n\n        $this->region          = $region;\n        $this->persister       = $persister;\n        $this->association     = $association;\n        $this->filters         = $em->getFilters();\n        $this->regionName      = $region->getName();\n        $this->uow             = $em->getUnitOfWork();\n        $this->metadataFactory = $em->getMetadataFactory();\n        $this->cacheLogger     = $cacheConfig->getCacheLogger();\n        $this->hydrator        = $cacheFactory->buildCollectionHydrator($em, $association);\n        $this->sourceEntity    = $em->getClassMetadata($association->sourceEntity);\n        $this->targetEntity    = $em->getClassMetadata($association->targetEntity);\n    }\n\n    public function getCacheRegion(): Region\n    {\n        return $this->region;\n    }\n\n    public function getSourceEntityMetadata(): ClassMetadata\n    {\n        return $this->sourceEntity;\n    }\n\n    public function getTargetEntityMetadata(): ClassMetadata\n    {\n        return $this->targetEntity;\n    }\n\n    public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null\n    {\n        $cache = $this->region->get($key);\n\n        if ($cache === null) {\n            return null;\n        }\n\n        return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);\n    }\n\n    public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void\n    {\n        $associationMapping = $this->sourceEntity->associationMappings[$key->association];\n        $targetPersister    = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);\n        assert($targetPersister instanceof CachedEntityPersister);\n        $targetRegion   = $targetPersister->getCacheRegion();\n        $targetHydrator = $targetPersister->getEntityHydrator();\n\n        // Only preserve ordering if association configured it\n        if (! $associationMapping->isIndexed()) {\n            // Elements may be an array or a Collection\n            $elements = array_values($elements instanceof Collection ? $elements->getValues() : $elements);\n        }\n\n        $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);\n\n        foreach ($entry->identifiers as $index => $entityKey) {\n            if ($targetRegion->contains($entityKey)) {\n                continue;\n            }\n\n            $class     = $this->targetEntity;\n            $className = DefaultProxyClassNameResolver::getClass($elements[$index]);\n\n            if ($className !== $this->targetEntity->name) {\n                $class = $this->metadataFactory->getMetadataFor($className);\n            }\n\n            $entity      = $elements[$index];\n            $entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);\n\n            $targetRegion->put($entityKey, $entityEntry);\n        }\n\n        if ($this->region->put($key, $entry)) {\n            $this->cacheLogger?->collectionCachePut($this->regionName, $key);\n        }\n    }\n\n    public function contains(PersistentCollection $collection, object $element): bool\n    {\n        return $this->persister->contains($collection, $element);\n    }\n\n    public function containsKey(PersistentCollection $collection, mixed $key): bool\n    {\n        return $this->persister->containsKey($collection, $key);\n    }\n\n    public function count(PersistentCollection $collection): int\n    {\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());\n        $entry   = $this->region->get($key);\n\n        if ($entry !== null) {\n            return count($entry->identifiers);\n        }\n\n        return $this->persister->count($collection);\n    }\n\n    public function get(PersistentCollection $collection, mixed $index): mixed\n    {\n        return $this->persister->get($collection, $index);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array\n    {\n        return $this->persister->slice($collection, $offset, $length);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array\n    {\n        return $this->persister->loadCriteria($collection, $criteria);\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Collection/CachedCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\n\n/**\n * Interface for second level cache collection persisters.\n */\ninterface CachedCollectionPersister extends CachedPersister, CollectionPersister\n{\n    public function getSourceEntityMetadata(): ClassMetadata;\n\n    public function getTargetEntityMetadata(): ClassMetadata;\n\n    /**\n     * Loads a collection from cache\n     *\n     * @return mixed[]|null\n     */\n    public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null;\n\n    /**\n     * Stores a collection into cache\n     *\n     * @param mixed[]|Collection $elements\n     */\n    public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void;\n}\n"
  },
  {
    "path": "src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\PersistentCollection;\n\nuse function spl_object_id;\n\nclass NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister\n{\n    public function afterTransactionComplete(): void\n    {\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $item) {\n                $this->storeCollectionCache($item['key'], $item['list']);\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $key) {\n                $this->region->evict($key);\n            }\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function afterTransactionRolledBack(): void\n    {\n        $this->queuedCache = [];\n    }\n\n    public function delete(PersistentCollection $collection): void\n    {\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());\n\n        $this->persister->delete($collection);\n\n        $this->queuedCache['delete'][spl_object_id($collection)] = $key;\n    }\n\n    public function update(PersistentCollection $collection): void\n    {\n        $isInitialized = $collection->isInitialized();\n        $isDirty       = $collection->isDirty();\n\n        if (! $isInitialized && ! $isDirty) {\n            return;\n        }\n\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());\n\n       // Invalidate non initialized collections OR ordered collection\n        if ($isDirty && ! $isInitialized || $this->association->isOrdered()) {\n            $this->persister->update($collection);\n\n            $this->queuedCache['delete'][spl_object_id($collection)] = $key;\n\n            return;\n        }\n\n        $this->persister->update($collection);\n\n        $this->queuedCache['update'][spl_object_id($collection)] = [\n            'key'   => $key,\n            'list'  => $collection,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CannotUpdateReadOnlyCollection;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\n\nclass ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister\n{\n    public function update(PersistentCollection $collection): void\n    {\n        if ($collection->isDirty() && $collection->getSnapshot()) {\n            throw CannotUpdateReadOnlyCollection::fromEntityAndField(\n                DefaultProxyClassNameResolver::getClass($collection->getOwner()),\n                $this->association->fieldName,\n            );\n        }\n\n        parent::update($collection);\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\n\nuse function spl_object_id;\n\nclass ReadWriteCachedCollectionPersister extends AbstractCollectionPersister\n{\n    public function __construct(\n        CollectionPersister $persister,\n        ConcurrentRegion $region,\n        EntityManagerInterface $em,\n        AssociationMapping $association,\n    ) {\n        parent::__construct($persister, $region, $em, $association);\n    }\n\n    public function afterTransactionComplete(): void\n    {\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function afterTransactionRolledBack(): void\n    {\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function delete(PersistentCollection $collection): void\n    {\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());\n        $lock    = $this->region->lock($key);\n\n        $this->persister->delete($collection);\n\n        if ($lock === null) {\n            return;\n        }\n\n        $this->queuedCache['delete'][spl_object_id($collection)] = [\n            'key'   => $key,\n            'lock'  => $lock,\n        ];\n    }\n\n    public function update(PersistentCollection $collection): void\n    {\n        $isInitialized = $collection->isInitialized();\n        $isDirty       = $collection->isDirty();\n\n        if (! $isInitialized && ! $isDirty) {\n            return;\n        }\n\n        $this->persister->update($collection);\n\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());\n        $lock    = $this->region->lock($key);\n\n        if ($lock === null) {\n            return;\n        }\n\n        $this->queuedCache['update'][spl_object_id($collection)] = [\n            'key'   => $key,\n            'lock'  => $lock,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Entity/AbstractEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityHydrator;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\Cache\\TimestampCacheKey;\nuse Doctrine\\ORM\\Cache\\TimestampRegion;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function array_merge;\nuse function func_get_args;\nuse function serialize;\nuse function sha1;\n\nabstract class AbstractEntityPersister implements CachedEntityPersister\n{\n    protected UnitOfWork $uow;\n    protected ClassMetadataFactory $metadataFactory;\n\n    /** @var mixed[] */\n    protected array $queuedCache = [];\n\n    protected TimestampRegion $timestampRegion;\n    protected TimestampCacheKey $timestampKey;\n    protected EntityHydrator $hydrator;\n    protected Cache $cache;\n    protected FilterCollection $filters;\n    protected CacheLogger|null $cacheLogger = null;\n    protected string $regionName;\n\n    /**\n     * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.\n     *\n     * @var array<string>|null\n     */\n    protected array|null $joinedAssociations = null;\n\n    public function __construct(\n        protected EntityPersister $persister,\n        protected Region $region,\n        EntityManagerInterface $em,\n        protected ClassMetadata $class,\n    ) {\n        $configuration = $em->getConfiguration();\n        $cacheConfig   = $configuration->getSecondLevelCacheConfiguration();\n        $cacheFactory  = $cacheConfig->getCacheFactory();\n\n        $this->cache           = $em->getCache();\n        $this->filters         = $em->getFilters();\n        $this->regionName      = $region->getName();\n        $this->uow             = $em->getUnitOfWork();\n        $this->metadataFactory = $em->getMetadataFactory();\n        $this->cacheLogger     = $cacheConfig->getCacheLogger();\n        $this->timestampRegion = $cacheFactory->getTimestampRegion();\n        $this->hydrator        = $cacheFactory->buildEntityHydrator($em, $class);\n        $this->timestampKey    = new TimestampCacheKey($this->class->rootEntityName);\n    }\n\n    public function addInsert(object $entity): void\n    {\n        $this->persister->addInsert($entity);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getInserts(): array\n    {\n        return $this->persister->getInserts();\n    }\n\n    public function getSelectSQL(\n        array|Criteria $criteria,\n        AssociationMapping|null $assoc = null,\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        int|null $offset = null,\n        array|null $orderBy = null,\n    ): string {\n        return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);\n    }\n\n    public function getCountSQL(array|Criteria $criteria = []): string\n    {\n        return $this->persister->getCountSQL($criteria);\n    }\n\n    public function getInsertSQL(): string\n    {\n        return $this->persister->getInsertSQL();\n    }\n\n    public function getResultSetMapping(): ResultSetMapping\n    {\n        return $this->persister->getResultSetMapping();\n    }\n\n    public function getSelectConditionStatementSQL(\n        string $field,\n        mixed $value,\n        AssociationMapping|null $assoc = null,\n        string|null $comparison = null,\n    ): string {\n        return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);\n    }\n\n    public function exists(object $entity, Criteria|null $extraConditions = null): bool\n    {\n        if ($extraConditions === null) {\n            $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity));\n\n            if ($this->region->contains($key)) {\n                return true;\n            }\n        }\n\n        return $this->persister->exists($entity, $extraConditions);\n    }\n\n    public function getCacheRegion(): Region\n    {\n        return $this->region;\n    }\n\n    public function getEntityHydrator(): EntityHydrator\n    {\n        return $this->hydrator;\n    }\n\n    public function storeEntityCache(object $entity, EntityCacheKey $key): bool\n    {\n        $class     = $this->class;\n        $className = DefaultProxyClassNameResolver::getClass($entity);\n\n        if ($className !== $this->class->name) {\n            $class = $this->metadataFactory->getMetadataFor($className);\n        }\n\n        $entry  = $this->hydrator->buildCacheEntry($class, $key, $entity);\n        $cached = $this->region->put($key, $entry);\n\n        if ($cached) {\n            $this->cacheLogger?->entityCachePut($this->regionName, $key);\n        }\n\n        return $cached;\n    }\n\n    private function storeJoinedAssociations(object $entity): void\n    {\n        if ($this->joinedAssociations === null) {\n            $associations = [];\n\n            foreach ($this->class->associationMappings as $name => $assoc) {\n                if (\n                    isset($assoc->cache) &&\n                    ($assoc->isToOne()) &&\n                    ($assoc->fetch === ClassMetadata::FETCH_EAGER || ! $assoc->isOwningSide())\n                ) {\n                    $associations[] = $name;\n                }\n            }\n\n            $this->joinedAssociations = $associations;\n        }\n\n        foreach ($this->joinedAssociations as $name) {\n            $assoc       = $this->class->associationMappings[$name];\n            $assocEntity = $this->class->getFieldValue($entity, $name);\n\n            if ($assocEntity === null) {\n                continue;\n            }\n\n            $assocId        = $this->uow->getEntityIdentifier($assocEntity);\n            $assocMetadata  = $this->metadataFactory->getMetadataFor($assoc->targetEntity);\n            $assocKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocId);\n            $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity);\n\n            $assocPersister->storeEntityCache($assocEntity, $assocKey);\n        }\n    }\n\n    /**\n     * Generates a string of currently query\n     *\n     * @param string[]|Criteria         $criteria\n     * @param array<string, Order>|null $orderBy\n     */\n    protected function getHash(\n        string $query,\n        array|Criteria $criteria,\n        array|null $orderBy = null,\n        int|null $limit = null,\n        int|null $offset = null,\n    ): string {\n        [$params] = $criteria instanceof Criteria\n            ? $this->persister->expandCriteriaParameters($criteria)\n            : $this->persister->expandParameters($criteria);\n\n        return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $this->filters->getHash());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function expandParameters(array $criteria): array\n    {\n        return $this->persister->expandParameters($criteria);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function expandCriteriaParameters(Criteria $criteria): array\n    {\n        return $this->persister->expandCriteriaParameters($criteria);\n    }\n\n    public function getClassMetadata(): ClassMetadata\n    {\n        return $this->persister->getClassMetadata();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getManyToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array {\n        return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array {\n        return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);\n    }\n\n    public function getOwningTable(string $fieldName): string\n    {\n        return $this->persister->getOwningTable($fieldName);\n    }\n\n    public function executeInserts(): void\n    {\n        // The commit order/foreign key relationships may make it necessary that multiple calls to executeInsert()\n        // are performed, so collect all the new entities.\n        $newInserts = $this->persister->getInserts();\n\n        if ($newInserts) {\n            $this->queuedCache['insert'] = array_merge($this->queuedCache['insert'] ?? [], $newInserts);\n        }\n\n        $this->persister->executeInserts();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function load(\n        array $criteria,\n        object|null $entity = null,\n        AssociationMapping|null $assoc = null,\n        array $hints = [],\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        array|null $orderBy = null,\n    ): object|null {\n        if ($entity !== null || $assoc !== null || $hints !== [] || $lockMode !== null) {\n            return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);\n        }\n\n        //handle only EntityRepository#findOneBy\n        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);\n        $hash       = $this->getHash($query, $criteria);\n        $rsm        = $this->getResultSetMapping();\n        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);\n        $queryCache = $this->cache->getQueryCache($this->regionName);\n        $result     = $queryCache->get($queryKey, $rsm);\n\n        if ($result !== null) {\n            $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);\n\n            return $result[0];\n        }\n\n        $result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);\n\n        if ($result === null) {\n            return null;\n        }\n\n        $cached = $queryCache->put($queryKey, $rsm, [$result]);\n\n        $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);\n\n        if ($cached) {\n            $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadAll(\n        array $criteria = [],\n        array|null $orderBy = null,\n        int|null $limit = null,\n        int|null $offset = null,\n    ): array {\n        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);\n        $hash       = $this->getHash($query, $criteria);\n        $rsm        = $this->getResultSetMapping();\n        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);\n        $queryCache = $this->cache->getQueryCache($this->regionName);\n        $result     = $queryCache->get($queryKey, $rsm);\n\n        if ($result !== null) {\n            $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);\n\n            return $result;\n        }\n\n        $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);\n        $cached = $queryCache->put($queryKey, $rsm, $result);\n\n        if ($result) {\n            $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);\n        }\n\n        if ($cached) {\n            $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadById(array $identifier, object|null $entity = null): object|null\n    {\n        $cacheKey   = new EntityCacheKey($this->class->rootEntityName, $identifier);\n        $cacheEntry = $this->region->get($cacheKey);\n        $class      = $this->class;\n\n        if ($cacheEntry !== null) {\n            if ($cacheEntry->class !== $this->class->name) {\n                $class = $this->metadataFactory->getMetadataFor($cacheEntry->class);\n            }\n\n            $cachedEntity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity);\n\n            if ($cachedEntity !== null) {\n                $this->cacheLogger?->entityCacheHit($this->regionName, $cacheKey);\n\n                return $cachedEntity;\n            }\n        }\n\n        $entity = $this->persister->loadById($identifier, $entity);\n\n        if ($entity === null) {\n            return null;\n        }\n\n        $class     = $this->class;\n        $className = DefaultProxyClassNameResolver::getClass($entity);\n\n        if ($className !== $this->class->name) {\n            $class = $this->metadataFactory->getMetadataFor($className);\n        }\n\n        $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);\n        $cached     = $this->region->put($cacheKey, $cacheEntry);\n\n        if ($cached && ($this->joinedAssociations === null || $this->joinedAssociations)) {\n            $this->storeJoinedAssociations($entity);\n        }\n\n        if ($cached) {\n            $this->cacheLogger?->entityCachePut($this->regionName, $cacheKey);\n        }\n\n        $this->cacheLogger?->entityCacheMiss($this->regionName, $cacheKey);\n\n        return $entity;\n    }\n\n    public function count(array|Criteria $criteria = []): int\n    {\n        return $this->persister->count($criteria);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadCriteria(Criteria $criteria): array\n    {\n        $orderBy     = $criteria->orderings();\n        $limit       = $criteria->getMaxResults();\n        $offset      = $criteria->getFirstResult();\n        $query       = $this->persister->getSelectSQL($criteria);\n        $hash        = $this->getHash($query, $criteria, $orderBy, $limit, $offset);\n        $rsm         = $this->getResultSetMapping();\n        $queryKey    = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);\n        $queryCache  = $this->cache->getQueryCache($this->regionName);\n        $cacheResult = $queryCache->get($queryKey, $rsm);\n\n        if ($cacheResult !== null) {\n            $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);\n\n            return $cacheResult;\n        }\n\n        $result = $this->persister->loadCriteria($criteria);\n        $cached = $queryCache->put($queryKey, $rsm, $result);\n\n        if ($result) {\n            $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);\n        }\n\n        if ($cached) {\n            $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadManyToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        PersistentCollection $collection,\n    ): array {\n        $persister = $this->uow->getCollectionPersister($assoc);\n        $hasCache  = ($persister instanceof CachedPersister);\n\n        if (! $hasCache) {\n            return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);\n        }\n\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());\n        $list    = $persister->loadCollectionCache($collection, $key);\n\n        if ($list !== null) {\n            $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key);\n\n            return $list;\n        }\n\n        $list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);\n\n        $persister->storeCollectionCache($key, $list);\n\n        $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);\n\n        return $list;\n    }\n\n    public function loadOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        PersistentCollection $collection,\n    ): mixed {\n        $persister = $this->uow->getCollectionPersister($assoc);\n        $hasCache  = ($persister instanceof CachedPersister);\n\n        if (! $hasCache) {\n            return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);\n        }\n\n        $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());\n        $key     = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());\n        $list    = $persister->loadCollectionCache($collection, $key);\n\n        if ($list !== null) {\n            $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key);\n\n            return $list;\n        }\n\n        $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);\n\n        $persister->storeCollectionCache($key, $list);\n\n        $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);\n\n        return $list;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null\n    {\n        return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function lock(array $criteria, LockMode|int $lockMode): void\n    {\n        $this->persister->lock($criteria, $lockMode);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void\n    {\n        $this->persister->refresh($id, $entity, $lockMode);\n    }\n\n    /** @param array<string, mixed> $ownerId */\n    protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey\n    {\n        $filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release\n\n        return new CollectionCacheKey(\n            $this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,\n            $association->fieldName,\n            $ownerId,\n            $filterHash,\n        );\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Entity/CachedEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityHydrator;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\n\n/**\n * Interface for second level cache entity persisters.\n */\ninterface CachedEntityPersister extends CachedPersister, EntityPersister\n{\n    public function getEntityHydrator(): EntityHydrator;\n\n    public function storeEntityCache(object $entity, EntityCacheKey $key): bool;\n}\n"
  },
  {
    "path": "src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\n\n/**\n * Specific non-strict read/write cached entity persister\n */\nclass NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister\n{\n    public function afterTransactionComplete(): void\n    {\n        $isChanged = false;\n\n        if (isset($this->queuedCache['insert'])) {\n            foreach ($this->queuedCache['insert'] as $entity) {\n                $isChanged = $this->updateCache($entity, $isChanged);\n            }\n        }\n\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $entity) {\n                $isChanged = $this->updateCache($entity, $isChanged);\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $key) {\n                $this->region->evict($key);\n\n                $isChanged = true;\n            }\n        }\n\n        if ($isChanged) {\n            $this->timestampRegion->update($this->timestampKey);\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function afterTransactionRolledBack(): void\n    {\n        $this->queuedCache = [];\n    }\n\n    public function delete(object $entity): bool\n    {\n        $key     = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));\n        $deleted = $this->persister->delete($entity);\n\n        if ($deleted) {\n            $this->region->evict($key);\n        }\n\n        $this->queuedCache['delete'][] = $key;\n\n        return $deleted;\n    }\n\n    public function update(object $entity): void\n    {\n        $this->persister->update($entity);\n\n        $this->queuedCache['update'][] = $entity;\n    }\n\n    private function updateCache(object $entity, bool $isChanged): bool\n    {\n        $class     = $this->metadataFactory->getMetadataFor($entity::class);\n        $key       = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));\n        $entry     = $this->hydrator->buildCacheEntry($class, $key, $entity);\n        $cached    = $this->region->put($key, $entry);\n        $isChanged = $isChanged || $cached;\n\n        if ($cached) {\n            $this->cacheLogger?->entityCachePut($this->regionName, $key);\n        }\n\n        return $isChanged;\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CannotUpdateReadOnlyEntity;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\n\n/**\n * Specific read-only region entity persister\n */\nclass ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister\n{\n    public function update(object $entity): void\n    {\n        throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\n\n/**\n * Specific read-write entity persister\n */\nclass ReadWriteCachedEntityPersister extends AbstractEntityPersister\n{\n    public function __construct(EntityPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, ClassMetadata $class)\n    {\n        parent::__construct($persister, $region, $em, $class);\n    }\n\n    public function afterTransactionComplete(): void\n    {\n        $isChanged = true;\n\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $item) {\n                $this->region->evict($item['key']);\n\n                $isChanged = true;\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $item) {\n                $this->region->evict($item['key']);\n\n                $isChanged = true;\n            }\n        }\n\n        if ($isChanged) {\n            $this->timestampRegion->update($this->timestampKey);\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function afterTransactionRolledBack(): void\n    {\n        if (isset($this->queuedCache['update'])) {\n            foreach ($this->queuedCache['update'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        if (isset($this->queuedCache['delete'])) {\n            foreach ($this->queuedCache['delete'] as $item) {\n                $this->region->evict($item['key']);\n            }\n        }\n\n        $this->queuedCache = [];\n    }\n\n    public function delete(object $entity): bool\n    {\n        $key     = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));\n        $lock    = $this->region->lock($key);\n        $deleted = $this->persister->delete($entity);\n\n        if ($deleted) {\n            $this->region->evict($key);\n        }\n\n        if ($lock === null) {\n            return $deleted;\n        }\n\n        $this->queuedCache['delete'][] = [\n            'lock'   => $lock,\n            'key'    => $key,\n        ];\n\n        return $deleted;\n    }\n\n    public function update(object $entity): void\n    {\n        $key  = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));\n        $lock = $this->region->lock($key);\n\n        $this->persister->update($entity);\n\n        if ($lock === null) {\n            return;\n        }\n\n        $this->queuedCache['update'][] = [\n            'lock'   => $lock,\n            'key'    => $key,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Cache/QueryCache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\n\n/**\n * Defines the contract for caches capable of storing query results.\n * These caches should only concern themselves with storing the matching result ids.\n */\ninterface QueryCache\n{\n    public function clear(): bool;\n\n    /** @param mixed[] $hints */\n    public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool;\n\n    /**\n     * @param mixed[] $hints\n     *\n     * @return mixed[]|null\n     */\n    public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null;\n\n    public function getRegion(): Region;\n}\n"
  },
  {
    "path": "src/Cache/QueryCacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function microtime;\n\nclass QueryCacheEntry implements CacheEntry\n{\n    /**\n     * Time creation of this cache entry\n     */\n    public readonly float $time;\n\n    /** @param array<string, mixed> $result List of entity identifiers */\n    public function __construct(\n        public readonly array $result,\n        float|null $time = null,\n    ) {\n        $this->time = $time ?: microtime(true);\n    }\n\n    /** @param array<string, mixed> $values */\n    public static function __set_state(array $values): self\n    {\n        return new self($values['result'], $values['time']);\n    }\n}\n"
  },
  {
    "path": "src/Cache/QueryCacheKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache;\n\n/**\n * A cache key that identifies a particular query.\n */\nclass QueryCacheKey extends CacheKey\n{\n    /** @param Cache::MODE_* $cacheMode */\n    public function __construct(\n        string $cacheId,\n        public readonly int $lifetime = 0,\n        public readonly int $cacheMode = Cache::MODE_NORMAL,\n        public readonly TimestampCacheKey|null $timestampKey = null,\n    ) {\n        parent::__construct($cacheId);\n    }\n}\n"
  },
  {
    "path": "src/Cache/QueryCacheValidator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Cache query validator interface.\n */\ninterface QueryCacheValidator\n{\n    /**\n     * Checks if the query entry is valid\n     */\n    public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool;\n}\n"
  },
  {
    "path": "src/Cache/Region/DefaultRegion.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Region;\n\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Psr\\Cache\\CacheItemInterface;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Traversable;\n\nuse function array_map;\nuse function iterator_to_array;\nuse function strtr;\n\n/**\n * The simplest cache region compatible with all doctrine-cache drivers.\n */\nclass DefaultRegion implements Region\n{\n    private const REGION_KEY_SEPARATOR = '_';\n    private const REGION_PREFIX        = 'DC2_REGION_';\n\n    public function __construct(\n        private readonly string $name,\n        private readonly CacheItemPoolInterface $cacheItemPool,\n        private readonly int $lifetime = 0,\n    ) {\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function contains(CacheKey $key): bool\n    {\n        return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key));\n    }\n\n    public function get(CacheKey $key): CacheEntry|null\n    {\n        $item  = $this->cacheItemPool->getItem($this->getCacheEntryKey($key));\n        $entry = $item->isHit() ? $item->get() : null;\n\n        if (! $entry instanceof CacheEntry) {\n            return null;\n        }\n\n        return $entry;\n    }\n\n    public function getMultiple(CollectionCacheEntry $collection): array|null\n    {\n        $keys = array_map(\n            $this->getCacheEntryKey(...),\n            $collection->identifiers,\n        );\n        /** @var iterable<string, CacheItemInterface> $items */\n        $items = $this->cacheItemPool->getItems($keys);\n        if ($items instanceof Traversable) {\n            $items = iterator_to_array($items);\n        }\n\n        $result = [];\n        foreach ($keys as $arrayKey => $cacheKey) {\n            if (! isset($items[$cacheKey]) || ! $items[$cacheKey]->isHit()) {\n                return null;\n            }\n\n            $entry = $items[$cacheKey]->get();\n            if (! $entry instanceof CacheEntry) {\n                return null;\n            }\n\n            $result[$arrayKey] = $entry;\n        }\n\n        return $result;\n    }\n\n    public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool\n    {\n        $item = $this->cacheItemPool\n            ->getItem($this->getCacheEntryKey($key))\n            ->set($entry);\n\n        if ($this->lifetime > 0) {\n            $item->expiresAfter($this->lifetime);\n        }\n\n        return $this->cacheItemPool->save($item);\n    }\n\n    public function evict(CacheKey $key): bool\n    {\n        return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key));\n    }\n\n    public function evictAll(): bool\n    {\n        return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name);\n    }\n\n    private function getCacheEntryKey(CacheKey $key): string\n    {\n        return self::REGION_PREFIX . $this->name . self::REGION_KEY_SEPARATOR . strtr($key->hash, '{}()/\\@:', '________');\n    }\n}\n"
  },
  {
    "path": "src/Cache/Region/FileLockRegion.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Region;\n\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Region;\nuse InvalidArgumentException;\n\nuse function array_filter;\nuse function array_map;\nuse function chmod;\nuse function file_get_contents;\nuse function file_put_contents;\nuse function fileatime;\nuse function glob;\nuse function is_dir;\nuse function is_file;\nuse function is_writable;\nuse function mkdir;\nuse function sprintf;\nuse function time;\nuse function unlink;\n\nuse const DIRECTORY_SEPARATOR;\nuse const LOCK_EX;\n\n/**\n * Very naive concurrent region, based on file locks.\n */\nclass FileLockRegion implements ConcurrentRegion\n{\n    final public const LOCK_EXTENSION = 'lock';\n\n    /**\n     * @param numeric-string|int $lockLifetime\n     *\n     * @throws InvalidArgumentException\n     */\n    public function __construct(\n        private readonly Region $region,\n        private readonly string $directory,\n        private readonly string|int $lockLifetime,\n    ) {\n        if (! is_dir($directory) && ! @mkdir($directory, 0775, true)) {\n            throw new InvalidArgumentException(sprintf('The directory \"%s\" does not exist and could not be created.', $directory));\n        }\n\n        if (! is_writable($directory)) {\n            throw new InvalidArgumentException(sprintf('The directory \"%s\" is not writable.', $directory));\n        }\n    }\n\n    private function isLocked(CacheKey $key, Lock|null $lock = null): bool\n    {\n        $filename = $this->getLockFileName($key);\n\n        if (! is_file($filename)) {\n            return false;\n        }\n\n        $time    = $this->getLockTime($filename);\n        $content = $this->getLockContent($filename);\n\n        if ($content === false || $time === false) {\n            @unlink($filename);\n\n            return false;\n        }\n\n        if ($lock && $content === $lock->value) {\n            return false;\n        }\n\n        // outdated lock\n        if ($time + $this->lockLifetime <= time()) {\n            @unlink($filename);\n\n            return false;\n        }\n\n        return true;\n    }\n\n    private function getLockFileName(CacheKey $key): string\n    {\n        return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION;\n    }\n\n    private function getLockContent(string $filename): string|false\n    {\n        return @file_get_contents($filename);\n    }\n\n    private function getLockTime(string $filename): int|false\n    {\n        return @fileatime($filename);\n    }\n\n    public function getName(): string\n    {\n        return $this->region->getName();\n    }\n\n    public function contains(CacheKey $key): bool\n    {\n        if ($this->isLocked($key)) {\n            return false;\n        }\n\n        return $this->region->contains($key);\n    }\n\n    public function get(CacheKey $key): CacheEntry|null\n    {\n        if ($this->isLocked($key)) {\n            return null;\n        }\n\n        return $this->region->get($key);\n    }\n\n    public function getMultiple(CollectionCacheEntry $collection): array|null\n    {\n        if (array_filter(array_map($this->isLocked(...), $collection->identifiers))) {\n            return null;\n        }\n\n        return $this->region->getMultiple($collection);\n    }\n\n    public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool\n    {\n        if ($this->isLocked($key, $lock)) {\n            return false;\n        }\n\n        return $this->region->put($key, $entry);\n    }\n\n    public function evict(CacheKey $key): bool\n    {\n        if ($this->isLocked($key)) {\n            @unlink($this->getLockFileName($key));\n        }\n\n        return $this->region->evict($key);\n    }\n\n    public function evictAll(): bool\n    {\n        // The check below is necessary because on some platforms glob returns false\n        // when nothing matched (even though no errors occurred)\n        $filenames = glob(sprintf('%s/*.%s', $this->directory, self::LOCK_EXTENSION)) ?: [];\n\n        foreach ($filenames as $filename) {\n            @unlink($filename);\n        }\n\n        return $this->region->evictAll();\n    }\n\n    public function lock(CacheKey $key): Lock|null\n    {\n        if ($this->isLocked($key)) {\n            return null;\n        }\n\n        $lock     = Lock::createLockRead();\n        $filename = $this->getLockFileName($key);\n\n        if (@file_put_contents($filename, $lock->value, LOCK_EX) === false) {\n            return null;\n        }\n\n        chmod($filename, 0664);\n\n        return $lock;\n    }\n\n    public function unlock(CacheKey $key, Lock $lock): bool\n    {\n        if ($this->isLocked($key, $lock)) {\n            return false;\n        }\n\n        return @unlink($this->getLockFileName($key));\n    }\n}\n"
  },
  {
    "path": "src/Cache/Region/UpdateTimestampCache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache\\Region;\n\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\TimestampCacheEntry;\nuse Doctrine\\ORM\\Cache\\TimestampRegion;\n\n/**\n * Tracks the timestamps of the most recent updates to particular keys.\n */\nclass UpdateTimestampCache extends DefaultRegion implements TimestampRegion\n{\n    public function update(CacheKey $key): void\n    {\n        $this->put($key, new TimestampCacheEntry());\n    }\n}\n"
  },
  {
    "path": "src/Cache/Region.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\n\n/**\n * Defines a contract for accessing a particular named region.\n */\ninterface Region\n{\n    /**\n     * Retrieve the name of this region.\n     */\n    public function getName(): string;\n\n    /**\n     * Determine whether this region contains data for the given key.\n     *\n     * @param CacheKey $key The cache key\n     */\n    public function contains(CacheKey $key): bool;\n\n    /**\n     * Get an item from the cache.\n     *\n     * @param CacheKey $key The key of the item to be retrieved.\n     *\n     * @return CacheEntry|null The cached entry or NULL\n     *\n     * @throws CacheException Indicates a problem accessing the item or region.\n     */\n    public function get(CacheKey $key): CacheEntry|null;\n\n    /**\n     * Get all items from the cache identified by $keys.\n     * It returns NULL if some elements can not be found.\n     *\n     * @param CollectionCacheEntry $collection The collection of the items to be retrieved.\n     *\n     * @return CacheEntry[]|null The cached entries or NULL if one or more entries can not be found\n     */\n    public function getMultiple(CollectionCacheEntry $collection): array|null;\n\n    /**\n     * Put an item into the cache.\n     *\n     * @param CacheKey   $key   The key under which to cache the item.\n     * @param CacheEntry $entry The entry to cache.\n     * @param Lock|null  $lock  The lock previously obtained.\n     *\n     * @throws CacheException Indicates a problem accessing the region.\n     */\n    public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool;\n\n    /**\n     * Remove an item from the cache.\n     *\n     * @param CacheKey $key The key under which to cache the item.\n     *\n     * @throws CacheException Indicates a problem accessing the region.\n     */\n    public function evict(CacheKey $key): bool;\n\n    /**\n     * Remove all contents of this particular cache region.\n     *\n     * @throws CacheException Indicates problem accessing the region.\n     */\n    public function evictAll(): bool;\n}\n"
  },
  {
    "path": "src/Cache/RegionsConfiguration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Cache regions configuration\n */\nclass RegionsConfiguration\n{\n    /** @var array<string,int> */\n    private array $lifetimes = [];\n\n    /** @var array<string,int> */\n    private array $lockLifetimes = [];\n\n    public function __construct(\n        private int $defaultLifetime = 3600,\n        private int $defaultLockLifetime = 60,\n    ) {\n    }\n\n    public function getDefaultLifetime(): int\n    {\n        return $this->defaultLifetime;\n    }\n\n    public function setDefaultLifetime(int $defaultLifetime): void\n    {\n        $this->defaultLifetime = $defaultLifetime;\n    }\n\n    public function getDefaultLockLifetime(): int\n    {\n        return $this->defaultLockLifetime;\n    }\n\n    public function setDefaultLockLifetime(int $defaultLockLifetime): void\n    {\n        $this->defaultLockLifetime = $defaultLockLifetime;\n    }\n\n    public function getLifetime(string $regionName): int\n    {\n        return $this->lifetimes[$regionName] ?? $this->defaultLifetime;\n    }\n\n    public function setLifetime(string $name, int $lifetime): void\n    {\n        $this->lifetimes[$name] = $lifetime;\n    }\n\n    public function getLockLifetime(string $regionName): int\n    {\n        return $this->lockLifetimes[$regionName] ?? $this->defaultLockLifetime;\n    }\n\n    public function setLockLifetime(string $name, int $lifetime): void\n    {\n        $this->lockLifetimes[$name] = $lifetime;\n    }\n}\n"
  },
  {
    "path": "src/Cache/TimestampCacheEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function microtime;\n\nclass TimestampCacheEntry implements CacheEntry\n{\n    public readonly float $time;\n\n    public function __construct(float|null $time = null)\n    {\n        $this->time = $time ?? microtime(true);\n    }\n\n    /**\n     * Creates a new TimestampCacheEntry\n     *\n     * This method allow Doctrine\\Common\\Cache\\PhpFileCache compatibility\n     *\n     * @param array<string,float> $values array containing property values\n     */\n    public static function __set_state(array $values): TimestampCacheEntry\n    {\n        return new self($values['time']);\n    }\n}\n"
  },
  {
    "path": "src/Cache/TimestampCacheKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * A key that identifies a timestamped space.\n */\nclass TimestampCacheKey extends CacheKey\n{\n    /** @param string $space Result cache id */\n    public function __construct(string $space)\n    {\n        parent::__construct($space);\n    }\n}\n"
  },
  {
    "path": "src/Cache/TimestampQueryCacheValidator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\nuse function microtime;\n\nclass TimestampQueryCacheValidator implements QueryCacheValidator\n{\n    public function __construct(private readonly TimestampRegion $timestampRegion)\n    {\n    }\n\n    public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool\n    {\n        if ($this->regionUpdated($key, $entry)) {\n            return false;\n        }\n\n        if ($key->lifetime === 0) {\n            return true;\n        }\n\n        return $entry->time + $key->lifetime > microtime(true);\n    }\n\n    private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry): bool\n    {\n        if ($key->timestampKey === null) {\n            return false;\n        }\n\n        $timestamp = $this->timestampRegion->get($key->timestampKey);\n\n        return $timestamp && $timestamp->time > $entry->time;\n    }\n}\n"
  },
  {
    "path": "src/Cache/TimestampRegion.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Cache;\n\n/**\n * Defines the contract for a cache region which will specifically be used to store entity \"update timestamps\".\n */\ninterface TimestampRegion extends Region\n{\n    /**\n     * Update a specific key into the cache region.\n     *\n     * @throws LockException Indicates a problem accessing the region.\n     */\n    public function update(CacheKey $key): void;\n}\n"
  },
  {
    "path": "src/Cache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Cache\\QueryCache;\nuse Doctrine\\ORM\\Cache\\Region;\n\n/**\n * Provides an API for querying/managing the second level cache regions.\n */\ninterface Cache\n{\n    public const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';\n\n    public const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';\n\n    /**\n     * May read items from the cache, but will not add items.\n     */\n    public const MODE_GET = 1;\n\n    /**\n     * Will never read items from the cache,\n     * but will add items to the cache as it reads them from the database.\n     */\n    public const MODE_PUT = 2;\n\n    /**\n     * May read items from the cache, and add items to the cache.\n     */\n    public const MODE_NORMAL = 3;\n\n    /**\n     * The query will never read items from the cache,\n     * but will refresh items to the cache as it reads them from the database.\n     */\n    public const MODE_REFRESH = 4;\n\n    public function getEntityCacheRegion(string $className): Region|null;\n\n    public function getCollectionCacheRegion(string $className, string $association): Region|null;\n\n    /**\n     * Determine whether the cache contains data for the given entity \"instance\".\n     */\n    public function containsEntity(string $className, mixed $identifier): bool;\n\n    /**\n     * Evicts the entity data for a particular entity \"instance\".\n     */\n    public function evictEntity(string $className, mixed $identifier): void;\n\n    /**\n     * Evicts all entity data from the given region.\n     */\n    public function evictEntityRegion(string $className): void;\n\n    /**\n     * Evict data from all entity regions.\n     */\n    public function evictEntityRegions(): void;\n\n    /**\n     * Determine whether the cache contains data for the given collection.\n     */\n    public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool;\n\n    /**\n     * Evicts the cache data for the given identified collection instance.\n     */\n    public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void;\n\n    /**\n     * Evicts all entity data from the given region.\n     */\n    public function evictCollectionRegion(string $className, string $association): void;\n\n    /**\n     * Evict data from all collection regions.\n     */\n    public function evictCollectionRegions(): void;\n\n    /**\n     * Determine whether the cache contains data for the given query.\n     */\n    public function containsQuery(string $regionName): bool;\n\n    /**\n     * Evicts all cached query results under the given name, or default query cache if the region name is NULL.\n     */\n    public function evictQueryRegion(string|null $regionName = null): void;\n\n    /**\n     * Evict data from all query regions.\n     */\n    public function evictQueryRegions(): void;\n\n    /**\n     * Get query cache by region name or create a new one if none exist.\n     *\n     * @param string|null $regionName Query cache region name, or default query cache if the region name is NULL.\n     */\n    public function getQueryCache(string|null $regionName = null): QueryCache;\n}\n"
  },
  {
    "path": "src/Configuration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Cache\\CacheConfiguration;\nuse Doctrine\\ORM\\Exception\\InvalidEntityRepository;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy;\nuse Doctrine\\ORM\\Mapping\\EntityListenerResolver;\nuse Doctrine\\ORM\\Mapping\\NamingStrategy;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\Mapping\\TypedFieldMapper;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\ORM\\Repository\\DefaultRepositoryFactory;\nuse Doctrine\\ORM\\Repository\\RepositoryFactory;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse LogicException;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nuse function class_exists;\nuse function is_a;\nuse function strtolower;\n\nuse const PHP_VERSION_ID;\n\n/**\n * Configuration container for all configuration options of Doctrine.\n * It combines all configuration options from DBAL & ORM.\n *\n * Internal note: When adding a new configuration option just write a getter/setter pair.\n */\nclass Configuration extends \\Doctrine\\DBAL\\Configuration\n{\n    /** @var mixed[] */\n    protected array $attributes = [];\n\n    /** @phpstan-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */\n    private $identityGenerationPreferences = [];\n\n    /** @phpstan-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */\n    public function setIdentityGenerationPreferences(array $value): void\n    {\n        $this->identityGenerationPreferences = $value;\n    }\n\n    /** @phpstan-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */\n    public function getIdentityGenerationPreferences(): array\n    {\n        return $this->identityGenerationPreferences;\n    }\n\n    /**\n     * Sets the directory where Doctrine generates any necessary proxy class files.\n     */\n    public function setProxyDir(string $dir): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::triggerIfCalledFromOutside(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        $this->attributes['proxyDir'] = $dir;\n    }\n\n    /**\n     * Gets the directory where Doctrine generates any necessary proxy class files.\n     */\n    public function getProxyDir(): string|null\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        return $this->attributes['proxyDir'] ?? null;\n    }\n\n    /**\n     * Gets the strategy for automatically generating proxy classes.\n     *\n     * @return ProxyFactory::AUTOGENERATE_*\n     */\n    public function getAutoGenerateProxyClasses(): int\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        return $this->attributes['autoGenerateProxyClasses'] ?? ProxyFactory::AUTOGENERATE_ALWAYS;\n    }\n\n    /**\n     * Sets the strategy for automatically generating proxy classes.\n     *\n     * @param bool|ProxyFactory::AUTOGENERATE_* $autoGenerate True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER.\n     */\n    public function setAutoGenerateProxyClasses(bool|int $autoGenerate): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::triggerIfCalledFromOutside(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        $this->attributes['autoGenerateProxyClasses'] = (int) $autoGenerate;\n    }\n\n    /**\n     * Gets the namespace where proxy classes reside.\n     */\n    public function getProxyNamespace(): string|null\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        return $this->attributes['proxyNamespace'] ?? null;\n    }\n\n    /**\n     * Sets the namespace where proxy classes reside.\n     */\n    public function setProxyNamespace(string $ns): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::triggerIfCalledFromOutside(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        $this->attributes['proxyNamespace'] = $ns;\n    }\n\n    /**\n     * Sets the cache driver implementation that is used for metadata caching.\n     *\n     * @todo Force parameter to be a Closure to ensure lazy evaluation\n     *       (as soon as a metadata cache is in effect, the driver never needs to initialize).\n     */\n    public function setMetadataDriverImpl(MappingDriver $driverImpl): void\n    {\n        $this->attributes['metadataDriverImpl'] = $driverImpl;\n    }\n\n    /**\n     * Sets the entity alias map.\n     *\n     * @phpstan-param array<string, string> $entityNamespaces\n     */\n    public function setEntityNamespaces(array $entityNamespaces): void\n    {\n        $this->attributes['entityNamespaces'] = $entityNamespaces;\n    }\n\n    /**\n     * Retrieves the list of registered entity namespace aliases.\n     *\n     * @phpstan-return array<string, string>\n     */\n    public function getEntityNamespaces(): array\n    {\n        return $this->attributes['entityNamespaces'];\n    }\n\n    /**\n     * Gets the cache driver implementation that is used for the mapping metadata.\n     */\n    public function getMetadataDriverImpl(): MappingDriver|null\n    {\n        return $this->attributes['metadataDriverImpl'] ?? null;\n    }\n\n    /**\n     * Gets the cache driver implementation that is used for the query cache (SQL cache).\n     */\n    public function getQueryCache(): CacheItemPoolInterface|null\n    {\n        return $this->attributes['queryCache'] ?? null;\n    }\n\n    /**\n     * Sets the cache driver implementation that is used for the query cache (SQL cache).\n     */\n    public function setQueryCache(CacheItemPoolInterface $cache): void\n    {\n        $this->attributes['queryCache'] = $cache;\n    }\n\n    public function getHydrationCache(): CacheItemPoolInterface|null\n    {\n        return $this->attributes['hydrationCache'] ?? null;\n    }\n\n    public function setHydrationCache(CacheItemPoolInterface $cache): void\n    {\n        $this->attributes['hydrationCache'] = $cache;\n    }\n\n    public function getMetadataCache(): CacheItemPoolInterface|null\n    {\n        return $this->attributes['metadataCache'] ?? null;\n    }\n\n    public function setMetadataCache(CacheItemPoolInterface $cache): void\n    {\n        $this->attributes['metadataCache'] = $cache;\n    }\n\n    /**\n     * Registers a custom DQL function that produces a string value.\n     * Such a function can then be used in any DQL statement in any place where string\n     * functions are allowed.\n     *\n     * DQL function names are case-insensitive.\n     *\n     * @param class-string|callable $className Class name or a callable that returns the function.\n     * @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className\n     */\n    public function addCustomStringFunction(string $name, string|callable $className): void\n    {\n        $this->attributes['customStringFunctions'][strtolower($name)] = $className;\n    }\n\n    /**\n     * Gets the implementation class name of a registered custom string DQL function.\n     *\n     * @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null\n     */\n    public function getCustomStringFunction(string $name): string|callable|null\n    {\n        $name = strtolower($name);\n\n        return $this->attributes['customStringFunctions'][$name] ?? null;\n    }\n\n    /**\n     * Sets a map of custom DQL string functions.\n     *\n     * Keys must be function names and values the FQCN of the implementing class.\n     * The function names will be case-insensitive in DQL.\n     *\n     * Any previously added string functions are discarded.\n     *\n     * @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions The map of custom\n     *                                                     DQL string functions.\n     */\n    public function setCustomStringFunctions(array $functions): void\n    {\n        foreach ($functions as $name => $className) {\n            $this->addCustomStringFunction($name, $className);\n        }\n    }\n\n    /**\n     * Registers a custom DQL function that produces a numeric value.\n     * Such a function can then be used in any DQL statement in any place where numeric\n     * functions are allowed.\n     *\n     * DQL function names are case-insensitive.\n     *\n     * @param class-string|callable $className Class name or a callable that returns the function.\n     * @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className\n     */\n    public function addCustomNumericFunction(string $name, string|callable $className): void\n    {\n        $this->attributes['customNumericFunctions'][strtolower($name)] = $className;\n    }\n\n    /**\n     * Gets the implementation class name of a registered custom numeric DQL function.\n     *\n     * @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null\n     */\n    public function getCustomNumericFunction(string $name): string|callable|null\n    {\n        $name = strtolower($name);\n\n        return $this->attributes['customNumericFunctions'][$name] ?? null;\n    }\n\n    /**\n     * Sets a map of custom DQL numeric functions.\n     *\n     * Keys must be function names and values the FQCN of the implementing class.\n     * The function names will be case-insensitive in DQL.\n     *\n     * Any previously added numeric functions are discarded.\n     *\n     * @param array<string, class-string> $functions The map of custom\n     *                                               DQL numeric functions.\n     */\n    public function setCustomNumericFunctions(array $functions): void\n    {\n        foreach ($functions as $name => $className) {\n            $this->addCustomNumericFunction($name, $className);\n        }\n    }\n\n    /**\n     * Registers a custom DQL function that produces a date/time value.\n     * Such a function can then be used in any DQL statement in any place where date/time\n     * functions are allowed.\n     *\n     * DQL function names are case-insensitive.\n     *\n     * @param string|callable $className Class name or a callable that returns the function.\n     * @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className\n     */\n    public function addCustomDatetimeFunction(string $name, string|callable $className): void\n    {\n        $this->attributes['customDatetimeFunctions'][strtolower($name)] = $className;\n    }\n\n    /**\n     * Gets the implementation class name of a registered custom date/time DQL function.\n     *\n     * @return class-string|callable|null\n     */\n    public function getCustomDatetimeFunction(string $name): string|callable|null\n    {\n        $name = strtolower($name);\n\n        return $this->attributes['customDatetimeFunctions'][$name] ?? null;\n    }\n\n    /**\n     * Sets a map of custom DQL date/time functions.\n     *\n     * Keys must be function names and values the FQCN of the implementing class.\n     * The function names will be case-insensitive in DQL.\n     *\n     * Any previously added date/time functions are discarded.\n     *\n     * @param array $functions The map of custom DQL date/time functions.\n     * @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions\n     */\n    public function setCustomDatetimeFunctions(array $functions): void\n    {\n        foreach ($functions as $name => $className) {\n            $this->addCustomDatetimeFunction($name, $className);\n        }\n    }\n\n    /**\n     * Sets a TypedFieldMapper for php typed fields to DBAL types auto-completion.\n     */\n    public function setTypedFieldMapper(TypedFieldMapper|null $typedFieldMapper): void\n    {\n        $this->attributes['typedFieldMapper'] = $typedFieldMapper;\n    }\n\n    /**\n     * Gets a TypedFieldMapper for php typed fields to DBAL types auto-completion.\n     */\n    public function getTypedFieldMapper(): TypedFieldMapper|null\n    {\n        return $this->attributes['typedFieldMapper'] ?? null;\n    }\n\n    /**\n     * Sets the custom hydrator modes in one pass.\n     *\n     * @param array<string, class-string<AbstractHydrator>> $modes An array of ($modeName => $hydrator).\n     */\n    public function setCustomHydrationModes(array $modes): void\n    {\n        $this->attributes['customHydrationModes'] = [];\n\n        foreach ($modes as $modeName => $hydrator) {\n            $this->addCustomHydrationMode($modeName, $hydrator);\n        }\n    }\n\n    /**\n     * Gets the hydrator class for the given hydration mode name.\n     *\n     * @return class-string<AbstractHydrator>|null\n     */\n    public function getCustomHydrationMode(string $modeName): string|null\n    {\n        return $this->attributes['customHydrationModes'][$modeName] ?? null;\n    }\n\n    /**\n     * Adds a custom hydration mode.\n     *\n     * @param class-string<AbstractHydrator> $hydrator\n     */\n    public function addCustomHydrationMode(string $modeName, string $hydrator): void\n    {\n        $this->attributes['customHydrationModes'][$modeName] = $hydrator;\n    }\n\n    /**\n     * Sets a class metadata factory.\n     *\n     * @param class-string $cmfName\n     */\n    public function setClassMetadataFactoryName(string $cmfName): void\n    {\n        $this->attributes['classMetadataFactoryName'] = $cmfName;\n    }\n\n    /** @return class-string */\n    public function getClassMetadataFactoryName(): string\n    {\n        if (! isset($this->attributes['classMetadataFactoryName'])) {\n            $this->attributes['classMetadataFactoryName'] = ClassMetadataFactory::class;\n        }\n\n        return $this->attributes['classMetadataFactoryName'];\n    }\n\n    /**\n     * Adds a filter to the list of possible filters.\n     *\n     * @param class-string<SQLFilter> $className The class name of the filter.\n     */\n    public function addFilter(string $name, string $className): void\n    {\n        $this->attributes['filters'][$name] = $className;\n    }\n\n    /**\n     * Gets the class name for a given filter name.\n     *\n     * @return class-string<SQLFilter>|null The class name of the filter,\n     *                                      or null if it is not defined.\n     */\n    public function getFilterClassName(string $name): string|null\n    {\n        return $this->attributes['filters'][$name] ?? null;\n    }\n\n    /**\n     * Sets default repository class.\n     *\n     * @param class-string<EntityRepository> $className\n     *\n     * @throws InvalidEntityRepository If $classname is not an ObjectRepository.\n     */\n    public function setDefaultRepositoryClassName(string $className): void\n    {\n        if (! class_exists($className) || ! is_a($className, EntityRepository::class, true)) {\n            throw InvalidEntityRepository::fromClassName($className);\n        }\n\n        $this->attributes['defaultRepositoryClassName'] = $className;\n    }\n\n    /**\n     * Get default repository class.\n     *\n     * @return class-string<EntityRepository>\n     */\n    public function getDefaultRepositoryClassName(): string\n    {\n        return $this->attributes['defaultRepositoryClassName'] ?? EntityRepository::class;\n    }\n\n    /**\n     * Sets naming strategy.\n     */\n    public function setNamingStrategy(NamingStrategy $namingStrategy): void\n    {\n        $this->attributes['namingStrategy'] = $namingStrategy;\n    }\n\n    /**\n     * Gets naming strategy..\n     */\n    public function getNamingStrategy(): NamingStrategy\n    {\n        if (! isset($this->attributes['namingStrategy'])) {\n            $this->attributes['namingStrategy'] = new DefaultNamingStrategy();\n        }\n\n        return $this->attributes['namingStrategy'];\n    }\n\n    /**\n     * Sets quote strategy.\n     */\n    public function setQuoteStrategy(QuoteStrategy $quoteStrategy): void\n    {\n        $this->attributes['quoteStrategy'] = $quoteStrategy;\n    }\n\n    /**\n     * Gets quote strategy.\n     */\n    public function getQuoteStrategy(): QuoteStrategy\n    {\n        if (! isset($this->attributes['quoteStrategy'])) {\n            $this->attributes['quoteStrategy'] = new DefaultQuoteStrategy();\n        }\n\n        return $this->attributes['quoteStrategy'];\n    }\n\n    /**\n     * Set the entity listener resolver.\n     */\n    public function setEntityListenerResolver(EntityListenerResolver $resolver): void\n    {\n        $this->attributes['entityListenerResolver'] = $resolver;\n    }\n\n    /**\n     * Get the entity listener resolver.\n     */\n    public function getEntityListenerResolver(): EntityListenerResolver\n    {\n        if (! isset($this->attributes['entityListenerResolver'])) {\n            $this->attributes['entityListenerResolver'] = new DefaultEntityListenerResolver();\n        }\n\n        return $this->attributes['entityListenerResolver'];\n    }\n\n    /**\n     * Set the entity repository factory.\n     */\n    public function setRepositoryFactory(RepositoryFactory $repositoryFactory): void\n    {\n        $this->attributes['repositoryFactory'] = $repositoryFactory;\n    }\n\n    /**\n     * Get the entity repository factory.\n     */\n    public function getRepositoryFactory(): RepositoryFactory\n    {\n        return $this->attributes['repositoryFactory'] ?? new DefaultRepositoryFactory();\n    }\n\n    public function isSecondLevelCacheEnabled(): bool\n    {\n        return $this->attributes['isSecondLevelCacheEnabled'] ?? false;\n    }\n\n    public function setSecondLevelCacheEnabled(bool $flag = true): void\n    {\n        $this->attributes['isSecondLevelCacheEnabled'] = $flag;\n    }\n\n    public function setSecondLevelCacheConfiguration(CacheConfiguration $cacheConfig): void\n    {\n        $this->attributes['secondLevelCacheConfiguration'] = $cacheConfig;\n    }\n\n    public function getSecondLevelCacheConfiguration(): CacheConfiguration|null\n    {\n        if (! isset($this->attributes['secondLevelCacheConfiguration']) && $this->isSecondLevelCacheEnabled()) {\n            $this->attributes['secondLevelCacheConfiguration'] = new CacheConfiguration();\n        }\n\n        return $this->attributes['secondLevelCacheConfiguration'] ?? null;\n    }\n\n    /**\n     * Returns query hints, which will be applied to every query in application\n     *\n     * @phpstan-return array<string, mixed>\n     */\n    public function getDefaultQueryHints(): array\n    {\n        return $this->attributes['defaultQueryHints'] ?? [];\n    }\n\n    /**\n     * Sets array of query hints, which will be applied to every query in application\n     *\n     * @phpstan-param array<string, mixed> $defaultQueryHints\n     */\n    public function setDefaultQueryHints(array $defaultQueryHints): void\n    {\n        $this->attributes['defaultQueryHints'] = $defaultQueryHints;\n    }\n\n    /**\n     * Gets the value of a default query hint. If the hint name is not recognized, FALSE is returned.\n     *\n     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.\n     */\n    public function getDefaultQueryHint(string $name): mixed\n    {\n        return $this->attributes['defaultQueryHints'][$name] ?? false;\n    }\n\n    /**\n     * Sets a default query hint. If the hint name is not recognized, it is silently ignored.\n     */\n    public function setDefaultQueryHint(string $name, mixed $value): void\n    {\n        $this->attributes['defaultQueryHints'][$name] = $value;\n    }\n\n    /**\n     * Gets a list of entity class names to be ignored by the SchemaTool\n     *\n     * @return list<class-string>\n     */\n    public function getSchemaIgnoreClasses(): array\n    {\n        return $this->attributes['schemaIgnoreClasses'] ?? [];\n    }\n\n    /**\n     * Sets a list of entity class names to be ignored by the SchemaTool\n     *\n     * @param list<class-string> $schemaIgnoreClasses List of entity class names\n     */\n    public function setSchemaIgnoreClasses(array $schemaIgnoreClasses): void\n    {\n        $this->attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses;\n    }\n\n    public function isNativeLazyObjectsEnabled(): bool\n    {\n        return $this->attributes['nativeLazyObjects'] ?? false;\n    }\n\n    public function enableNativeLazyObjects(bool $nativeLazyObjects): void\n    {\n        if (PHP_VERSION_ID >= 80400 && ! $nativeLazyObjects) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Disabling native lazy objects is deprecated and will be impossible in Doctrine ORM 4.0.',\n            );\n        }\n\n        if (PHP_VERSION_ID < 80400 && $nativeLazyObjects) {\n            throw new LogicException('Lazy loading proxies require PHP 8.4 or higher.');\n        }\n\n        $this->attributes['nativeLazyObjects'] = $nativeLazyObjects;\n    }\n\n    /**\n     * @deprecated lazy ghost objects are always enabled\n     *\n     * @return true\n     */\n    public function isLazyGhostObjectEnabled(): bool\n    {\n        return true;\n    }\n\n    /** @deprecated lazy ghost objects cannot be disabled */\n    public function setLazyGhostObjectEnabled(bool $flag): void\n    {\n        if (! $flag) {\n            throw new LogicException(<<<'EXCEPTION'\n            The lazy ghost object feature cannot be disabled anymore.\n            Please remove the call to setLazyGhostObjectEnabled(false).\n            EXCEPTION);\n        }\n    }\n\n    /** @deprecated rejecting ID collisions in the identity map cannot be disabled */\n    public function setRejectIdCollisionInIdentityMap(bool $flag): void\n    {\n        if (! $flag) {\n            throw new LogicException(<<<'EXCEPTION'\n                Rejecting ID collisions in the identity map cannot be disabled anymore.\n                Please remove the call to setRejectIdCollisionInIdentityMap(false).\n                EXCEPTION);\n        }\n    }\n\n    /**\n     * @deprecated rejecting ID collisions in the identity map is always enabled\n     *\n     * @return true\n     */\n    public function isRejectIdCollisionInIdentityMapEnabled(): bool\n    {\n        return true;\n    }\n\n    public function setEagerFetchBatchSize(int $batchSize = 100): void\n    {\n        $this->attributes['fetchModeSubselectBatchSize'] = $batchSize;\n    }\n\n    public function getEagerFetchBatchSize(): int\n    {\n        return $this->attributes['fetchModeSubselectBatchSize'] ?? 100;\n    }\n}\n"
  },
  {
    "path": "src/Decorator/EntityManagerDecorator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Decorator;\n\nuse DateTimeInterface;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\NativeQuery;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\QueryBuilder;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Persistence\\ObjectManagerDecorator;\n\n/**\n * Base class for EntityManager decorators\n *\n * @extends ObjectManagerDecorator<EntityManagerInterface>\n */\nabstract class EntityManagerDecorator extends ObjectManagerDecorator implements EntityManagerInterface\n{\n    public function __construct(EntityManagerInterface $wrapped)\n    {\n        $this->wrapped = $wrapped;\n    }\n\n    public function getRepository(string $className): EntityRepository\n    {\n        return $this->wrapped->getRepository($className);\n    }\n\n    public function getMetadataFactory(): ClassMetadataFactory\n    {\n        return $this->wrapped->getMetadataFactory();\n    }\n\n    public function getClassMetadata(string $className): ClassMetadata\n    {\n        return $this->wrapped->getClassMetadata($className);\n    }\n\n    public function getConnection(): Connection\n    {\n        return $this->wrapped->getConnection();\n    }\n\n    public function getExpressionBuilder(): Expr\n    {\n        return $this->wrapped->getExpressionBuilder();\n    }\n\n    public function beginTransaction(): void\n    {\n        $this->wrapped->beginTransaction();\n    }\n\n    public function wrapInTransaction(callable $func): mixed\n    {\n        return $this->wrapped->wrapInTransaction($func);\n    }\n\n    public function commit(): void\n    {\n        $this->wrapped->commit();\n    }\n\n    public function rollback(): void\n    {\n        $this->wrapped->rollback();\n    }\n\n    public function createQuery(string $dql = ''): Query\n    {\n        return $this->wrapped->createQuery($dql);\n    }\n\n    public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery\n    {\n        return $this->wrapped->createNativeQuery($sql, $rsm);\n    }\n\n    public function createQueryBuilder(): QueryBuilder\n    {\n        return $this->wrapped->createQueryBuilder();\n    }\n\n    public function getReference(string $entityName, mixed $id): object|null\n    {\n        return $this->wrapped->getReference($entityName, $id);\n    }\n\n    public function close(): void\n    {\n        $this->wrapped->close();\n    }\n\n    public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void\n    {\n        $this->wrapped->lock($entity, $lockMode, $lockVersion);\n    }\n\n    public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null\n    {\n        return $this->wrapped->find($className, $id, $lockMode, $lockVersion);\n    }\n\n    public function refresh(object $object, LockMode|int|null $lockMode = null): void\n    {\n        $this->wrapped->refresh($object, $lockMode);\n    }\n\n    public function getEventManager(): EventManager\n    {\n        return $this->wrapped->getEventManager();\n    }\n\n    public function getConfiguration(): Configuration\n    {\n        return $this->wrapped->getConfiguration();\n    }\n\n    public function isOpen(): bool\n    {\n        return $this->wrapped->isOpen();\n    }\n\n    public function getUnitOfWork(): UnitOfWork\n    {\n        return $this->wrapped->getUnitOfWork();\n    }\n\n    public function newHydrator(string|int $hydrationMode): AbstractHydrator\n    {\n        return $this->wrapped->newHydrator($hydrationMode);\n    }\n\n    public function getProxyFactory(): ProxyFactory\n    {\n        return $this->wrapped->getProxyFactory();\n    }\n\n    public function getFilters(): FilterCollection\n    {\n        return $this->wrapped->getFilters();\n    }\n\n    public function isFiltersStateClean(): bool\n    {\n        return $this->wrapped->isFiltersStateClean();\n    }\n\n    public function hasFilters(): bool\n    {\n        return $this->wrapped->hasFilters();\n    }\n\n    public function getCache(): Cache|null\n    {\n        return $this->wrapped->getCache();\n    }\n}\n"
  },
  {
    "path": "src/EntityManager.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse BackedEnum;\nuse DateTimeInterface;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Exception\\EntityManagerClosed;\nuse Doctrine\\ORM\\Exception\\InvalidHydrationMode;\nuse Doctrine\\ORM\\Exception\\MissingIdentifierField;\nuse Doctrine\\ORM\\Exception\\MissingMappingDriverImplementation;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Exception\\UnrecognizedIdentifierFields;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Repository\\RepositoryFactory;\n\nuse function array_keys;\nuse function is_array;\nuse function is_object;\nuse function ltrim;\nuse function method_exists;\n\n/**\n * The EntityManager is the central access point to ORM functionality.\n *\n * It is a facade to all different ORM subsystems such as UnitOfWork,\n * Query Language and Repository API. The quickest way to obtain a fully\n * configured EntityManager is:\n *\n *     use Doctrine\\ORM\\Tools\\ORMSetup;\n *     use Doctrine\\ORM\\EntityManager;\n *\n *     $paths = ['/path/to/entity/mapping/files'];\n *\n *     $config = ORMSetup::createAttributeMetadataConfig($paths);\n *     $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config);\n *     $entityManager = new EntityManager($connection, $config);\n *\n * For more information see\n * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/configuration.html}\n *\n * You should never attempt to inherit from the EntityManager: Inheritance\n * is not a valid extension point for the EntityManager. Instead you\n * should take a look at the {@see \\Doctrine\\ORM\\Decorator\\EntityManagerDecorator}\n * and wrap your entity manager in a decorator.\n *\n * @final\n */\nclass EntityManager implements EntityManagerInterface\n{\n    /**\n     * The metadata factory, used to retrieve the ORM metadata of entity classes.\n     */\n    private ClassMetadataFactory $metadataFactory;\n\n    /**\n     * The UnitOfWork used to coordinate object-level transactions.\n     */\n    private UnitOfWork $unitOfWork;\n\n    /**\n     * The event manager that is the central point of the event system.\n     */\n    private EventManager $eventManager;\n\n    /**\n     * The proxy factory used to create dynamic proxies.\n     */\n    private ProxyFactory $proxyFactory;\n\n    /**\n     * The repository factory used to create dynamic repositories.\n     */\n    private RepositoryFactory $repositoryFactory;\n\n    /**\n     * The expression builder instance used to generate query expressions.\n     */\n    private Expr|null $expressionBuilder = null;\n\n    /**\n     * Whether the EntityManager is closed or not.\n     */\n    private bool $closed = false;\n\n    /**\n     * Collection of query filters.\n     */\n    private FilterCollection|null $filterCollection = null;\n\n    /**\n     * The second level cache regions API.\n     */\n    private Cache|null $cache = null;\n\n    /**\n     * Creates a new EntityManager that operates on the given database connection\n     * and uses the given Configuration and EventManager implementations.\n     *\n     * @param Connection $conn The database connection used by the EntityManager.\n     */\n    public function __construct(\n        private Connection $conn,\n        private Configuration $config,\n        EventManager|null $eventManager = null,\n    ) {\n        if (! $config->getMetadataDriverImpl()) {\n            throw MissingMappingDriverImplementation::create();\n        }\n\n        $this->eventManager = $eventManager\n            ?? (method_exists($conn, 'getEventManager')\n                ? $conn->getEventManager()\n                : new EventManager()\n            );\n\n        $metadataFactoryClassName = $config->getClassMetadataFactoryName();\n\n        $this->metadataFactory = new $metadataFactoryClassName();\n        $this->metadataFactory->setEntityManager($this);\n\n        $this->configureMetadataCache();\n\n        $this->repositoryFactory = $config->getRepositoryFactory();\n        $this->unitOfWork        = new UnitOfWork($this);\n        if ($config->isNativeLazyObjectsEnabled()) {\n            $this->proxyFactory = new ProxyFactory($this);\n        } else {\n            $this->proxyFactory = new ProxyFactory(\n                $this,\n                $config->getProxyDir(),\n                $config->getProxyNamespace(),\n                $config->getAutoGenerateProxyClasses(),\n            );\n        }\n\n        if ($config->isSecondLevelCacheEnabled()) {\n            $cacheConfig  = $config->getSecondLevelCacheConfiguration();\n            $cacheFactory = $cacheConfig->getCacheFactory();\n            $this->cache  = $cacheFactory->createCache($this);\n        }\n    }\n\n    public function getConnection(): Connection\n    {\n        return $this->conn;\n    }\n\n    public function getMetadataFactory(): ClassMetadataFactory\n    {\n        return $this->metadataFactory;\n    }\n\n    public function getExpressionBuilder(): Expr\n    {\n        return $this->expressionBuilder ??= new Expr();\n    }\n\n    public function beginTransaction(): void\n    {\n        $this->conn->beginTransaction();\n    }\n\n    public function getCache(): Cache|null\n    {\n        return $this->cache;\n    }\n\n    public function wrapInTransaction(callable $func): mixed\n    {\n        $this->conn->beginTransaction();\n\n        $successful = false;\n\n        try {\n            $return = $func($this);\n\n            $this->flush();\n            $this->conn->commit();\n\n            $successful = true;\n\n            return $return;\n        } finally {\n            if (! $successful) {\n                $this->close();\n                if ($this->conn->isTransactionActive()) {\n                    $this->conn->rollBack();\n                }\n            }\n        }\n    }\n\n    public function commit(): void\n    {\n        $this->conn->commit();\n    }\n\n    public function rollback(): void\n    {\n        $this->conn->rollBack();\n    }\n\n    /**\n     * Returns the ORM metadata descriptor for a class.\n     *\n     * Internal note: Performance-sensitive method.\n     *\n     * {@inheritDoc}\n     */\n    public function getClassMetadata(string $className): Mapping\\ClassMetadata\n    {\n        return $this->metadataFactory->getMetadataFor($className);\n    }\n\n    public function createQuery(string $dql = ''): Query\n    {\n        $query = new Query($this);\n\n        if (! empty($dql)) {\n            $query->setDQL($dql);\n        }\n\n        return $query;\n    }\n\n    public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery\n    {\n        $query = new NativeQuery($this);\n\n        $query->setSQL($sql);\n        $query->setResultSetMapping($rsm);\n\n        return $query;\n    }\n\n    public function createQueryBuilder(): QueryBuilder\n    {\n        return new QueryBuilder($this);\n    }\n\n    /**\n     * Flushes all changes to objects that have been queued up to now to the database.\n     * This effectively synchronizes the in-memory state of managed objects with the\n     * database.\n     *\n     * If an entity is explicitly passed to this method only this entity and\n     * the cascade-persist semantics + scheduled inserts/removals are synchronized.\n     *\n     * @throws OptimisticLockException If a version check on an entity that\n     * makes use of optimistic locking fails.\n     * @throws ORMException\n     */\n    public function flush(): void\n    {\n        $this->errorIfClosed();\n        $this->unitOfWork->commit();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function find($className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null\n    {\n        $class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\\\'));\n\n        if ($lockMode !== null) {\n            $this->checkLockRequirements($lockMode, $class);\n        }\n\n        if (! is_array($id)) {\n            if ($class->isIdentifierComposite) {\n                throw ORMInvalidArgumentException::invalidCompositeIdentifier();\n            }\n\n            $id = [$class->identifier[0] => $id];\n        }\n\n        foreach ($id as $i => $value) {\n            if (is_object($value)) {\n                $className = DefaultProxyClassNameResolver::getClass($value);\n                if ($this->metadataFactory->hasMetadataFor($className)) {\n                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);\n\n                    if ($id[$i] === null) {\n                        throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($className);\n                    }\n                }\n            }\n        }\n\n        $sortedId = [];\n\n        foreach ($class->identifier as $identifier) {\n            if (! isset($id[$identifier])) {\n                throw MissingIdentifierField::fromFieldAndClass($identifier, $class->name);\n            }\n\n            if ($id[$identifier] instanceof BackedEnum) {\n                $sortedId[$identifier] = $id[$identifier]->value;\n            } else {\n                $sortedId[$identifier] = $id[$identifier];\n            }\n\n            unset($id[$identifier]);\n        }\n\n        if ($id) {\n            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($class->name, array_keys($id));\n        }\n\n        $unitOfWork = $this->getUnitOfWork();\n\n        $entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName);\n\n        // Check identity map first\n        if ($entity !== false) {\n            if (! ($entity instanceof $class->name)) {\n                return null;\n            }\n\n            switch (true) {\n                case $lockMode === LockMode::OPTIMISTIC:\n                    $this->lock($entity, $lockMode, $lockVersion);\n                    break;\n\n                case $lockMode === LockMode::NONE:\n                case $lockMode === LockMode::PESSIMISTIC_READ:\n                case $lockMode === LockMode::PESSIMISTIC_WRITE:\n                    $persister = $unitOfWork->getEntityPersister($class->name);\n                    $persister->refresh($sortedId, $entity, $lockMode);\n                    break;\n            }\n\n            return $entity; // Hit!\n        }\n\n        $persister = $unitOfWork->getEntityPersister($class->name);\n\n        switch (true) {\n            case $lockMode === LockMode::OPTIMISTIC:\n                $entity = $persister->load($sortedId);\n\n                if ($entity !== null) {\n                    $unitOfWork->lock($entity, $lockMode, $lockVersion);\n                }\n\n                return $entity;\n\n            case $lockMode === LockMode::PESSIMISTIC_READ:\n            case $lockMode === LockMode::PESSIMISTIC_WRITE:\n                return $persister->load($sortedId, null, null, [], $lockMode);\n\n            default:\n                return $persister->loadById($sortedId);\n        }\n    }\n\n    public function getReference(string $entityName, mixed $id): object|null\n    {\n        $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\\\'));\n\n        if (! is_array($id)) {\n            $id = [$class->identifier[0] => $id];\n        }\n\n        $sortedId = [];\n\n        foreach ($class->identifier as $identifier) {\n            if (! isset($id[$identifier])) {\n                throw MissingIdentifierField::fromFieldAndClass($identifier, $class->name);\n            }\n\n            $sortedId[$identifier] = $id[$identifier];\n            unset($id[$identifier]);\n        }\n\n        if ($id) {\n            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($class->name, array_keys($id));\n        }\n\n        $entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName);\n\n        // Check identity map first, if its already in there just return it.\n        if ($entity !== false) {\n            return $entity instanceof $class->name ? $entity : null;\n        }\n\n        if ($class->subClasses) {\n            return $this->find($entityName, $sortedId);\n        }\n\n        $entity = $this->proxyFactory->getProxy($class->name, $sortedId);\n\n        $this->unitOfWork->registerManaged($entity, $sortedId, []);\n\n        return $entity;\n    }\n\n    /**\n     * Clears the EntityManager. All entities that are currently managed\n     * by this EntityManager become detached.\n     */\n    public function clear(): void\n    {\n        $this->unitOfWork->clear();\n    }\n\n    public function close(): void\n    {\n        $this->clear();\n\n        $this->closed = true;\n    }\n\n    /**\n     * Tells the EntityManager to make an instance managed and persistent.\n     *\n     * The entity will be entered into the database at or before transaction\n     * commit or as a result of the flush operation.\n     *\n     * NOTE: The persist operation always considers entities that are not yet known to\n     * this EntityManager as NEW. Do not pass detached entities to the persist operation.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws ORMException\n     */\n    public function persist(object $object): void\n    {\n        $this->errorIfClosed();\n\n        $this->unitOfWork->persist($object);\n    }\n\n    /**\n     * Removes an entity instance.\n     *\n     * A removed entity will be removed from the database at or before transaction commit\n     * or as a result of the flush operation.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws ORMException\n     */\n    public function remove(object $object): void\n    {\n        $this->errorIfClosed();\n\n        $this->unitOfWork->remove($object);\n    }\n\n    public function refresh(object $object, LockMode|int|null $lockMode = null): void\n    {\n        $this->errorIfClosed();\n\n        $this->unitOfWork->refresh($object, $lockMode);\n    }\n\n    /**\n     * Detaches an entity from the EntityManager, causing a managed entity to\n     * become detached.  Unflushed changes made to the entity if any\n     * (including removal of the entity), will not be synchronized to the database.\n     * Entities which previously referenced the detached entity will continue to\n     * reference it.\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function detach(object $object): void\n    {\n        $this->unitOfWork->detach($object);\n    }\n\n    public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void\n    {\n        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);\n    }\n\n    /**\n     * Gets the repository for an entity class.\n     *\n     * @param class-string<T> $className The name of the entity.\n     *\n     * @return EntityRepository<T> The repository class.\n     *\n     * @template T of object\n     */\n    public function getRepository(string $className): EntityRepository\n    {\n        return $this->repositoryFactory->getRepository($this, $className);\n    }\n\n    /**\n     * Determines whether an entity instance is managed in this EntityManager.\n     *\n     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.\n     */\n    public function contains(object $object): bool\n    {\n        return $this->unitOfWork->isScheduledForInsert($object)\n            || $this->unitOfWork->isInIdentityMap($object)\n            && ! $this->unitOfWork->isScheduledForDelete($object);\n    }\n\n    public function getEventManager(): EventManager\n    {\n        return $this->eventManager;\n    }\n\n    public function getConfiguration(): Configuration\n    {\n        return $this->config;\n    }\n\n    /**\n     * Throws an exception if the EntityManager is closed or currently not active.\n     *\n     * @throws EntityManagerClosed If the EntityManager is closed.\n     */\n    private function errorIfClosed(): void\n    {\n        if ($this->closed) {\n            throw EntityManagerClosed::create();\n        }\n    }\n\n    public function isOpen(): bool\n    {\n        return ! $this->closed;\n    }\n\n    public function getUnitOfWork(): UnitOfWork\n    {\n        return $this->unitOfWork;\n    }\n\n    public function newHydrator(string|int $hydrationMode): AbstractHydrator\n    {\n        return match ($hydrationMode) {\n            Query::HYDRATE_OBJECT => new Internal\\Hydration\\ObjectHydrator($this),\n            Query::HYDRATE_ARRAY => new Internal\\Hydration\\ArrayHydrator($this),\n            Query::HYDRATE_SCALAR => new Internal\\Hydration\\ScalarHydrator($this),\n            Query::HYDRATE_SINGLE_SCALAR => new Internal\\Hydration\\SingleScalarHydrator($this),\n            Query::HYDRATE_SIMPLEOBJECT => new Internal\\Hydration\\SimpleObjectHydrator($this),\n            Query::HYDRATE_SCALAR_COLUMN => new Internal\\Hydration\\ScalarColumnHydrator($this),\n            default => $this->createCustomHydrator((string) $hydrationMode),\n        };\n    }\n\n    public function getProxyFactory(): ProxyFactory\n    {\n        return $this->proxyFactory;\n    }\n\n    public function initializeObject(object $obj): void\n    {\n        $this->unitOfWork->initializeObject($obj);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function isUninitializedObject($value): bool\n    {\n        return $this->unitOfWork->isUninitializedObject($value);\n    }\n\n    public function getFilters(): FilterCollection\n    {\n        return $this->filterCollection ??= new FilterCollection($this);\n    }\n\n    public function isFiltersStateClean(): bool\n    {\n        return $this->filterCollection === null || $this->filterCollection->isClean();\n    }\n\n    public function hasFilters(): bool\n    {\n        return $this->filterCollection !== null;\n    }\n\n    /**\n     * @phpstan-param LockMode::* $lockMode\n     *\n     * @throws OptimisticLockException\n     * @throws TransactionRequiredException\n     */\n    private function checkLockRequirements(LockMode|int $lockMode, ClassMetadata $class): void\n    {\n        switch ($lockMode) {\n            case LockMode::OPTIMISTIC:\n                if (! $class->isVersioned) {\n                    throw OptimisticLockException::notVersioned($class->name);\n                }\n\n                break;\n            case LockMode::PESSIMISTIC_READ:\n            case LockMode::PESSIMISTIC_WRITE:\n                if (! $this->getConnection()->isTransactionActive()) {\n                    throw TransactionRequiredException::transactionRequired();\n                }\n        }\n    }\n\n    private function configureMetadataCache(): void\n    {\n        $metadataCache = $this->config->getMetadataCache();\n        if (! $metadataCache) {\n            return;\n        }\n\n        $this->metadataFactory->setCache($metadataCache);\n    }\n\n    private function createCustomHydrator(string $hydrationMode): AbstractHydrator\n    {\n        $class = $this->config->getCustomHydrationMode($hydrationMode);\n\n        if ($class !== null) {\n            return new $class($this);\n        }\n\n        throw InvalidHydrationMode::fromMode($hydrationMode);\n    }\n}\n"
  },
  {
    "path": "src/EntityManagerInterface.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse DateTimeInterface;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Persistence\\ObjectManager;\n\ninterface EntityManagerInterface extends ObjectManager\n{\n    /**\n     * {@inheritDoc}\n     *\n     * @param class-string<T> $className\n     *\n     * @return EntityRepository<T>\n     *\n     * @template T of object\n     */\n    public function getRepository(string $className): EntityRepository;\n\n    /**\n     * Returns the cache API for managing the second level cache regions or NULL if the cache is not enabled.\n     */\n    public function getCache(): Cache|null;\n\n    /**\n     * Gets the database connection object used by the EntityManager.\n     */\n    public function getConnection(): Connection;\n\n    public function getMetadataFactory(): ClassMetadataFactory;\n\n    /**\n     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.\n     *\n     * Example:\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder();\n     *     $expr = $em->getExpressionBuilder();\n     *     $qb->select('u')->from('User', 'u')\n     *         ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));\n     * </code>\n     */\n    public function getExpressionBuilder(): Expr;\n\n    /**\n     * Starts a transaction on the underlying database connection.\n     */\n    public function beginTransaction(): void;\n\n    /**\n     * Executes a function in a transaction.\n     *\n     * The function gets passed this EntityManager instance as an (optional) parameter.\n     *\n     * {@link flush} is invoked prior to transaction commit.\n     *\n     * If an exception occurs during execution of the function or flushing or transaction commit,\n     * the transaction is rolled back, the EntityManager closed and the exception re-thrown.\n     *\n     * @phpstan-param callable(self): T $func The function to execute transactionally.\n     *\n     * @return mixed The value returned from the closure.\n     * @phpstan-return T\n     *\n     * @template T\n     */\n    public function wrapInTransaction(callable $func): mixed;\n\n    /**\n     * Commits a transaction on the underlying database connection.\n     */\n    public function commit(): void;\n\n    /**\n     * Performs a rollback on the underlying database connection.\n     */\n    public function rollback(): void;\n\n    /**\n     * Creates a new Query object.\n     *\n     * @param string $dql The DQL string.\n     */\n    public function createQuery(string $dql = ''): Query;\n\n    /**\n     * Creates a native SQL query.\n     */\n    public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery;\n\n    /**\n     * Create a QueryBuilder instance\n     */\n    public function createQueryBuilder(): QueryBuilder;\n\n    /**\n     * Finds an Entity by its identifier.\n     *\n     * @param string            $className   The class name of the entity to find.\n     * @param mixed             $id          The identity of the entity to find.\n     * @param LockMode|int|null $lockMode    One of the \\Doctrine\\DBAL\\LockMode::* constants\n     *                                       or NULL if no specific lock mode should be used\n     *                                       during the search.\n     * @param int|null          $lockVersion The version of the entity to find when using\n     *                                       optimistic locking.\n     * @phpstan-param class-string<T> $className\n     * @phpstan-param LockMode::*|null $lockMode\n     *\n     * @return object|null The entity instance or NULL if the entity can not be found.\n     * @phpstan-return T|null\n     *\n     * @throws OptimisticLockException\n     * @throws ORMInvalidArgumentException\n     * @throws TransactionRequiredException\n     * @throws ORMException\n     *\n     * @template T of object\n     */\n    public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null;\n\n    /**\n     * Refreshes the persistent state of an object from the database,\n     * overriding any local changes that have not yet been persisted.\n     *\n     * @param LockMode|int|null $lockMode One of the \\Doctrine\\DBAL\\LockMode::* constants\n     *                                    or NULL if no specific lock mode should be used\n     *                                    during the search.\n     * @phpstan-param LockMode::*|null $lockMode\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws ORMException\n     * @throws TransactionRequiredException\n     */\n    public function refresh(object $object, LockMode|int|null $lockMode = null): void;\n\n    /**\n     * Gets a reference to the entity identified by the given type and identifier\n     * without actually loading it, if the entity is not yet loaded.\n     *\n     * @param class-string<T> $entityName The name of the entity type.\n     * @param mixed           $id         The entity identifier.\n     *\n     * @return T|null The entity reference.\n     *\n     * @throws ORMException\n     *\n     * @template T of object\n     */\n    public function getReference(string $entityName, mixed $id): object|null;\n\n    /**\n     * Closes the EntityManager. All entities that are currently managed\n     * by this EntityManager become detached. The EntityManager may no longer\n     * be used after it is closed.\n     */\n    public function close(): void;\n\n    /**\n     * Acquire a lock on the given entity.\n     *\n     * @phpstan-param LockMode::* $lockMode\n     *\n     * @throws OptimisticLockException\n     * @throws PessimisticLockException\n     */\n    public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void;\n\n    /**\n     * Gets the EventManager used by the EntityManager.\n     */\n    public function getEventManager(): EventManager;\n\n    /**\n     * Gets the Configuration used by the EntityManager.\n     */\n    public function getConfiguration(): Configuration;\n\n    /**\n     * Check if the Entity manager is open or closed.\n     */\n    public function isOpen(): bool;\n\n    /**\n     * Gets the UnitOfWork used by the EntityManager to coordinate operations.\n     */\n    public function getUnitOfWork(): UnitOfWork;\n\n    /**\n     * Create a new instance for the given hydration mode.\n     *\n     * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode\n     *\n     * @throws ORMException\n     */\n    public function newHydrator(string|int $hydrationMode): AbstractHydrator;\n\n    /**\n     * Gets the proxy factory used by the EntityManager to create entity proxies.\n     */\n    public function getProxyFactory(): ProxyFactory;\n\n    /**\n     * Gets the enabled filters.\n     */\n    public function getFilters(): FilterCollection;\n\n    /**\n     * Checks whether the state of the filter collection is clean.\n     */\n    public function isFiltersStateClean(): bool;\n\n    /**\n     * Checks whether the Entity Manager has filters.\n     */\n    public function hasFilters(): bool;\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param string|class-string<T> $className\n     *\n     * @phpstan-return ($className is class-string<T> ? Mapping\\ClassMetadata<T> : Mapping\\ClassMetadata<object>)\n     *\n     * @phpstan-template T of object\n     */\n    public function getClassMetadata(string $className): Mapping\\ClassMetadata;\n}\n"
  },
  {
    "path": "src/EntityNotFoundException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse RuntimeException;\n\nuse function implode;\nuse function sprintf;\n\n/**\n * Exception thrown when a Proxy fails to retrieve an Entity result.\n */\nclass EntityNotFoundException extends RuntimeException implements ORMException\n{\n    /**\n     * Static constructor.\n     *\n     * @param string[] $id\n     */\n    public static function fromClassNameAndIdentifier(string $className, array $id): self\n    {\n        $ids = [];\n\n        foreach ($id as $key => $value) {\n            $ids[] = $key . '(' . $value . ')';\n        }\n\n        return new self(\n            'Entity of type \\'' . $className . '\\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found',\n        );\n    }\n\n    /**\n     * Instance for which no identifier can be found\n     */\n    public static function noIdentifierFound(string $className): self\n    {\n        return new self(sprintf(\n            'Unable to find \"%s\" entity identifier associated with the UnitOfWork',\n            $className,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/EntityRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse BadMethodCallException;\nuse Doctrine\\Common\\Collections\\AbstractLazyCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\Inflector\\Inflector;\nuse Doctrine\\Inflector\\InflectorFactory;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\ORM\\Repository\\Exception\\InvalidMagicMethodCall;\nuse Doctrine\\Persistence\\ObjectRepository;\n\nuse function array_slice;\nuse function lcfirst;\nuse function sprintf;\nuse function str_starts_with;\nuse function substr;\n\n/**\n * An EntityRepository serves as a repository for entities with generic as well as\n * business specific methods for retrieving entities.\n *\n * This class is designed for inheritance and users can subclass this class to\n * write their own repositories with business-specific methods to locate entities.\n *\n * @template T of object\n * @template-implements Selectable<int,T>\n * @template-implements ObjectRepository<T>\n */\nclass EntityRepository implements ObjectRepository, Selectable\n{\n    /** @var class-string<T> */\n    private readonly string $entityName;\n    private static Inflector|null $inflector = null;\n\n    /** @param ClassMetadata<T> $class */\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        private readonly ClassMetadata $class,\n    ) {\n        $this->entityName = $class->name;\n    }\n\n    /**\n     * Creates a new QueryBuilder instance that is prepopulated for this entity name.\n     */\n    public function createQueryBuilder(string $alias, string|null $indexBy = null): QueryBuilder\n    {\n        return $this->em->createQueryBuilder()\n            ->select($alias)\n            ->from($this->entityName, $alias, $indexBy);\n    }\n\n    /**\n     * Creates a new result set mapping builder for this entity.\n     *\n     * The column naming strategy is \"INCREMENT\".\n     */\n    public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder\n    {\n        $rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addRootEntityFromClassMetadata($this->entityName, $alias);\n\n        return $rsm;\n    }\n\n    /**\n     * Finds an entity by its primary key / identifier.\n     *\n     * @param LockMode|int|null $lockMode One of the \\Doctrine\\DBAL\\LockMode::* constants\n     *                                    or NULL if no specific lock mode should be used\n     *                                    during the search.\n     * @phpstan-param LockMode::*|null $lockMode\n     *\n     * @return object|null The entity instance or NULL if the entity can not be found.\n     * @phpstan-return ?T\n     */\n    public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null\n    {\n        return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);\n    }\n\n    /**\n     * Finds all entities in the repository.\n     *\n     * @phpstan-return list<T> The entities.\n     */\n    public function findAll(): array\n    {\n        return $this->findBy([]);\n    }\n\n    /**\n     * Finds entities by a set of criteria.\n     *\n     * {@inheritDoc}\n     *\n     * @phpstan-return list<T>\n     */\n    public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array\n    {\n        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);\n\n        return $persister->loadAll($criteria, $orderBy, $limit, $offset);\n    }\n\n    /**\n     * Finds a single entity by a set of criteria.\n     *\n     * @phpstan-param array<string, mixed> $criteria\n     * @phpstan-param array<string, string>|null $orderBy\n     *\n     * @phpstan-return T|null\n     */\n    public function findOneBy(array $criteria, array|null $orderBy = null): object|null\n    {\n        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);\n\n        return $persister->load($criteria, null, null, [], null, 1, $orderBy);\n    }\n\n    /**\n     * Counts entities by a set of criteria.\n     *\n     * @phpstan-param array<string, mixed> $criteria\n     *\n     * @return int The cardinality of the objects that match the given criteria.\n     * @phpstan-return 0|positive-int\n     *\n     * @todo Add this method to `ObjectRepository` interface in the next major release\n     */\n    public function count(array $criteria = []): int\n    {\n        return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);\n    }\n\n    /**\n     * Adds support for magic method calls.\n     *\n     * @param mixed[] $arguments\n     * @phpstan-param list<mixed> $arguments\n     *\n     * @throws BadMethodCallException If the method called is invalid.\n     */\n    public function __call(string $method, array $arguments): mixed\n    {\n        if (str_starts_with($method, 'findBy')) {\n            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);\n        }\n\n        if (str_starts_with($method, 'findOneBy')) {\n            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);\n        }\n\n        if (str_starts_with($method, 'countBy')) {\n            return $this->resolveMagicCall('count', substr($method, 7), $arguments);\n        }\n\n        throw new BadMethodCallException(sprintf(\n            'Undefined method \"%s\". The method name must start with ' .\n            'either findBy, findOneBy or countBy!',\n            $method,\n        ));\n    }\n\n    /** @return class-string<T> */\n    protected function getEntityName(): string\n    {\n        return $this->entityName;\n    }\n\n    public function getClassName(): string\n    {\n        return $this->getEntityName();\n    }\n\n    protected function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    /** @phpstan-return ClassMetadata<T> */\n    protected function getClassMetadata(): ClassMetadata\n    {\n        return $this->class;\n    }\n\n    /**\n     * Select all elements from a selectable that match the expression and\n     * return a new collection containing these elements.\n     *\n     * @phpstan-return AbstractLazyCollection<int, T>&Selectable<int, T>\n     */\n    public function matching(Criteria $criteria): AbstractLazyCollection&Selectable\n    {\n        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);\n\n        return new LazyCriteriaCollection($persister, $criteria);\n    }\n\n    /**\n     * Resolves a magic method call to the proper existent method at `EntityRepository`.\n     *\n     * @param string $method The method to call\n     * @param string $by     The property name used as condition\n     * @phpstan-param list<mixed> $arguments The arguments to pass at method call\n     *\n     * @throws InvalidMagicMethodCall If the method called is invalid or the\n     *                                requested field/association does not exist.\n     */\n    private function resolveMagicCall(string $method, string $by, array $arguments): mixed\n    {\n        if (! $arguments) {\n            throw InvalidMagicMethodCall::onMissingParameter($method . $by);\n        }\n\n        self::$inflector ??= InflectorFactory::create()->build();\n\n        $fieldName = lcfirst(self::$inflector->classify($by));\n\n        if (! ($this->class->hasField($fieldName) || $this->class->hasAssociation($fieldName))) {\n            throw InvalidMagicMethodCall::becauseFieldNotFoundIn(\n                $this->entityName,\n                $fieldName,\n                $method . $by,\n            );\n        }\n\n        return $this->$method([$fieldName => $arguments[0]], ...array_slice($arguments, 1));\n    }\n}\n"
  },
  {
    "path": "src/Event/ListenersInvoker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\Common\\EventArgs;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\EntityListenerResolver;\n\n/**\n * A method invoker based on entity lifecycle.\n */\nclass ListenersInvoker\n{\n    final public const INVOKE_NONE      = 0;\n    final public const INVOKE_LISTENERS = 1;\n    final public const INVOKE_CALLBACKS = 2;\n    final public const INVOKE_MANAGER   = 4;\n\n    /** The Entity listener resolver. */\n    private readonly EntityListenerResolver $resolver;\n\n    /** The EventManager used for dispatching events. */\n    private readonly EventManager $eventManager;\n\n    public function __construct(EntityManagerInterface $em)\n    {\n        $this->eventManager = $em->getEventManager();\n        $this->resolver     = $em->getConfiguration()->getEntityListenerResolver();\n    }\n\n    /**\n     * Get the subscribed event systems\n     *\n     * @param ClassMetadata $metadata  The entity metadata.\n     * @param string        $eventName The entity lifecycle event.\n     *\n     * @phpstan-return int-mask-of<self::INVOKE_*> Bitmask of subscribed event systems.\n     */\n    public function getSubscribedSystems(ClassMetadata $metadata, string $eventName): int\n    {\n        $invoke = self::INVOKE_NONE;\n\n        if (isset($metadata->lifecycleCallbacks[$eventName])) {\n            $invoke |= self::INVOKE_CALLBACKS;\n        }\n\n        if (isset($metadata->entityListeners[$eventName])) {\n            $invoke |= self::INVOKE_LISTENERS;\n        }\n\n        if ($this->eventManager->hasListeners($eventName)) {\n            $invoke |= self::INVOKE_MANAGER;\n        }\n\n        return $invoke;\n    }\n\n    /**\n     * Dispatches the lifecycle event of the given entity.\n     *\n     * @param ClassMetadata $metadata  The entity metadata.\n     * @param string        $eventName The entity lifecycle event.\n     * @param object        $entity    The Entity on which the event occurred.\n     * @param EventArgs     $event     The Event args.\n     * @phpstan-param int-mask-of<self::INVOKE_*> $invoke Bitmask to invoke listeners.\n     */\n    public function invoke(\n        ClassMetadata $metadata,\n        string $eventName,\n        object $entity,\n        EventArgs $event,\n        int $invoke,\n    ): void {\n        if ($invoke & self::INVOKE_CALLBACKS) {\n            foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) {\n                $entity->$callback($event);\n            }\n        }\n\n        if ($invoke & self::INVOKE_LISTENERS) {\n            foreach ($metadata->entityListeners[$eventName] as $listener) {\n                $class    = $listener['class'];\n                $method   = $listener['method'];\n                $instance = $this->resolver->resolve($class);\n\n                $instance->$method($entity, $event);\n            }\n        }\n\n        if ($invoke & self::INVOKE_MANAGER) {\n            $this->eventManager->dispatchEvent($eventName, $event);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Event/LoadClassMetadataEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Event\\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs;\n\n/**\n * Class that holds event arguments for a loadMetadata event.\n *\n * @extends BaseLoadClassMetadataEventArgs<ClassMetadata<object>, EntityManagerInterface>\n */\nclass LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs\n{\n    /**\n     * Retrieve associated EntityManager.\n     */\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->getObjectManager();\n    }\n}\n"
  },
  {
    "path": "src/Event/OnClassMetadataNotFoundEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\ManagerEventArgs;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\ObjectManager;\n\n/**\n * Class that holds event arguments for a `onClassMetadataNotFound` event.\n *\n * This object is mutable by design, allowing callbacks having access to it to set the\n * found metadata in it, and therefore \"cancelling\" a `onClassMetadataNotFound` event\n *\n * @extends ManagerEventArgs<EntityManagerInterface>\n */\nclass OnClassMetadataNotFoundEventArgs extends ManagerEventArgs\n{\n    private ClassMetadata|null $foundMetadata = null;\n\n    /** @param EntityManagerInterface $objectManager */\n    public function __construct(\n        private readonly string $className,\n        ObjectManager $objectManager,\n    ) {\n        parent::__construct($objectManager);\n    }\n\n    public function setFoundMetadata(ClassMetadata|null $classMetadata): void\n    {\n        $this->foundMetadata = $classMetadata;\n    }\n\n    public function getFoundMetadata(): ClassMetadata|null\n    {\n        return $this->foundMetadata;\n    }\n\n    /**\n     * Retrieve class name for which a failed metadata fetch attempt was executed\n     */\n    public function getClassName(): string\n    {\n        return $this->className;\n    }\n}\n"
  },
  {
    "path": "src/Event/OnClearEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\OnClearEventArgs as BaseOnClearEventArgs;\n\n/**\n * Provides event arguments for the onClear event.\n *\n * @link        www.doctrine-project.org\n *\n * @extends BaseOnClearEventArgs<EntityManagerInterface>\n */\nclass OnClearEventArgs extends BaseOnClearEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/OnFlushEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\ManagerEventArgs;\n\n/**\n * Provides event arguments for the preFlush event.\n *\n * @link        www.doctrine-project.org\n *\n * @extends ManagerEventArgs<EntityManagerInterface>\n */\nclass OnFlushEventArgs extends ManagerEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PostFlushEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\ManagerEventArgs;\n\n/**\n * Provides event arguments for the postFlush event.\n *\n * @link        www.doctrine-project.org\n *\n * @extends ManagerEventArgs<EntityManagerInterface>\n */\nclass PostFlushEventArgs extends ManagerEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PostLoadEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PostLoadEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PostPersistEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PostPersistEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PostRemoveEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PostRemoveEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PostUpdateEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PostUpdateEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PreFlushEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\ManagerEventArgs;\n\n/**\n * Provides event arguments for the preFlush event.\n *\n * @link        www.doctrine-project.com\n *\n * @extends ManagerEventArgs<EntityManagerInterface>\n */\nclass PreFlushEventArgs extends ManagerEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PrePersistEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PrePersistEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PreRemoveEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\n\n/** @extends LifecycleEventArgs<EntityManagerInterface> */\nfinal class PreRemoveEventArgs extends LifecycleEventArgs\n{\n}\n"
  },
  {
    "path": "src/Event/PreUpdateEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Event;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\nuse InvalidArgumentException;\n\nuse function get_debug_type;\nuse function sprintf;\n\n/**\n * Class that holds event arguments for a preUpdate event.\n *\n * @extends LifecycleEventArgs<EntityManagerInterface>\n */\nclass PreUpdateEventArgs extends LifecycleEventArgs\n{\n    /** @var array<string, array{mixed, mixed}|PersistentCollection> */\n    private array $entityChangeSet;\n\n    /**\n     * @param mixed[][] $changeSet\n     * @phpstan-param array<string, array{mixed, mixed}|PersistentCollection> $changeSet\n     */\n    public function __construct(object $entity, EntityManagerInterface $em, array &$changeSet)\n    {\n        parent::__construct($entity, $em);\n\n        $this->entityChangeSet = &$changeSet;\n    }\n\n    /**\n     * Retrieves entity changeset.\n     *\n     * @return mixed[][]\n     * @phpstan-return array<string, array{mixed, mixed}|PersistentCollection>\n     */\n    public function getEntityChangeSet(): array\n    {\n        return $this->entityChangeSet;\n    }\n\n    /**\n     * Checks if field has a changeset.\n     */\n    public function hasChangedField(string $field): bool\n    {\n        return isset($this->entityChangeSet[$field]);\n    }\n\n    /**\n     * Gets the old value of the changeset of the changed field.\n     */\n    public function getOldValue(string $field): mixed\n    {\n        $this->assertValidField($field);\n\n        return $this->entityChangeSet[$field][0];\n    }\n\n    /**\n     * Gets the new value of the changeset of the changed field.\n     */\n    public function getNewValue(string $field): mixed\n    {\n        $this->assertValidField($field);\n\n        return $this->entityChangeSet[$field][1];\n    }\n\n    /**\n     * Sets the new value of this field.\n     */\n    public function setNewValue(string $field, mixed $value): void\n    {\n        $this->assertValidField($field);\n\n        $this->entityChangeSet[$field][1] = $value;\n    }\n\n    /**\n     * Asserts the field exists in changeset.\n     *\n     * @throws InvalidArgumentException\n     */\n    private function assertValidField(string $field): void\n    {\n        if (! isset($this->entityChangeSet[$field])) {\n            throw new InvalidArgumentException(sprintf(\n                'Field \"%s\" is not a valid field of the entity \"%s\" in PreUpdateEventArgs.',\n                $field,\n                get_debug_type($this->getObject()),\n            ));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Events.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\n/**\n * Container for all ORM events.\n *\n * This class cannot be instantiated.\n */\nfinal class Events\n{\n    /**\n     * Private constructor. This class is not meant to be instantiated.\n     */\n    private function __construct()\n    {\n    }\n\n    /**\n     * The preRemove event occurs for a given entity before the respective\n     * EntityManager remove operation for that entity is executed.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const preRemove = 'preRemove';\n\n    /**\n     * The postRemove event occurs for an entity after the entity has\n     * been deleted. It will be invoked after the database delete operations.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const postRemove = 'postRemove';\n\n    /**\n     * The prePersist event occurs for a given entity before the respective\n     * EntityManager persist operation for that entity is executed.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const prePersist = 'prePersist';\n\n    /**\n     * The postPersist event occurs for an entity after the entity has\n     * been made persistent. It will be invoked after the database insert operations.\n     * Generated primary key values are available in the postPersist event.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const postPersist = 'postPersist';\n\n    /**\n     * The preUpdate event occurs before the database update operations to\n     * entity data.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const preUpdate = 'preUpdate';\n\n    /**\n     * The postUpdate event occurs after the database update operations to\n     * entity data.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const postUpdate = 'postUpdate';\n\n    /**\n     * The postLoad event occurs for an entity after the entity has been loaded\n     * into the current EntityManager from the database or after the refresh operation\n     * has been applied to it.\n     *\n     * Note that the postLoad event occurs for an entity before any associations have been\n     * initialized. Therefore, it is not safe to access associations in a postLoad callback\n     * or event handler.\n     *\n     * This is an entity lifecycle event.\n     */\n    public const postLoad = 'postLoad';\n\n    /**\n     * The loadClassMetadata event occurs after the mapping metadata for a class\n     * has been loaded from a mapping source (attributes/xml).\n     */\n    public const loadClassMetadata = 'loadClassMetadata';\n\n    /**\n     * The onClassMetadataNotFound event occurs whenever loading metadata for a class\n     * failed.\n     */\n    public const onClassMetadataNotFound = 'onClassMetadataNotFound';\n\n    /**\n     * The preFlush event occurs when the EntityManager#flush() operation is invoked,\n     * but before any changes to managed entities have been calculated. This event is\n     * always raised right after EntityManager#flush() call.\n     */\n    public const preFlush = 'preFlush';\n\n    /**\n     * The onFlush event occurs when the EntityManager#flush() operation is invoked,\n     * after any changes to managed entities have been determined but before any\n     * actual database operations are executed. The event is only raised if there is\n     * actually something to do for the underlying UnitOfWork.\n     */\n    public const onFlush = 'onFlush';\n\n    /**\n     * The postFlush event occurs when the EntityManager#flush() operation is invoked and\n     * after all actual database operations are executed successfully. The event is only raised if there is\n     * actually something to do for the underlying UnitOfWork. The event won't be raised if an error occurs during the\n     * flush operation.\n     */\n    public const postFlush = 'postFlush';\n\n    /**\n     * The onClear event occurs when the EntityManager#clear() operation is invoked,\n     * after all references to entities have been removed from the unit of work.\n     */\n    public const onClear = 'onClear';\n}\n"
  },
  {
    "path": "src/Exception/ConfigurationException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\ninterface ConfigurationException extends ORMException\n{\n}\n"
  },
  {
    "path": "src/Exception/DuplicateFieldException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function sprintf;\n\nclass DuplicateFieldException extends LogicException implements ORMException\n{\n    public static function create(string $argName, string $columnName): self\n    {\n        return new self(sprintf('Name \"%s\" for \"%s\" already in use.', $argName, $columnName));\n    }\n}\n"
  },
  {
    "path": "src/Exception/EntityIdentityCollisionException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Exception;\n\nuse function sprintf;\n\nfinal class EntityIdentityCollisionException extends Exception implements ORMException\n{\n    public static function create(object $existingEntity, object $newEntity, string $idHash): self\n    {\n        return new self(\n            sprintf(\n                <<<'EXCEPTION'\nWhile adding an entity of class %s with an ID hash of \"%s\" to the identity map,\nanother object of class %s was already present for the same ID. This exception\nis a safeguard against an internal inconsistency - IDs should uniquely map to\nentity object instances. This problem may occur if:\n\n- you use application-provided IDs and reuse ID values;\n- database-provided IDs are reassigned after truncating the database without\nclearing the EntityManager;\n- you might have been using EntityManager#getReference() to create a reference\nfor a nonexistent ID that was subsequently (by the RDBMS) assigned to another\nentity.\n\nOtherwise, it might be an ORM-internal inconsistency, please report it.\nEXCEPTION\n                ,\n                $newEntity::class,\n                $idHash,\n                $existingEntity::class,\n            ),\n        );\n    }\n}\n"
  },
  {
    "path": "src/Exception/EntityManagerClosed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse RuntimeException;\n\nfinal class EntityManagerClosed extends RuntimeException implements ManagerException\n{\n    public static function create(): self\n    {\n        return new self('The EntityManager is closed.');\n    }\n}\n"
  },
  {
    "path": "src/Exception/EntityMissingAssignedId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function get_debug_type;\n\nfinal class EntityMissingAssignedId extends LogicException implements ORMException\n{\n    public static function forField(object $entity, string $field): self\n    {\n        return new self('Entity of type ' . get_debug_type($entity) . \" is missing an assigned ID for field  '\" . $field . \"'. \" .\n            'The identifier generation strategy for this entity requires the ID field to be populated before ' .\n            'EntityManager#persist() is called. If you want automatically generated identifiers instead ' .\n            'you need to adjust the metadata mapping accordingly.');\n    }\n}\n"
  },
  {
    "path": "src/Exception/InvalidEntityRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Doctrine\\ORM\\EntityRepository;\nuse LogicException;\n\nfinal class InvalidEntityRepository extends LogicException implements ConfigurationException\n{\n    public static function fromClassName(string $className): self\n    {\n        return new self(\n            \"Invalid repository class '\" . $className . \"'. It must be a \" . EntityRepository::class . '.',\n        );\n    }\n}\n"
  },
  {
    "path": "src/Exception/InvalidHydrationMode.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function sprintf;\n\nfinal class InvalidHydrationMode extends LogicException implements ManagerException\n{\n    public static function fromMode(string $mode): self\n    {\n        return new self(sprintf('\"%s\" is an invalid hydration mode.', $mode));\n    }\n}\n"
  },
  {
    "path": "src/Exception/ManagerException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Throwable;\n\ninterface ManagerException extends Throwable\n{\n}\n"
  },
  {
    "path": "src/Exception/MissingIdentifierField.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function sprintf;\n\nfinal class MissingIdentifierField extends LogicException implements ManagerException\n{\n    public static function fromFieldAndClass(string $fieldName, string $className): self\n    {\n        return new self(sprintf(\n            'The identifier %s is missing for a query of %s',\n            $fieldName,\n            $className,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Exception/MissingMappingDriverImplementation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nfinal class MissingMappingDriverImplementation extends LogicException implements ManagerException\n{\n    public static function create(): self\n    {\n        return new self(\n            \"It's a requirement to specify a Metadata Driver and pass it \" .\n            'to Doctrine\\\\ORM\\\\Configuration::setMetadataDriverImpl().',\n        );\n    }\n}\n"
  },
  {
    "path": "src/Exception/MultipleSelectorsFoundException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function implode;\nuse function sprintf;\n\nfinal class MultipleSelectorsFoundException extends LogicException implements ORMException\n{\n    public const MULTIPLE_SELECTORS_FOUND_EXCEPTION = 'Multiple selectors found: %s. Please select only one.';\n\n    /** @param string[] $selectors */\n    public static function create(array $selectors): self\n    {\n        return new self(\n            sprintf(\n                self::MULTIPLE_SELECTORS_FOUND_EXCEPTION,\n                implode(', ', $selectors),\n            ),\n        );\n    }\n}\n"
  },
  {
    "path": "src/Exception/NoMatchingPropertyException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function sprintf;\n\nclass NoMatchingPropertyException extends LogicException implements ORMException\n{\n    public static function create(string $property): self\n    {\n        return new self(sprintf('Column name \"%s\" does not match any property name. Consider aliasing it to the name of an existing property.', $property));\n    }\n}\n"
  },
  {
    "path": "src/Exception/NotSupported.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function sprintf;\n\n/** @deprecated */\nfinal class NotSupported extends LogicException implements ORMException\n{\n    public static function create(): self\n    {\n        return new self('This behaviour is (currently) not supported by Doctrine 2');\n    }\n\n    public static function createForDbal3(string $context): self\n    {\n        return new self(sprintf(\n            <<<'EXCEPTION'\nContext: %s\nProblem: Feature was deprecated in doctrine/dbal 2.x and is not supported by installed doctrine/dbal:3.x\nSolution: See the doctrine/deprecations logs for new alternative approaches.\nEXCEPTION\n            ,\n            $context,\n        ));\n    }\n\n    public static function createForPersistence3(string $context): self\n    {\n        return new self(sprintf(\n            <<<'EXCEPTION'\nContext: %s\nProblem: Feature was deprecated in doctrine/persistence 2.x and is not supported by installed doctrine/persistence:3.x\nSolution: See the doctrine/deprecations logs for new alternative approaches.\nEXCEPTION\n            ,\n            $context,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Exception/ORMException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Throwable;\n\ninterface ORMException extends Throwable\n{\n}\n"
  },
  {
    "path": "src/Exception/PersisterException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Doctrine\\ORM\\Persisters\\PersisterException as BasePersisterException;\n\nclass PersisterException extends BasePersisterException\n{\n}\n"
  },
  {
    "path": "src/Exception/RepositoryException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\n/**\n * This interface should be implemented by all exceptions in the Repository\n * namespace.\n */\ninterface RepositoryException extends ORMException\n{\n}\n"
  },
  {
    "path": "src/Exception/SchemaToolException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Throwable;\n\ninterface SchemaToolException extends Throwable\n{\n}\n"
  },
  {
    "path": "src/Exception/UnexpectedAssociationValue.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\n\nuse function sprintf;\n\nfinal class UnexpectedAssociationValue extends CacheException\n{\n    public static function create(\n        string $class,\n        string $association,\n        string $given,\n        string $expected,\n    ): self {\n        return new self(sprintf(\n            'Found entity of type %s on association %s#%s, but expecting %s',\n            $given,\n            $class,\n            $association,\n            $expected,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Exception/UnrecognizedIdentifierFields.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Exception;\n\nuse LogicException;\n\nuse function implode;\nuse function sprintf;\n\nfinal class UnrecognizedIdentifierFields extends LogicException implements ManagerException\n{\n    /** @param string[] $fieldNames */\n    public static function fromClassAndFieldNames(string $className, array $fieldNames): self\n    {\n        return new self(sprintf(\n            'Unrecognized identifier fields: \"%s\" are not present on class \"%s\".',\n            implode(\"', '\", $fieldNames),\n            $className,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Id/AbstractIdGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Id;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\nabstract class AbstractIdGenerator\n{\n    /**\n     * Generates an identifier for an entity.\n     */\n    abstract public function generateId(EntityManagerInterface $em, object|null $entity): mixed;\n\n    /**\n     * Gets whether this generator is a post-insert generator which means that\n     * {@link generateId()} must be called after the entity has been inserted\n     * into the database.\n     *\n     * By default, this method returns FALSE. Generators that have this requirement\n     * must override this method and return TRUE.\n     */\n    public function isPostInsertGenerator(): bool\n    {\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/Id/AssignedGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Id;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\EntityMissingAssignedId;\n\n/**\n * Special generator for application-assigned identifiers (doesn't really generate anything).\n */\nclass AssignedGenerator extends AbstractIdGenerator\n{\n    /**\n     * Returns the identifier assigned to the given entity.\n     *\n     * {@inheritDoc}\n     *\n     * @throws EntityMissingAssignedId\n     */\n    public function generateId(EntityManagerInterface $em, object|null $entity): array\n    {\n        $class      = $em->getClassMetadata($entity::class);\n        $idFields   = $class->getIdentifierFieldNames();\n        $identifier = [];\n\n        foreach ($idFields as $idField) {\n            $value = $class->getFieldValue($entity, $idField);\n\n            if (! isset($value)) {\n                throw EntityMissingAssignedId::forField($entity, $idField);\n            }\n\n            if (isset($class->associationMappings[$idField])) {\n                // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.\n                $value = $em->getUnitOfWork()->getSingleIdentifierValue($value);\n            }\n\n            $identifier[$idField] = $value;\n        }\n\n        return $identifier;\n    }\n}\n"
  },
  {
    "path": "src/Id/BigIntegerIdentityGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Id;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\n/**\n * Id generator that obtains IDs from special \"identity\" columns. These are columns\n * that automatically get a database-generated, auto-incremented identifier on INSERT.\n * This generator obtains the last insert id after such an insert.\n */\nclass BigIntegerIdentityGenerator extends AbstractIdGenerator\n{\n    public function generateId(EntityManagerInterface $em, object|null $entity): string\n    {\n        return (string) $em->getConnection()->lastInsertId();\n    }\n\n    public function isPostInsertGenerator(): bool\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/Id/IdentityGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Id;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\n/**\n * Id generator that obtains IDs from special \"identity\" columns. These are columns\n * that automatically get a database-generated, auto-incremented identifier on INSERT.\n * This generator obtains the last insert id after such an insert.\n */\nclass IdentityGenerator extends AbstractIdGenerator\n{\n    public function generateId(EntityManagerInterface $em, object|null $entity): int\n    {\n        return (int) $em->getConnection()->lastInsertId();\n    }\n\n    public function isPostInsertGenerator(): bool\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/Id/SequenceGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Id;\n\nuse Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Serializable;\n\nuse function serialize;\nuse function unserialize;\n\n/**\n * Represents an ID generator that uses a database sequence.\n */\nclass SequenceGenerator extends AbstractIdGenerator implements Serializable\n{\n    private int $nextValue     = 0;\n    private int|null $maxValue = null;\n\n    /**\n     * Initializes a new sequence generator.\n     *\n     * @param string $sequenceName   The name of the sequence.\n     * @param int    $allocationSize The allocation size of the sequence.\n     */\n    public function __construct(\n        private string $sequenceName,\n        private int $allocationSize,\n    ) {\n    }\n\n    public function generateId(EntityManagerInterface $em, object|null $entity): int\n    {\n        if ($this->maxValue === null || $this->nextValue === $this->maxValue) {\n            // Allocate new values\n            $connection = $em->getConnection();\n            $sql        = $connection->getDatabasePlatform()->getSequenceNextValSQL($this->sequenceName);\n\n            if ($connection instanceof PrimaryReadReplicaConnection) {\n                $connection->ensureConnectedToPrimary();\n            }\n\n            $this->nextValue = (int) $connection->fetchOne($sql);\n            $this->maxValue  = $this->nextValue + $this->allocationSize;\n        }\n\n        return $this->nextValue++;\n    }\n\n    /**\n     * Gets the maximum value of the currently allocated bag of values.\n     */\n    public function getCurrentMaxValue(): int|null\n    {\n        return $this->maxValue;\n    }\n\n    /**\n     * Gets the next value that will be returned by generate().\n     */\n    public function getNextValue(): int\n    {\n        return $this->nextValue;\n    }\n\n    /** @deprecated without replacement. */\n    final public function serialize(): string\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11468',\n            '%s() is deprecated, use __serialize() instead. %s won\\'t implement the Serializable interface anymore in ORM 4.',\n            __METHOD__,\n            self::class,\n        );\n\n        return serialize($this->__serialize());\n    }\n\n    /** @return array<string, mixed> */\n    public function __serialize(): array\n    {\n        return [\n            'allocationSize' => $this->allocationSize,\n            'sequenceName' => $this->sequenceName,\n        ];\n    }\n\n    /** @deprecated without replacement. */\n    final public function unserialize(string $serialized): void\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11468',\n            '%s() is deprecated, use __unserialize() instead. %s won\\'t implement the Serializable interface anymore in ORM 4.',\n            __METHOD__,\n            self::class,\n        );\n\n        $this->__unserialize(unserialize($serialized));\n    }\n\n    /** @param array<string, mixed> $data */\n    public function __unserialize(array $data): void\n    {\n        $this->sequenceName   = $data['sequenceName'];\n        $this->allocationSize = $data['allocationSize'];\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/AbstractHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse BackedEnum;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryWalker;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Generator;\nuse LogicException;\nuse ReflectionClass;\nuse ReflectionEnum;\n\nuse function array_key_exists;\nuse function array_keys;\nuse function array_map;\nuse function array_merge;\nuse function count;\nuse function current;\nuse function end;\nuse function in_array;\nuse function is_array;\nuse function is_object;\nuse function ksort;\n\n/**\n * Base class for all hydrators. A hydrator is a class that provides some form\n * of transformation of an SQL result set into another structure.\n *\n * @phpstan-consistent-constructor\n */\nabstract class AbstractHydrator\n{\n    /**\n     * The ResultSetMapping.\n     */\n    protected ResultSetMapping|null $rsm = null;\n\n    /**\n     * The dbms Platform instance.\n     */\n    protected AbstractPlatform $platform;\n\n    /**\n     * The UnitOfWork of the associated EntityManager.\n     */\n    protected UnitOfWork $uow;\n\n    /**\n     * Local ClassMetadata cache to avoid going to the EntityManager all the time.\n     *\n     * @var array<string, ClassMetadata<object>>\n     */\n    protected array $metadataCache = [];\n\n    /**\n     * The cache used during row-by-row hydration.\n     *\n     * @var array<string, mixed[]|null>\n     */\n    protected array $cache = [];\n\n    /**\n     * The statement that provides the data to hydrate.\n     */\n    protected Result|null $stmt = null;\n\n    /**\n     * The query hints.\n     *\n     * @var array<string, mixed>\n     */\n    protected array $hints = [];\n\n    /**\n     * Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.\n     */\n    public function __construct(protected EntityManagerInterface $em)\n    {\n        $this->platform = $em->getConnection()->getDatabasePlatform();\n        $this->uow      = $em->getUnitOfWork();\n    }\n\n    /**\n     * Initiates a row-by-row hydration.\n     *\n     * @phpstan-param array<string, mixed> $hints\n     *\n     * @return Generator<array-key, mixed>\n     *\n     * @final\n     */\n    final public function toIterable(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): Generator\n    {\n        $this->stmt  = $stmt;\n        $this->rsm   = $resultSetMapping;\n        $this->hints = $hints;\n\n        $evm = $this->em->getEventManager();\n\n        $evm->addEventListener([Events::onClear], $this);\n\n        $this->prepare();\n\n        try {\n            while (true) {\n                $row = $this->statement()->fetchAssociative();\n\n                if ($row === false) {\n                    break;\n                }\n\n                $result = [];\n\n                $this->hydrateRowData($row, $result);\n\n                $this->cleanupAfterRowIteration();\n                if (count($result) === 1) {\n                    if (count($resultSetMapping->indexByMap) === 0) {\n                        yield end($result);\n                    } else {\n                        yield from $result;\n                    }\n                } elseif (is_object(current($result))) {\n                    yield $result;\n                } else {\n                    yield array_merge(...$result);\n                }\n            }\n        } finally {\n            $this->cleanup();\n        }\n    }\n\n    final protected function statement(): Result\n    {\n        if ($this->stmt === null) {\n            throw new LogicException('Uninitialized _stmt property');\n        }\n\n        return $this->stmt;\n    }\n\n    final protected function resultSetMapping(): ResultSetMapping\n    {\n        if ($this->rsm === null) {\n            throw new LogicException('Uninitialized _rsm property');\n        }\n\n        return $this->rsm;\n    }\n\n    /**\n     * Hydrates all rows returned by the passed statement instance at once.\n     *\n     * @phpstan-param array<string, string> $hints\n     */\n    public function hydrateAll(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): mixed\n    {\n        $this->stmt  = $stmt;\n        $this->rsm   = $resultSetMapping;\n        $this->hints = $hints;\n\n        $this->em->getEventManager()->addEventListener([Events::onClear], $this);\n        $this->prepare();\n\n        try {\n            $result = $this->hydrateAllData();\n        } finally {\n            $this->cleanup();\n        }\n\n        return $result;\n    }\n\n    /**\n     * When executed in a hydrate() loop we have to clear internal state to\n     * decrease memory consumption.\n     */\n    public function onClear(mixed $eventArgs): void\n    {\n    }\n\n    /**\n     * Executes one-time preparation tasks, once each time hydration is started\n     * through {@link hydrateAll} or {@link toIterable()}.\n     */\n    protected function prepare(): void\n    {\n    }\n\n    /**\n     * Executes one-time cleanup tasks at the end of a hydration that was initiated\n     * through {@link hydrateAll} or {@link toIterable()}.\n     */\n    protected function cleanup(): void\n    {\n        $this->statement()->free();\n\n        $this->stmt          = null;\n        $this->rsm           = null;\n        $this->cache         = [];\n        $this->metadataCache = [];\n\n        $this\n            ->em\n            ->getEventManager()\n            ->removeEventListener([Events::onClear], $this);\n    }\n\n    protected function cleanupAfterRowIteration(): void\n    {\n    }\n\n    /**\n     * Hydrates a single row from the current statement instance.\n     *\n     * Template method.\n     *\n     * @param mixed[] $row    The row data.\n     * @param mixed[] $result The result to fill.\n     *\n     * @throws HydrationException\n     */\n    protected function hydrateRowData(array $row, array &$result): void\n    {\n        throw new HydrationException('hydrateRowData() not implemented by this hydrator.');\n    }\n\n    /**\n     * Hydrates all rows from the current statement instance at once.\n     */\n    abstract protected function hydrateAllData(): mixed;\n\n    /**\n     * Processes a row of the result set.\n     *\n     * Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).\n     * Puts the elements of a result row into a new array, grouped by the dql alias\n     * they belong to. The column names in the result set are mapped to their\n     * field names during this procedure as well as any necessary conversions on\n     * the values applied. Scalar values are kept in a specific key 'scalars'.\n     *\n     * @param mixed[] $data SQL Result Row.\n     * @phpstan-param array<string, string> $id                 Dql-Alias => ID-Hash.\n     * @phpstan-param array<string, bool>   $nonemptyComponents Does this DQL-Alias has at least one non NULL value?\n     *\n     * @return array<string, array<string, mixed>> An array with all the fields\n     *                                             (name => value) of the data\n     *                                             row, grouped by their\n     *                                             component alias.\n     * @phpstan-return array{\n     *                   data: array<array-key, array>,\n     *                   newObjects?: array<array-key, array{\n     *                       class: ReflectionClass,\n     *                       args: array,\n     *                       obj: object\n     *                   }>,\n     *                   scalars?: array\n     *               }\n     */\n    protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents): array\n    {\n        $rowData = ['data' => [], 'newObjects' => []];\n\n        foreach ($this->rsm->newObjectMappings as $mapping) {\n            if (! array_key_exists($mapping['objIndex'], $this->rsm->newObject)) {\n                $this->rsm->newObject[$mapping['objIndex']] = $mapping['className'];\n            }\n        }\n\n        foreach ($this->rsm->newObject as $objIndex => $newObject) {\n            $rowData['newObjects'][$objIndex]['class'] = new ReflectionClass($newObject);\n            $rowData['newObjects'][$objIndex]['args']  = [];\n        }\n\n        foreach ($data as $key => $value) {\n            $cacheKeyInfo = $this->hydrateColumnInfo($key);\n            if ($cacheKeyInfo === null) {\n                continue;\n            }\n\n            $fieldName = $cacheKeyInfo['fieldName'];\n\n            switch (true) {\n                case isset($cacheKeyInfo['isNewObjectParameter']):\n                    $argIndex = $cacheKeyInfo['argIndex'];\n                    $objIndex = $cacheKeyInfo['objIndex'];\n                    $type     = $cacheKeyInfo['type'];\n                    $value    = $type->convertToPHPValue($value, $this->platform);\n\n                    if ($value !== null && isset($cacheKeyInfo['enumType'])) {\n                        $value = $this->buildEnum($value, $cacheKeyInfo['enumType']);\n                    }\n\n                    $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value;\n                    break;\n\n                case isset($cacheKeyInfo['isScalar']):\n                    $type  = $cacheKeyInfo['type'];\n                    $value = $type->convertToPHPValue($value, $this->platform);\n\n                    if ($value !== null && isset($cacheKeyInfo['enumType'])) {\n                        $value = $this->buildEnum($value, $cacheKeyInfo['enumType']);\n                    }\n\n                    $rowData['scalars'][$fieldName] = $value;\n\n                    break;\n\n                //case (isset($cacheKeyInfo['isMetaColumn'])):\n                default:\n                    $dqlAlias = $cacheKeyInfo['dqlAlias'];\n                    $type     = $cacheKeyInfo['type'];\n\n                    // If there are field name collisions in the child class, then we need\n                    // to only hydrate if we are looking at the correct discriminator value\n                    if (\n                        isset($cacheKeyInfo['discriminatorColumn'], $data[$cacheKeyInfo['discriminatorColumn']])\n                        && ! in_array((string) $data[$cacheKeyInfo['discriminatorColumn']], $cacheKeyInfo['discriminatorValues'], true)\n                    ) {\n                        break;\n                    }\n\n                    // in an inheritance hierarchy the same field could be defined several times.\n                    // We overwrite this value so long we don't have a non-null value, that value we keep.\n                    // Per definition it cannot be that a field is defined several times and has several values.\n                    if (isset($rowData['data'][$dqlAlias][$fieldName])) {\n                        break;\n                    }\n\n                    $rowData['data'][$dqlAlias][$fieldName] = $type\n                        ? $type->convertToPHPValue($value, $this->platform)\n                        : $value;\n\n                    if ($rowData['data'][$dqlAlias][$fieldName] !== null && isset($cacheKeyInfo['enumType'])) {\n                        $rowData['data'][$dqlAlias][$fieldName] = $this->buildEnum($rowData['data'][$dqlAlias][$fieldName], $cacheKeyInfo['enumType']);\n                    }\n\n                    if ($cacheKeyInfo['isIdentifier'] && $value !== null) {\n                        $id[$dqlAlias]                .= '|' . $value;\n                        $nonemptyComponents[$dqlAlias] = true;\n                    }\n\n                    break;\n            }\n        }\n\n        $nestedEntities = [];\n        /**@var string $argAlias */\n        foreach ($this->resultSetMapping()->nestedNewObjectArguments as ['ownerIndex' => $ownerIndex, 'argIndex' => $argIndex, 'argAlias' => $argAlias]) {\n            if (array_key_exists($argAlias, $rowData['newObjects'])) {\n                ksort($rowData['newObjects'][$argAlias]['args']);\n                $rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['newObjects'][$argAlias]['class']->newInstanceArgs($rowData['newObjects'][$argAlias]['args']);\n                unset($rowData['newObjects'][$argAlias]);\n            } elseif (array_key_exists($argAlias, $rowData['data'])) {\n                if (! array_key_exists($argAlias, $nestedEntities)) {\n                    $nestedEntities[$argAlias]  = '';\n                    $rowData['data'][$argAlias] = $this->hydrateNestedEntity($rowData['data'][$argAlias], $argAlias);\n                }\n\n                $rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['data'][$argAlias];\n            } else {\n                throw new LogicException($argAlias . ' does not exist');\n            }\n        }\n\n        foreach (array_keys($nestedEntities) as $entity) {\n            unset($rowData['data'][$entity]);\n        }\n\n        foreach ($rowData['newObjects'] as $objIndex => $newObject) {\n            ksort($rowData['newObjects'][$objIndex]['args']);\n            $obj = $rowData['newObjects'][$objIndex]['class']->newInstanceArgs($rowData['newObjects'][$objIndex]['args']);\n\n            $rowData['newObjects'][$objIndex]['obj'] = $obj;\n        }\n\n        return $rowData;\n    }\n\n    /** @param mixed[] $data pre-hydrated SQL Result Row. */\n    protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed\n    {\n        return $data;\n    }\n\n    /**\n     * Processes a row of the result set.\n     *\n     * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that\n     * simply converts column names to field names and properly converts the\n     * values according to their types. The resulting row has the same number\n     * of elements as before.\n     *\n     * @param mixed[] $data\n     * @phpstan-param array<string, mixed> $data\n     *\n     * @return mixed[] The processed row.\n     * @phpstan-return array<string, mixed>\n     */\n    protected function gatherScalarRowData(array &$data): array\n    {\n        $rowData = [];\n\n        foreach ($data as $key => $value) {\n            $cacheKeyInfo = $this->hydrateColumnInfo($key);\n            if ($cacheKeyInfo === null) {\n                continue;\n            }\n\n            $fieldName = $cacheKeyInfo['fieldName'];\n\n            // WARNING: BC break! We know this is the desired behavior to type convert values, but this\n            // erroneous behavior exists since 2.0 and we're forced to keep compatibility.\n            if (! isset($cacheKeyInfo['isScalar'])) {\n                $type  = $cacheKeyInfo['type'];\n                $value = $type ? $type->convertToPHPValue($value, $this->platform) : $value;\n\n                $fieldName = $cacheKeyInfo['dqlAlias'] . '_' . $fieldName;\n            }\n\n            $rowData[$fieldName] = $value;\n        }\n\n        return $rowData;\n    }\n\n    /**\n     * Retrieve column information from ResultSetMapping.\n     *\n     * @param string $key Column name\n     *\n     * @return mixed[]|null\n     * @phpstan-return array<string, mixed>|null\n     */\n    protected function hydrateColumnInfo(string $key): array|null\n    {\n        if (isset($this->cache[$key])) {\n            return $this->cache[$key];\n        }\n\n        switch (true) {\n            // NOTE: Most of the times it's a field mapping, so keep it first!!!\n            case isset($this->rsm->fieldMappings[$key]):\n                $classMetadata = $this->getClassMetadata($this->rsm->declaringClasses[$key]);\n                $fieldName     = $this->rsm->fieldMappings[$key];\n                $fieldMapping  = $classMetadata->fieldMappings[$fieldName];\n                $ownerMap      = $this->rsm->columnOwnerMap[$key];\n                $columnInfo    = [\n                    'isIdentifier' => in_array($fieldName, $classMetadata->identifier, true),\n                    'fieldName'    => $fieldName,\n                    'type'         => Type::getType($fieldMapping->type),\n                    'dqlAlias'     => $ownerMap,\n                    'enumType'     => $this->rsm->enumMappings[$key] ?? null,\n                ];\n\n                // the current discriminator value must be saved in order to disambiguate fields hydration,\n                // should there be field name collisions\n                if ($classMetadata->parentClasses && isset($this->rsm->discriminatorColumns[$ownerMap])) {\n                    return $this->cache[$key] = array_merge(\n                        $columnInfo,\n                        [\n                            'discriminatorColumn' => $this->rsm->discriminatorColumns[$ownerMap],\n                            'discriminatorValue'  => $classMetadata->discriminatorValue,\n                            'discriminatorValues' => $this->getDiscriminatorValues($classMetadata),\n                        ],\n                    );\n                }\n\n                return $this->cache[$key] = $columnInfo;\n\n            case isset($this->rsm->newObjectMappings[$key]):\n                // WARNING: A NEW object is also a scalar, so it must be declared before!\n                $mapping = $this->rsm->newObjectMappings[$key];\n\n                return $this->cache[$key] = [\n                    'isScalar'             => true,\n                    'isNewObjectParameter' => true,\n                    'fieldName'            => $this->rsm->scalarMappings[$key],\n                    'type'                 => Type::getType($this->rsm->typeMappings[$key]),\n                    'argIndex'             => $mapping['argIndex'],\n                    'objIndex'             => $mapping['objIndex'],\n                    'enumType'             => $this->rsm->enumMappings[$key] ?? null,\n                ];\n\n            case isset($this->rsm->scalarMappings[$key], $this->hints[LimitSubqueryWalker::FORCE_DBAL_TYPE_CONVERSION]):\n                return $this->cache[$key] = [\n                    'fieldName' => $this->rsm->scalarMappings[$key],\n                    'type'      => Type::getType($this->rsm->typeMappings[$key]),\n                    'dqlAlias'  => '',\n                    'enumType'  => $this->rsm->enumMappings[$key] ?? null,\n                ];\n\n            case isset($this->rsm->scalarMappings[$key]):\n                return $this->cache[$key] = [\n                    'isScalar'  => true,\n                    'fieldName' => $this->rsm->scalarMappings[$key],\n                    'type'      => Type::getType($this->rsm->typeMappings[$key]),\n                    'enumType'  => $this->rsm->enumMappings[$key] ?? null,\n                ];\n\n            case isset($this->rsm->metaMappings[$key]):\n                // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).\n                $fieldName = $this->rsm->metaMappings[$key];\n                $dqlAlias  = $this->rsm->columnOwnerMap[$key];\n                $type      = isset($this->rsm->typeMappings[$key])\n                    ? Type::getType($this->rsm->typeMappings[$key])\n                    : null;\n\n                // Cache metadata fetch\n                $this->getClassMetadata($this->rsm->aliasMap[$dqlAlias]);\n\n                return $this->cache[$key] = [\n                    'isIdentifier' => isset($this->rsm->isIdentifierColumn[$dqlAlias][$key]),\n                    'isMetaColumn' => true,\n                    'fieldName'    => $fieldName,\n                    'type'         => $type,\n                    'dqlAlias'     => $dqlAlias,\n                    'enumType'     => $this->rsm->enumMappings[$key] ?? null,\n                ];\n        }\n\n        // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2\n        // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.\n        return null;\n    }\n\n    /**\n     * @return string[]\n     * @phpstan-return non-empty-list<string>\n     */\n    private function getDiscriminatorValues(ClassMetadata $classMetadata): array\n    {\n        $values = array_map(\n            fn (string $subClass): string => (string) $this->getClassMetadata($subClass)->discriminatorValue,\n            $classMetadata->subClasses,\n        );\n\n        $values[] = (string) $classMetadata->discriminatorValue;\n\n        return $values;\n    }\n\n    /**\n     * Retrieve ClassMetadata associated to entity class name.\n     */\n    protected function getClassMetadata(string $className): ClassMetadata\n    {\n        if (! isset($this->metadataCache[$className])) {\n            $this->metadataCache[$className] = $this->em->getClassMetadata($className);\n        }\n\n        return $this->metadataCache[$className];\n    }\n\n    /**\n     * Register entity as managed in UnitOfWork.\n     *\n     * @param mixed[] $data\n     *\n     * @todo The \"$id\" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow\n     */\n    protected function registerManaged(ClassMetadata $class, object $entity, array $data): void\n    {\n        if ($class->isIdentifierComposite) {\n            $id = [];\n\n            foreach ($class->identifier as $fieldName) {\n                $id[$fieldName] = isset($class->associationMappings[$fieldName]) && $class->associationMappings[$fieldName]->isToOneOwningSide()\n                    ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name]\n                    : $data[$fieldName];\n            }\n        } else {\n            $fieldName = $class->identifier[0];\n            $id        = [\n                $fieldName => isset($class->associationMappings[$fieldName]) && $class->associationMappings[$fieldName]->isToOneOwningSide()\n                    ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name]\n                    : $data[$fieldName],\n            ];\n        }\n\n        $this->em->getUnitOfWork()->registerManaged($entity, $id, $data);\n    }\n\n    /**\n     * @param class-string<BackedEnum> $enumType\n     *\n     * @return BackedEnum|array<BackedEnum>\n     */\n    final protected function buildEnum(mixed $value, string $enumType): BackedEnum|array\n    {\n        $reflection  = new ReflectionEnum($enumType);\n        $isIntBacked = $reflection->isBacked() && $reflection->getBackingType()->getName() === 'int';\n\n        if (is_array($value)) {\n            return array_map(\n                static fn ($value) => $enumType::from($isIntBacked ? (int) $value : $value),\n                $value,\n            );\n        }\n\n        $value = $isIntBacked ? (int) $value : $value;\n\n        return $enumType::from($value);\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/ArrayHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse function array_key_last;\nuse function count;\nuse function is_array;\nuse function key;\nuse function reset;\n\n/**\n * The ArrayHydrator produces a nested array \"graph\" that is often (not always)\n * interchangeable with the corresponding object graph for read-only access.\n */\nclass ArrayHydrator extends AbstractHydrator\n{\n    /** @var array<string,bool> */\n    private array $rootAliases = [];\n\n    private bool $isSimpleQuery = false;\n\n    /** @var mixed[] */\n    private array $identifierMap = [];\n\n    /** @var mixed[] */\n    private array $resultPointers = [];\n\n    /** @var array<string,string> */\n    private array $idTemplate = [];\n\n    private int $resultCounter = 0;\n\n    protected function prepare(): void\n    {\n        $this->isSimpleQuery = count($this->resultSetMapping()->aliasMap) <= 1;\n\n        foreach ($this->resultSetMapping()->aliasMap as $dqlAlias => $className) {\n            $this->identifierMap[$dqlAlias]  = [];\n            $this->resultPointers[$dqlAlias] = [];\n            $this->idTemplate[$dqlAlias]     = '';\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateAllData(): array\n    {\n        $result = [];\n\n        while ($data = $this->statement()->fetchAssociative()) {\n            $this->hydrateRowData($data, $result);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateRowData(array $row, array &$result): void\n    {\n        // 1) Initialize\n        $id                 = $this->idTemplate; // initialize the id-memory\n        $nonemptyComponents = [];\n        $rowData            = $this->gatherRowData($row, $id, $nonemptyComponents);\n\n        // 2) Now hydrate the data found in the current row.\n        foreach ($rowData['data'] as $dqlAlias => $data) {\n            $index = false;\n\n            if (isset($this->resultSetMapping()->parentAliasMap[$dqlAlias])) {\n                // It's a joined result\n\n                $parent = $this->resultSetMapping()->parentAliasMap[$dqlAlias];\n                $path   = $parent . '.' . $dqlAlias;\n\n                // missing parent data, skipping as RIGHT JOIN hydration is not supported.\n                if (! isset($nonemptyComponents[$parent])) {\n                    continue;\n                }\n\n                // Get a reference to the right element in the result tree.\n                // This element will get the associated element attached.\n                if ($this->resultSetMapping()->isMixed && isset($this->rootAliases[$parent])) {\n                    $first = reset($this->resultPointers);\n                    // TODO: Exception if $key === null ?\n                    $baseElement =& $this->resultPointers[$parent][key($first)];\n                } elseif (isset($this->resultPointers[$parent])) {\n                    $baseElement =& $this->resultPointers[$parent];\n                } else {\n                    unset($this->resultPointers[$dqlAlias]); // Ticket #1228\n\n                    continue;\n                }\n\n                $relationAlias = $this->resultSetMapping()->relationMap[$dqlAlias];\n                $parentClass   = $this->metadataCache[$this->resultSetMapping()->aliasMap[$parent]];\n                $relation      = $parentClass->associationMappings[$relationAlias];\n\n                // Check the type of the relation (many or single-valued)\n                if (! $relation->isToOne()) {\n                    $oneToOne = false;\n\n                    if (! isset($baseElement[$relationAlias])) {\n                        $baseElement[$relationAlias] = [];\n                    }\n\n                    if (isset($nonemptyComponents[$dqlAlias])) {\n                        $indexExists  = isset($this->identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);\n                        $index        = $indexExists ? $this->identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;\n                        $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;\n\n                        if (! $indexExists || ! $indexIsValid) {\n                            $element = $data;\n\n                            if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {\n                                $baseElement[$relationAlias][$row[$this->resultSetMapping()->indexByMap[$dqlAlias]]] = $element;\n                            } else {\n                                $baseElement[$relationAlias][] = $element;\n                            }\n\n                            $this->identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = array_key_last($baseElement[$relationAlias]);\n                        }\n                    }\n                } else {\n                    $oneToOne = true;\n\n                    if (\n                        ! isset($nonemptyComponents[$dqlAlias]) &&\n                        ( ! isset($baseElement[$relationAlias]))\n                    ) {\n                        $baseElement[$relationAlias] = null;\n                    } elseif (! isset($baseElement[$relationAlias])) {\n                        $baseElement[$relationAlias] = $data;\n                    }\n                }\n\n                $coll =& $baseElement[$relationAlias];\n\n                if (is_array($coll)) {\n                    $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);\n                }\n            } else {\n                // It's a root result element\n\n                $this->rootAliases[$dqlAlias] = true; // Mark as root\n                $entityKey                    = $this->resultSetMapping()->entityMappings[$dqlAlias] ?: 0;\n\n                // if this row has a NULL value for the root result id then make it a null result.\n                if (! isset($nonemptyComponents[$dqlAlias])) {\n                    $result[] = $this->resultSetMapping()->isMixed\n                        ? [$entityKey => null]\n                        : null;\n\n                    $resultKey = $this->resultCounter;\n                    ++$this->resultCounter;\n\n                    continue;\n                }\n\n                // Check for an existing element\n                if ($this->isSimpleQuery || ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {\n                    $element = $this->resultSetMapping()->isMixed\n                        ? [$entityKey => $data]\n                        : $data;\n\n                    if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {\n                        $resultKey          = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];\n                        $result[$resultKey] = $element;\n                    } else {\n                        $resultKey = $this->resultCounter;\n                        $result[]  = $element;\n\n                        ++$this->resultCounter;\n                    }\n\n                    $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;\n                } else {\n                    $index     = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];\n                    $resultKey = $index;\n                }\n\n                $this->updateResultPointer($result, $index, $dqlAlias, false);\n            }\n        }\n\n        if (! isset($resultKey)) {\n            $this->resultCounter++;\n        }\n\n        // Append scalar values to mixed result sets\n        if (isset($rowData['scalars'])) {\n            if (! isset($resultKey)) {\n                // this only ever happens when no object is fetched (scalar result only)\n                $resultKey = isset($this->resultSetMapping()->indexByMap['scalars'])\n                    ? $row[$this->resultSetMapping()->indexByMap['scalars']]\n                    : $this->resultCounter - 1;\n            }\n\n            foreach ($rowData['scalars'] as $name => $value) {\n                $result[$resultKey][$name] = $value;\n            }\n        }\n\n        // Append new object to mixed result sets\n        if (isset($rowData['newObjects'])) {\n            if (! isset($resultKey)) {\n                $resultKey = $this->resultCounter - 1;\n            }\n\n            $scalarCount = (isset($rowData['scalars']) ? count($rowData['scalars']) : 0);\n\n            foreach ($rowData['newObjects'] as $objIndex => $newObject) {\n                $args = $newObject['args'];\n                $obj  = $newObject['obj'];\n\n                if (count($args) === $scalarCount || ($scalarCount === 0 && count($rowData['newObjects']) === 1)) {\n                    $result[$resultKey] = $obj;\n\n                    continue;\n                }\n\n                $result[$resultKey][$objIndex] = $obj;\n            }\n        }\n    }\n\n    /**\n     * Updates the result pointer for an Entity. The result pointers point to the\n     * last seen instance of each Entity type. This is used for graph construction.\n     *\n     * @param mixed[]|null     $coll     The element.\n     * @param string|int|false $index    Index of the element in the collection.\n     * @param bool             $oneToOne Whether it is a single-valued association or not.\n     */\n    private function updateResultPointer(\n        array|null &$coll,\n        string|int|false $index,\n        string $dqlAlias,\n        bool $oneToOne,\n    ): void {\n        if ($coll === null) {\n            unset($this->resultPointers[$dqlAlias]); // Ticket #1228\n\n            return;\n        }\n\n        if ($oneToOne) {\n            $this->resultPointers[$dqlAlias] =& $coll;\n\n            return;\n        }\n\n        if ($index !== false) {\n            $this->resultPointers[$dqlAlias] =& $coll[$index];\n\n            return;\n        }\n\n        if (! $coll) {\n            return;\n        }\n\n        $this->resultPointers[$dqlAlias] =& $coll[array_key_last($coll)];\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/HydrationException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Exception;\n\nuse function implode;\nuse function sprintf;\n\nclass HydrationException extends Exception implements ORMException\n{\n    public static function nonUniqueResult(): self\n    {\n        return new self('The result returned by the query was not unique.');\n    }\n\n    public static function parentObjectOfRelationNotFound(string $alias, string $parentAlias): self\n    {\n        return new self(sprintf(\n            \"The parent object of entity result with alias '%s' was not found.\"\n            . \" The parent alias is '%s'.\",\n            $alias,\n            $parentAlias,\n        ));\n    }\n\n    public static function emptyDiscriminatorValue(string $dqlAlias): self\n    {\n        return new self(\"The DQL alias '\" . $dqlAlias . \"' contains an entity \" .\n            'of an inheritance hierarchy with an empty discriminator value. This means ' .\n            'that the database contains inconsistent data with an empty ' .\n            'discriminator value in a table row.');\n    }\n\n    public static function missingDiscriminatorColumn(string $entityName, string $discrColumnName, string $dqlAlias): self\n    {\n        return new self(sprintf(\n            'The discriminator column \"%s\" is missing for \"%s\" using the DQL alias \"%s\".',\n            $discrColumnName,\n            $entityName,\n            $dqlAlias,\n        ));\n    }\n\n    public static function missingDiscriminatorMetaMappingColumn(string $entityName, string $discrColumnName, string $dqlAlias): self\n    {\n        return new self(sprintf(\n            'The meta mapping for the discriminator column \"%s\" is missing for \"%s\" using the DQL alias \"%s\".',\n            $discrColumnName,\n            $entityName,\n            $dqlAlias,\n        ));\n    }\n\n    /** @param list<int|string> $discrValues */\n    public static function invalidDiscriminatorValue(string $discrValue, array $discrValues): self\n    {\n        return new self(sprintf(\n            'The discriminator value \"%s\" is invalid. It must be one of \"%s\".',\n            $discrValue,\n            implode('\", \"', $discrValues),\n        ));\n    }\n\n    public static function partialObjectHydrationDisallowed(): self\n    {\n        return new self('Hydration of entity objects is not allowed when DQL PARTIAL keyword is used.');\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/ObjectHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse BackedEnum;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function array_fill_keys;\nuse function array_keys;\nuse function array_map;\nuse function assert;\nuse function count;\nuse function is_array;\nuse function key;\nuse function ltrim;\nuse function spl_object_id;\n\n/**\n * The ObjectHydrator constructs an object graph out of an SQL result set.\n *\n * Internal note: Highly performance-sensitive code.\n */\nclass ObjectHydrator extends AbstractHydrator\n{\n    /** @var mixed[] */\n    private array $identifierMap = [];\n\n    /** @var mixed[] */\n    private array $resultPointers = [];\n\n    /** @var mixed[] */\n    private array $idTemplate = [];\n\n    private int $resultCounter = 0;\n\n    /** @var mixed[] */\n    private array $rootAliases = [];\n\n    /** @var mixed[] */\n    private array $initializedCollections = [];\n\n    /** @var array<string, PersistentCollection> */\n    private array $uninitializedCollections = [];\n\n    /** @var mixed[] */\n    private array $existingCollections = [];\n\n    protected function prepare(): void\n    {\n        if (! isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {\n            $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;\n        }\n\n        foreach ($this->resultSetMapping()->aliasMap as $dqlAlias => $className) {\n            $this->identifierMap[$dqlAlias] = [];\n            $this->idTemplate[$dqlAlias]    = '';\n\n            // Remember which associations are \"fetch joined\", so that we know where to inject\n            // collection stubs or proxies and where not.\n            if (! isset($this->resultSetMapping()->relationMap[$dqlAlias])) {\n                continue;\n            }\n\n            $parent = $this->resultSetMapping()->parentAliasMap[$dqlAlias];\n\n            if (! isset($this->resultSetMapping()->aliasMap[$parent])) {\n                if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {\n                    continue;\n                }\n\n                throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);\n            }\n\n            $sourceClassName = $this->resultSetMapping()->aliasMap[$parent];\n            $sourceClass     = $this->getClassMetadata($sourceClassName);\n            $assoc           = $sourceClass->associationMappings[$this->resultSetMapping()->relationMap[$dqlAlias]];\n\n            $this->hints['fetched'][$parent][$assoc->fieldName] = true;\n\n            if ($assoc->isManyToMany()) {\n                continue;\n            }\n\n            // Mark any non-collection opposite sides as fetched, too.\n            if (! $assoc->isOwningSide()) {\n                $this->hints['fetched'][$dqlAlias][$assoc->mappedBy] = true;\n\n                continue;\n            }\n\n            // handle fetch-joined owning side bi-directional one-to-one associations\n            if ($assoc->inversedBy !== null) {\n                $class        = $this->getClassMetadata($className);\n                $inverseAssoc = $class->associationMappings[$assoc->inversedBy];\n\n                if (! $inverseAssoc->isToOne()) {\n                    continue;\n                }\n\n                $this->hints['fetched'][$dqlAlias][$inverseAssoc->fieldName] = true;\n            }\n        }\n    }\n\n    protected function cleanup(): void\n    {\n        $eagerLoad = isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD]) && $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] === true;\n\n        parent::cleanup();\n\n        $this->identifierMap            =\n        $this->initializedCollections   =\n        $this->uninitializedCollections =\n        $this->existingCollections      =\n        $this->resultPointers           = [];\n\n        if ($eagerLoad) {\n            $this->uow->triggerEagerLoads();\n        }\n\n        $this->uow->hydrationComplete();\n    }\n\n    protected function cleanupAfterRowIteration(): void\n    {\n        $this->identifierMap            =\n        $this->initializedCollections   =\n        $this->uninitializedCollections =\n        $this->existingCollections      =\n        $this->resultPointers           = [];\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateAllData(): array\n    {\n        $result = [];\n\n        while ($row = $this->statement()->fetchAssociative()) {\n            $this->hydrateRowData($row, $result);\n        }\n\n        // Take snapshots from all newly initialized collections\n        foreach ($this->initializedCollections as $coll) {\n            $coll->takeSnapshot();\n        }\n\n        foreach ($this->uninitializedCollections as $coll) {\n            if (! $coll->isInitialized()) {\n                $coll->setInitialized(true);\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Initializes a related collection.\n     *\n     * @param string $fieldName      The name of the field on the entity that holds the collection.\n     * @param string $parentDqlAlias Alias of the parent fetch joining this collection.\n     */\n    private function initRelatedCollection(\n        object $entity,\n        ClassMetadata $class,\n        string $fieldName,\n        string $parentDqlAlias,\n    ): PersistentCollection {\n        $oid      = spl_object_id($entity);\n        $relation = $class->associationMappings[$fieldName];\n        $value    = $class->propertyAccessors[$fieldName]->getValue($entity);\n\n        if ($value === null || is_array($value)) {\n            $value = new ArrayCollection((array) $value);\n        }\n\n        if (! $value instanceof PersistentCollection) {\n            assert($relation->isToMany());\n            $value = new PersistentCollection(\n                $this->em,\n                $this->metadataCache[$relation->targetEntity],\n                $value,\n            );\n            $value->setOwner($entity, $relation);\n\n            $class->propertyAccessors[$fieldName]->setValue($entity, $value);\n            $this->uow->setOriginalEntityProperty($oid, $fieldName, $value);\n\n            $this->initializedCollections[$oid . $fieldName] = $value;\n        } elseif (\n            isset($this->hints[Query::HINT_REFRESH]) ||\n            isset($this->hints['fetched'][$parentDqlAlias][$fieldName]) &&\n             ! $value->isInitialized()\n        ) {\n            // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!\n            $value->setDirty(false);\n            $value->setInitialized(true);\n            $value->unwrap()->clear();\n\n            $this->initializedCollections[$oid . $fieldName] = $value;\n        } else {\n            // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!\n            $this->existingCollections[$oid . $fieldName] = $value;\n        }\n\n        return $value;\n    }\n\n    /**\n     * Gets an entity instance.\n     *\n     * @param string $dqlAlias The DQL alias of the entity's class.\n     * @phpstan-param array<string, mixed> $data     The instance data.\n     *\n     * @throws HydrationException\n     */\n    private function getEntity(array $data, string $dqlAlias): object\n    {\n        $className = $this->resultSetMapping()->aliasMap[$dqlAlias];\n\n        if (isset($this->resultSetMapping()->discriminatorColumns[$dqlAlias])) {\n            $fieldName = $this->resultSetMapping()->discriminatorColumns[$dqlAlias];\n\n            if (! isset($this->resultSetMapping()->metaMappings[$fieldName])) {\n                throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias);\n            }\n\n            $discrColumn = $this->resultSetMapping()->metaMappings[$fieldName];\n\n            if (! isset($data[$discrColumn])) {\n                throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);\n            }\n\n            if ($data[$discrColumn] === '') {\n                throw HydrationException::emptyDiscriminatorValue($dqlAlias);\n            }\n\n            $discrMap           = $this->metadataCache[$className]->discriminatorMap;\n            $discriminatorValue = $data[$discrColumn];\n            if ($discriminatorValue instanceof BackedEnum) {\n                $discriminatorValue = $discriminatorValue->value;\n            }\n\n            $discriminatorValue = (string) $discriminatorValue;\n\n            if (! isset($discrMap[$discriminatorValue])) {\n                throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap));\n            }\n\n            $className = $discrMap[$discriminatorValue];\n\n            unset($data[$discrColumn]);\n        }\n\n        if (isset($this->hints[Query::HINT_REFRESH_ENTITY], $this->rootAliases[$dqlAlias])) {\n            $this->registerManaged($this->metadataCache[$className], $this->hints[Query::HINT_REFRESH_ENTITY], $data);\n        }\n\n        $this->hints['fetchAlias'] = $dqlAlias;\n\n        return $this->uow->createEntity($className, $data, $this->hints);\n    }\n\n    /**\n     * @param class-string $className\n     * @phpstan-param array<string, mixed> $data\n     */\n    private function getEntityFromIdentityMap(string $className, array $data): object|bool\n    {\n        // TODO: Abstract this code and UnitOfWork::createEntity() equivalent?\n        $class = $this->metadataCache[$className];\n\n        if ($class->isIdentifierComposite) {\n            $idHash = UnitOfWork::getIdHashByIdentifier(\n                array_map(\n                    /** @return mixed */\n                    static fn (string $fieldName) => isset($class->associationMappings[$fieldName]) && assert($class->associationMappings[$fieldName]->isToOneOwningSide())\n                        ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name]\n                        : $data[$fieldName],\n                    $class->identifier,\n                ),\n            );\n\n            return $this->uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName);\n        } elseif (isset($class->associationMappings[$class->identifier[0]])) {\n            $association = $class->associationMappings[$class->identifier[0]];\n            assert($association->isToOneOwningSide());\n\n            return $this->uow->tryGetByIdHash($data[$association->joinColumns[0]->name], $class->rootEntityName);\n        }\n\n        return $this->uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);\n    }\n\n    /**\n     * Hydrates a single row in an SQL result set.\n     *\n     * @internal\n     * First, the data of the row is split into chunks where each chunk contains data\n     * that belongs to a particular component/class. Afterwards, all these chunks\n     * are processed, one after the other. For each chunk of class data only one of the\n     * following code paths is executed:\n     * Path A: The data chunk belongs to a joined/associated object and the association\n     *         is collection-valued.\n     * Path B: The data chunk belongs to a joined/associated object and the association\n     *         is single-valued.\n     * Path C: The data chunk belongs to a root result element/object that appears in the topmost\n     *         level of the hydrated result. A typical example are the objects of the type\n     *         specified by the FROM clause in a DQL query.\n     *\n     * @param mixed[] $row    The data of the row to process.\n     * @param mixed[] $result The result array to fill.\n     */\n    protected function hydrateRowData(array $row, array &$result): void\n    {\n        // Initialize\n        $id                 = $this->idTemplate; // initialize the id-memory\n        $nonemptyComponents = [];\n        // Split the row data into chunks of class data.\n        $rowData = $this->gatherRowData($row, $id, $nonemptyComponents);\n\n        // reset result pointers for each data row\n        $this->resultPointers = [];\n\n        // Hydrate the data chunks\n        foreach ($rowData['data'] as $dqlAlias => $data) {\n            $entityName = $this->resultSetMapping()->aliasMap[$dqlAlias];\n\n            if (isset($this->resultSetMapping()->parentAliasMap[$dqlAlias])) {\n                // It's a joined result\n\n                $parentAlias = $this->resultSetMapping()->parentAliasMap[$dqlAlias];\n                // we need the $path to save into the identifier map which entities were already\n                // seen for this parent-child relationship\n                $path = $parentAlias . '.' . $dqlAlias;\n\n                // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs\n                if (! isset($nonemptyComponents[$parentAlias])) {\n                    // TODO: Add special case code where we hydrate the right join objects into identity map at least\n                    continue;\n                }\n\n                $parentClass   = $this->metadataCache[$this->resultSetMapping()->aliasMap[$parentAlias]];\n                $relationField = $this->resultSetMapping()->relationMap[$dqlAlias];\n                $relation      = $parentClass->associationMappings[$relationField];\n                $reflField     = $parentClass->propertyAccessors[$relationField];\n\n                // Get a reference to the parent object to which the joined element belongs.\n                if ($this->resultSetMapping()->isMixed && isset($this->rootAliases[$parentAlias])) {\n                    $objectClass  = $this->resultPointers[$parentAlias];\n                    $parentObject = $objectClass[key($objectClass)];\n                } elseif (isset($this->resultPointers[$parentAlias])) {\n                    $parentObject = $this->resultPointers[$parentAlias];\n                } else {\n                    // Parent object of relation not found, mark as not-fetched again\n                    if (isset($nonemptyComponents[$dqlAlias])) {\n                        $element = $this->getEntity($data, $dqlAlias);\n\n                        // Update result pointer and provide initial fetch data for parent\n                        $this->resultPointers[$dqlAlias]               = $element;\n                        $rowData['data'][$parentAlias][$relationField] = $element;\n                    } else {\n                        $element = null;\n                    }\n\n                    // Mark as not-fetched again\n                    unset($this->hints['fetched'][$parentAlias][$relationField]);\n                    continue;\n                }\n\n                $oid = spl_object_id($parentObject);\n\n                // Check the type of the relation (many or single-valued)\n                if (! $relation->isToOne()) {\n                    // PATH A: Collection-valued association\n                    $reflFieldValue = $reflField->getValue($parentObject);\n\n                    if (isset($nonemptyComponents[$dqlAlias])) {\n                        $collKey = $oid . $relationField;\n                        if (isset($this->initializedCollections[$collKey])) {\n                            $reflFieldValue = $this->initializedCollections[$collKey];\n                        } elseif (! isset($this->existingCollections[$collKey])) {\n                            $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);\n                        }\n\n                        $indexExists  = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);\n                        $index        = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;\n                        $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;\n\n                        if (! $indexExists || ! $indexIsValid) {\n                            if (isset($this->existingCollections[$collKey])) {\n                                // Collection exists, only look for the element in the identity map.\n                                $element = $this->getEntityFromIdentityMap($entityName, $data);\n                                if ($element) {\n                                    $this->resultPointers[$dqlAlias] = $element;\n                                } else {\n                                    unset($this->resultPointers[$dqlAlias]);\n                                }\n                            } else {\n                                $element = $this->getEntity($data, $dqlAlias);\n\n                                if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {\n                                    $indexValue = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];\n                                    $reflFieldValue->hydrateSet($indexValue, $element);\n                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;\n                                } else {\n                                    if (! $reflFieldValue->contains($element)) {\n                                        $reflFieldValue->hydrateAdd($element);\n                                        $reflFieldValue->last();\n                                    }\n\n                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();\n                                }\n\n                                // Update result pointer\n                                $this->resultPointers[$dqlAlias] = $element;\n                            }\n                        } else {\n                            // Update result pointer\n                            $this->resultPointers[$dqlAlias] = $reflFieldValue[$index];\n                        }\n                    } elseif (! $reflFieldValue) {\n                        $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);\n                    } elseif ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false && ! isset($this->uninitializedCollections[$oid . $relationField])) {\n                        $this->uninitializedCollections[$oid . $relationField] = $reflFieldValue;\n                    }\n                } else {\n                    // PATH B: Single-valued association\n                    $reflFieldValue = $reflField->getValue($parentObject);\n\n                    if (! $reflFieldValue || isset($this->hints[Query::HINT_REFRESH]) || $this->uow->isUninitializedObject($reflFieldValue)) {\n                        // we only need to take action if this value is null,\n                        // we refresh the entity or its an uninitialized proxy.\n                        if (isset($nonemptyComponents[$dqlAlias])) {\n                            $element = $this->getEntity($data, $dqlAlias);\n                            $reflField->setValue($parentObject, $element);\n                            $this->uow->setOriginalEntityProperty($oid, $relationField, $element);\n                            $targetClass = $this->metadataCache[$relation->targetEntity];\n\n                            if ($relation->isOwningSide()) {\n                                // TODO: Just check hints['fetched'] here?\n                                // If there is an inverse mapping on the target class its bidirectional\n                                if ($relation->inversedBy !== null) {\n                                    $inverseAssoc = $targetClass->associationMappings[$relation->inversedBy];\n                                    if ($inverseAssoc->isToOne()) {\n                                        $targetClass->propertyAccessors[$inverseAssoc->fieldName]->setValue($element, $parentObject);\n                                        $this->uow->setOriginalEntityProperty(spl_object_id($element), $inverseAssoc->fieldName, $parentObject);\n                                    }\n                                }\n                            } else {\n                                // For sure bidirectional, as there is no inverse side in unidirectional mappings\n                                $targetClass->propertyAccessors[$relation->mappedBy]->setValue($element, $parentObject);\n                                $this->uow->setOriginalEntityProperty(spl_object_id($element), $relation->mappedBy, $parentObject);\n                            }\n\n                            // Update result pointer\n                            $this->resultPointers[$dqlAlias] = $element;\n                        } else {\n                            $this->uow->setOriginalEntityProperty($oid, $relationField, null);\n                            $reflField->setValue($parentObject, null);\n                        }\n                        // else leave $reflFieldValue null for single-valued associations\n                    } else {\n                        // Update result pointer\n                        $this->resultPointers[$dqlAlias] = $reflFieldValue;\n                    }\n                }\n            } else {\n                // PATH C: Its a root result element\n                $this->rootAliases[$dqlAlias] = true; // Mark as root alias\n                $entityKey                    = $this->resultSetMapping()->entityMappings[$dqlAlias] ?: 0;\n\n                // if this row has a NULL value for the root result id then make it a null result.\n                if (! isset($nonemptyComponents[$dqlAlias])) {\n                    if ($this->resultSetMapping()->isMixed) {\n                        $result[] = [$entityKey => null];\n                    } else {\n                        $result[] = null;\n                    }\n\n                    $resultKey = $this->resultCounter;\n                    ++$this->resultCounter;\n                    continue;\n                }\n\n                // check for existing result from the iterations before\n                if (! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {\n                    $element = $this->getEntity($data, $dqlAlias);\n\n                    if ($this->resultSetMapping()->isMixed) {\n                        $element = [$entityKey => $element];\n                    }\n\n                    if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {\n                        $resultKey = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];\n\n                        if (isset($this->hints['collection'])) {\n                            $this->hints['collection']->hydrateSet($resultKey, $element);\n                        }\n\n                        $result[$resultKey] = $element;\n                    } else {\n                        $resultKey = $this->resultCounter;\n                        ++$this->resultCounter;\n\n                        if (isset($this->hints['collection'])) {\n                            $this->hints['collection']->hydrateAdd($element);\n                        }\n\n                        $result[] = $element;\n                    }\n\n                    $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;\n\n                    // Update result pointer\n                    $this->resultPointers[$dqlAlias] = $element;\n                } else {\n                    // Update result pointer\n                    $index                           = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];\n                    $this->resultPointers[$dqlAlias] = $result[$index];\n                    $resultKey                       = $index;\n                }\n            }\n\n            if (isset($this->hints[Query::HINT_INTERNAL_ITERATION]) && $this->hints[Query::HINT_INTERNAL_ITERATION]) {\n                $this->uow->hydrationComplete();\n            }\n        }\n\n        if (! isset($resultKey)) {\n            $this->resultCounter++;\n        }\n\n        // Append scalar values to mixed result sets\n        if (isset($rowData['scalars'])) {\n            if (! isset($resultKey)) {\n                $resultKey = isset($this->resultSetMapping()->indexByMap['scalars'])\n                    ? $row[$this->resultSetMapping()->indexByMap['scalars']]\n                    : $this->resultCounter - 1;\n            }\n\n            foreach ($rowData['scalars'] as $name => $value) {\n                $result[$resultKey][$name] = $value;\n            }\n        }\n\n        // Append new object to mixed result sets\n        if (isset($rowData['newObjects'])) {\n            if (! isset($resultKey)) {\n                $resultKey = $this->resultCounter - 1;\n            }\n\n            $scalarCount = (isset($rowData['scalars']) ? count($rowData['scalars']) : 0);\n\n            foreach ($rowData['newObjects'] as $objIndex => $newObject) {\n                $obj = $newObject['obj'];\n\n                if ($scalarCount === 0 && count($rowData['newObjects']) === 1) {\n                    $result[$resultKey] = $obj;\n\n                    continue;\n                }\n\n                $result[$resultKey][$objIndex] = $obj;\n            }\n        }\n    }\n\n    /** @param mixed[] $data pre-hydrated SQL Result Row. */\n    protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed\n    {\n        if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {\n            return $this->getEntity($data, $dqlAlias);\n        }\n\n        return $data;\n    }\n\n    /**\n     * When executed in a hydrate() loop we may have to clear internal state to\n     * decrease memory consumption.\n     */\n    public function onClear(mixed $eventArgs): void\n    {\n        parent::onClear($eventArgs);\n\n        $aliases = array_keys($this->identifierMap);\n\n        $this->identifierMap = array_fill_keys($aliases, []);\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/ScalarColumnHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse Doctrine\\DBAL\\Driver\\Exception;\nuse Doctrine\\ORM\\Exception\\MultipleSelectorsFoundException;\n\nuse function count;\n\n/**\n * Hydrator that produces one-dimensional array.\n */\nfinal class ScalarColumnHydrator extends AbstractHydrator\n{\n    /**\n     * {@inheritDoc}\n     *\n     * @throws MultipleSelectorsFoundException\n     * @throws Exception\n     */\n    protected function hydrateAllData(): array\n    {\n        if (count($this->resultSetMapping()->fieldMappings) > 1) {\n            throw MultipleSelectorsFoundException::create($this->resultSetMapping()->fieldMappings);\n        }\n\n        return $this->statement()->fetchFirstColumn();\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/ScalarHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\n/**\n * Hydrator that produces flat, rectangular results of scalar data.\n * The created result is almost the same as a regular SQL result set, except\n * that column names are mapped to field names and data type conversions take place.\n */\nclass ScalarHydrator extends AbstractHydrator\n{\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateAllData(): array\n    {\n        $result = [];\n\n        while ($data = $this->statement()->fetchAssociative()) {\n            $this->hydrateRowData($data, $result);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateRowData(array $row, array &$result): void\n    {\n        $result[] = $this->gatherScalarRowData($row);\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/SimpleObjectHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Query;\nuse Exception;\nuse RuntimeException;\nuse ValueError;\n\nuse function array_keys;\nuse function array_search;\nuse function assert;\nuse function count;\nuse function in_array;\nuse function is_array;\nuse function key;\nuse function reset;\nuse function sprintf;\n\nclass SimpleObjectHydrator extends AbstractHydrator\n{\n    use SQLResultCasing;\n\n    private ClassMetadata|null $class = null;\n\n    protected function prepare(): void\n    {\n        if (count($this->resultSetMapping()->aliasMap) !== 1) {\n            throw new RuntimeException('Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.');\n        }\n\n        if ($this->resultSetMapping()->scalarMappings) {\n            throw new RuntimeException('Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.');\n        }\n\n        $this->class = $this->getClassMetadata(reset($this->resultSetMapping()->aliasMap));\n    }\n\n    protected function cleanup(): void\n    {\n        parent::cleanup();\n\n        $this->uow->triggerEagerLoads();\n        $this->uow->hydrationComplete();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateAllData(): array\n    {\n        $result = [];\n\n        while ($row = $this->statement()->fetchAssociative()) {\n            $this->hydrateRowData($row, $result);\n        }\n\n        $this->em->getUnitOfWork()->triggerEagerLoads();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateRowData(array $row, array &$result): void\n    {\n        assert($this->class !== null);\n        $entityName       = $this->class->name;\n        $data             = [];\n        $discrColumnValue = null;\n\n        // We need to find the correct entity class name if we have inheritance in resultset\n        if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {\n            $discrColumn     = $this->class->getDiscriminatorColumn();\n            $discrColumnName = $this->getSQLResultCasing($this->platform, $discrColumn->name);\n\n            // Find mapped discriminator column from the result set.\n            $metaMappingDiscrColumnName = array_search($discrColumnName, $this->resultSetMapping()->metaMappings, true);\n            if ($metaMappingDiscrColumnName) {\n                $discrColumnName = $metaMappingDiscrColumnName;\n            }\n\n            if (! isset($row[$discrColumnName])) {\n                throw HydrationException::missingDiscriminatorColumn(\n                    $entityName,\n                    $discrColumnName,\n                    key($this->resultSetMapping()->aliasMap),\n                );\n            }\n\n            if ($row[$discrColumnName] === '') {\n                throw HydrationException::emptyDiscriminatorValue(key(\n                    $this->resultSetMapping()->aliasMap,\n                ));\n            }\n\n            $discrMap = $this->class->discriminatorMap;\n\n            if (! isset($discrMap[$row[$discrColumnName]])) {\n                throw HydrationException::invalidDiscriminatorValue($row[$discrColumnName], array_keys($discrMap));\n            }\n\n            $entityName       = $discrMap[$row[$discrColumnName]];\n            $discrColumnValue = $row[$discrColumnName];\n\n            unset($row[$discrColumnName]);\n        }\n\n        foreach ($row as $column => $value) {\n            // An ObjectHydrator should be used instead of SimpleObjectHydrator\n            if (isset($this->resultSetMapping()->relationMap[$column])) {\n                throw new Exception(sprintf('Unable to retrieve association information for column \"%s\"', $column));\n            }\n\n            $cacheKeyInfo = $this->hydrateColumnInfo($column);\n\n            if (! $cacheKeyInfo) {\n                continue;\n            }\n\n            // If we have inheritance in resultset, make sure the field belongs to the correct class\n            if (isset($cacheKeyInfo['discriminatorValues']) && ! in_array((string) $discrColumnValue, $cacheKeyInfo['discriminatorValues'], true)) {\n                continue;\n            }\n\n            // Check if value is null before conversion (because some types convert null to something else)\n            $valueIsNull = $value === null;\n\n            // Convert field to a valid PHP value\n            if (isset($cacheKeyInfo['type'])) {\n                $type  = $cacheKeyInfo['type'];\n                $value = $type->convertToPHPValue($value, $this->platform);\n            }\n\n            if ($value !== null && isset($cacheKeyInfo['enumType'])) {\n                $originalValue = $currentValue = $value;\n                try {\n                    if (! is_array($originalValue)) {\n                        $value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);\n                    } else {\n                        $value = [];\n                        foreach ($originalValue as $i => $currentValue) {\n                            $value[$i] = $this->buildEnum($currentValue, $cacheKeyInfo['enumType']);\n                        }\n                    }\n                } catch (ValueError $e) {\n                    throw MappingException::invalidEnumValue(\n                        $entityName,\n                        $cacheKeyInfo['fieldName'],\n                        (string) $currentValue,\n                        $cacheKeyInfo['enumType'],\n                        $e,\n                    );\n                }\n            }\n\n            $fieldName = $cacheKeyInfo['fieldName'];\n\n            // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)\n            if (! isset($data[$fieldName]) || ! $valueIsNull) {\n                $data[$fieldName] = $value;\n            }\n        }\n\n        if (isset($this->hints[Query::HINT_REFRESH_ENTITY])) {\n            $this->registerManaged($this->class, $this->hints[Query::HINT_REFRESH_ENTITY], $data);\n        }\n\n        $uow    = $this->em->getUnitOfWork();\n        $entity = $uow->createEntity($entityName, $data, $this->hints);\n\n        $result[] = $entity;\n\n        if (isset($this->hints[Query::HINT_INTERNAL_ITERATION]) && $this->hints[Query::HINT_INTERNAL_ITERATION]) {\n            $this->uow->hydrationComplete();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Internal/Hydration/SingleScalarHydrator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\Hydration;\n\nuse Doctrine\\ORM\\NonUniqueResultException;\nuse Doctrine\\ORM\\NoResultException;\n\nuse function array_shift;\nuse function count;\nuse function key;\n\n/**\n * Hydrator that hydrates a single scalar value from the result set.\n */\nclass SingleScalarHydrator extends AbstractHydrator\n{\n    protected function hydrateAllData(): mixed\n    {\n        $data    = $this->statement()->fetchAllAssociative();\n        $numRows = count($data);\n\n        if ($numRows === 0) {\n            throw new NoResultException();\n        }\n\n        if ($numRows > 1) {\n            throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().');\n        }\n\n        $result = $this->gatherScalarRowData($data[key($data)]);\n\n        if (count($result) > 1) {\n            throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().');\n        }\n\n        return array_shift($result);\n    }\n}\n"
  },
  {
    "path": "src/Internal/HydrationCompleteHandler.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\ListenersInvoker;\nuse Doctrine\\ORM\\Event\\PostLoadEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * Class, which can handle completion of hydration cycle and produce some of tasks.\n * In current implementation triggers deferred postLoad event.\n */\nfinal class HydrationCompleteHandler\n{\n    /** @var mixed[][] */\n    private array $deferredPostLoadInvocations = [];\n\n    public function __construct(\n        private readonly ListenersInvoker $listenersInvoker,\n        private readonly EntityManagerInterface $em,\n    ) {\n    }\n\n    /**\n     * Method schedules invoking of postLoad entity to the very end of current hydration cycle.\n     */\n    public function deferPostLoadInvoking(ClassMetadata $class, object $entity): void\n    {\n        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad);\n\n        if ($invoke === ListenersInvoker::INVOKE_NONE) {\n            return;\n        }\n\n        $this->deferredPostLoadInvocations[] = [$class, $invoke, $entity];\n    }\n\n    /**\n     * This method should be called after any hydration cycle completed.\n     *\n     * Method fires all deferred invocations of postLoad events\n     */\n    public function hydrationComplete(): void\n    {\n        $toInvoke                          = $this->deferredPostLoadInvocations;\n        $this->deferredPostLoadInvocations = [];\n\n        foreach ($toInvoke as $classAndEntity) {\n            [$class, $invoke, $entity] = $classAndEntity;\n\n            $this->listenersInvoker->invoke(\n                $class,\n                Events::postLoad,\n                $entity,\n                new PostLoadEventArgs($entity, $this->em),\n                $invoke,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/Internal/NoUnknownNamedArguments.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal;\n\nuse BadMethodCallException;\n\nuse function array_filter;\nuse function array_is_list;\nuse function array_keys;\nuse function array_values;\nuse function assert;\nuse function debug_backtrace;\nuse function implode;\nuse function is_string;\nuse function sprintf;\n\nuse const DEBUG_BACKTRACE_IGNORE_ARGS;\n\n/**\n * Checks if a variadic parameter contains unexpected named arguments.\n *\n * @internal\n */\ntrait NoUnknownNamedArguments\n{\n    /**\n     * @param TItem[] $parameter\n     *\n     * @template TItem\n     * @phpstan-assert list<TItem> $parameter\n     */\n    private static function validateVariadicParameter(array $parameter): void\n    {\n        if (array_is_list($parameter)) {\n            return;\n        }\n\n        [, $trace] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n        assert(isset($trace['class']));\n\n        $additionalArguments = array_values(array_filter(\n            array_keys($parameter),\n            is_string(...),\n        ));\n\n        throw new BadMethodCallException(sprintf(\n            'Invalid call to %s::%s(), unknown named arguments: %s',\n            $trace['class'],\n            $trace['function'],\n            implode(', ', $additionalArguments),\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Internal/SQLResultCasing.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\DB2Platform;\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\n\nuse function strtolower;\nuse function strtoupper;\n\n/** @internal */\ntrait SQLResultCasing\n{\n    private function getSQLResultCasing(AbstractPlatform $platform, string $column): string\n    {\n        if ($platform instanceof DB2Platform || $platform instanceof OraclePlatform) {\n            return strtoupper($column);\n        }\n\n        if ($platform instanceof PostgreSQLPlatform) {\n            return strtolower($column);\n        }\n\n        return $column;\n    }\n}\n"
  },
  {
    "path": "src/Internal/StronglyConnectedComponents.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal;\n\nuse InvalidArgumentException;\n\nuse function array_keys;\nuse function array_pop;\nuse function array_push;\nuse function min;\nuse function spl_object_id;\n\n/**\n * StronglyConnectedComponents implements Tarjan's algorithm to find strongly connected\n * components (SCC) in a directed graph. This algorithm has a linear running time based on\n * nodes (V) and edges between the nodes (E), resulting in a computational complexity\n * of O(V + E).\n *\n * See https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm\n * for an explanation and the meaning of the DFS and lowlink numbers.\n *\n * @internal\n */\nfinal class StronglyConnectedComponents\n{\n    private const NOT_VISITED = 1;\n    private const IN_PROGRESS = 2;\n    private const VISITED     = 3;\n\n    /**\n     * Array of all nodes, indexed by object ids.\n     *\n     * @var array<int, object>\n     */\n    private array $nodes = [];\n\n    /**\n     * DFS state for the different nodes, indexed by node object id and using one of\n     * this class' constants as value.\n     *\n     * @var array<int, self::*>\n     */\n    private array $states = [];\n\n    /**\n     * Edges between the nodes. The first-level key is the object id of the outgoing\n     * node; the second array maps the destination node by object id as key.\n     *\n     * @var array<int, array<int, bool>>\n     */\n    private array $edges = [];\n\n    /**\n     * DFS numbers, by object ID\n     *\n     * @var array<int, int>\n     */\n    private array $dfs = [];\n\n    /**\n     * lowlink numbers, by object ID\n     *\n     * @var array<int, int>\n     */\n    private array $lowlink = [];\n\n    private int $maxdfs = 0;\n\n    /**\n     * Nodes representing the SCC another node is in, indexed by lookup-node object ID\n     *\n     * @var array<int, object>\n     */\n    private array $representingNodes = [];\n\n    /**\n     * Stack with OIDs of nodes visited in the current state of the DFS\n     *\n     * @var list<int>\n     */\n    private array $stack = [];\n\n    public function addNode(object $node): void\n    {\n        $id                = spl_object_id($node);\n        $this->nodes[$id]  = $node;\n        $this->states[$id] = self::NOT_VISITED;\n        $this->edges[$id]  = [];\n    }\n\n    public function hasNode(object $node): bool\n    {\n        return isset($this->nodes[spl_object_id($node)]);\n    }\n\n    /**\n     * Adds a new edge between two nodes to the graph\n     */\n    public function addEdge(object $from, object $to): void\n    {\n        $fromId = spl_object_id($from);\n        $toId   = spl_object_id($to);\n\n        $this->edges[$fromId][$toId] = true;\n    }\n\n    public function findStronglyConnectedComponents(): void\n    {\n        foreach (array_keys($this->nodes) as $oid) {\n            if ($this->states[$oid] === self::NOT_VISITED) {\n                $this->tarjan($oid);\n            }\n        }\n    }\n\n    private function tarjan(int $oid): void\n    {\n        $this->dfs[$oid]    = $this->lowlink[$oid] = $this->maxdfs++;\n        $this->states[$oid] = self::IN_PROGRESS;\n        array_push($this->stack, $oid);\n\n        foreach ($this->edges[$oid] as $adjacentId => $ignored) {\n            if ($this->states[$adjacentId] === self::NOT_VISITED) {\n                $this->tarjan($adjacentId);\n                $this->lowlink[$oid] = min($this->lowlink[$oid], $this->lowlink[$adjacentId]);\n            } elseif ($this->states[$adjacentId] === self::IN_PROGRESS) {\n                $this->lowlink[$oid] = min($this->lowlink[$oid], $this->dfs[$adjacentId]);\n            }\n        }\n\n        $lowlink = $this->lowlink[$oid];\n        if ($lowlink === $this->dfs[$oid]) {\n            $representingNode = null;\n            do {\n                $unwindOid = array_pop($this->stack);\n\n                if (! $representingNode) {\n                    $representingNode = $this->nodes[$unwindOid];\n                }\n\n                $this->representingNodes[$unwindOid] = $representingNode;\n                $this->states[$unwindOid]            = self::VISITED;\n            } while ($unwindOid !== $oid);\n        }\n    }\n\n    public function getNodeRepresentingStronglyConnectedComponent(object $node): object\n    {\n        $oid = spl_object_id($node);\n\n        if (! isset($this->representingNodes[$oid])) {\n            throw new InvalidArgumentException('unknown node');\n        }\n\n        return $this->representingNodes[$oid];\n    }\n}\n"
  },
  {
    "path": "src/Internal/TopologicalSort/CycleDetectedException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\TopologicalSort;\n\nuse RuntimeException;\n\nuse function array_unshift;\n\nclass CycleDetectedException extends RuntimeException\n{\n    /** @var list<object> */\n    private array $cycle;\n\n    /**\n     * Do we have the complete cycle collected?\n     */\n    private bool $cycleCollected = false;\n\n    public function __construct(private readonly object $startNode)\n    {\n        parent::__construct('A cycle has been detected, so a topological sort is not possible. The getCycle() method provides the list of nodes that form the cycle.');\n\n        $this->cycle = [$startNode];\n    }\n\n    /** @return list<object> */\n    public function getCycle(): array\n    {\n        return $this->cycle;\n    }\n\n    public function addToCycle(object $node): void\n    {\n        array_unshift($this->cycle, $node);\n\n        if ($node === $this->startNode) {\n            $this->cycleCollected = true;\n        }\n    }\n\n    public function isCycleCollected(): bool\n    {\n        return $this->cycleCollected;\n    }\n}\n"
  },
  {
    "path": "src/Internal/TopologicalSort.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal;\n\nuse Doctrine\\ORM\\Internal\\TopologicalSort\\CycleDetectedException;\n\nuse function array_keys;\nuse function spl_object_id;\n\n/**\n * TopologicalSort implements topological sorting, which is an ordering\n * algorithm for directed graphs (DG) using a depth-first searching (DFS)\n * to traverse the graph built in memory.\n * This algorithm has a linear running time based on nodes (V) and edges\n * between the nodes (E), resulting in a computational complexity of O(V + E).\n *\n * @internal\n */\nfinal class TopologicalSort\n{\n    private const NOT_VISITED = 1;\n    private const IN_PROGRESS = 2;\n    private const VISITED     = 3;\n\n    /**\n     * Array of all nodes, indexed by object ids.\n     *\n     * @var array<int, object>\n     */\n    private array $nodes = [];\n\n    /**\n     * DFS state for the different nodes, indexed by node object id and using one of\n     * this class' constants as value.\n     *\n     * @var array<int, self::*>\n     */\n    private array $states = [];\n\n    /**\n     * Edges between the nodes. The first-level key is the object id of the outgoing\n     * node; the second array maps the destination node by object id as key. The final\n     * boolean value indicates whether the edge is optional or not.\n     *\n     * @var array<int, array<int, bool>>\n     */\n    private array $edges = [];\n\n    /**\n     * Builds up the result during the DFS.\n     *\n     * @var list<object>\n     */\n    private array $sortResult = [];\n\n    public function addNode(object $node): void\n    {\n        $id                = spl_object_id($node);\n        $this->nodes[$id]  = $node;\n        $this->states[$id] = self::NOT_VISITED;\n        $this->edges[$id]  = [];\n    }\n\n    public function hasNode(object $node): bool\n    {\n        return isset($this->nodes[spl_object_id($node)]);\n    }\n\n    /**\n     * Adds a new edge between two nodes to the graph\n     *\n     * @param bool $optional This indicates whether the edge may be ignored during the topological sort if it is necessary to break cycles.\n     */\n    public function addEdge(object $from, object $to, bool $optional): void\n    {\n        $fromId = spl_object_id($from);\n        $toId   = spl_object_id($to);\n\n        if (isset($this->edges[$fromId][$toId]) && $this->edges[$fromId][$toId] === false) {\n            return; // we already know about this dependency, and it is not optional\n        }\n\n        $this->edges[$fromId][$toId] = $optional;\n    }\n\n    /**\n     * Returns a topological sort of all nodes. When we have an edge A->B between two nodes\n     * A and B, then B will be listed before A in the result. Visually speaking, when ordering\n     * the nodes in the result order from left to right, all edges point to the left.\n     *\n     * @return list<object>\n     */\n    public function sort(): array\n    {\n        foreach (array_keys($this->nodes) as $oid) {\n            if ($this->states[$oid] === self::NOT_VISITED) {\n                $this->visit($oid);\n            }\n        }\n\n        return $this->sortResult;\n    }\n\n    private function visit(int $oid): void\n    {\n        if ($this->states[$oid] === self::IN_PROGRESS) {\n            // This node is already on the current DFS stack. We've found a cycle!\n            throw new CycleDetectedException($this->nodes[$oid]);\n        }\n\n        if ($this->states[$oid] === self::VISITED) {\n            // We've reached a node that we've already seen, including all\n            // other nodes that are reachable from here. We're done here, return.\n            return;\n        }\n\n        $this->states[$oid] = self::IN_PROGRESS;\n\n        // Continue the DFS downwards the edge list\n        foreach ($this->edges[$oid] as $adjacentId => $optional) {\n            try {\n                $this->visit($adjacentId);\n            } catch (CycleDetectedException $exception) {\n                if ($exception->isCycleCollected()) {\n                    // There is a complete cycle downstream of the current node. We cannot\n                    // do anything about that anymore.\n                    throw $exception;\n                }\n\n                if ($optional) {\n                    // The current edge is part of a cycle, but it is optional and the closest\n                    // such edge while backtracking. Break the cycle here by skipping the edge\n                    // and continuing with the next one.\n                    continue;\n                }\n\n                // We have found a cycle and cannot break it at $edge. Best we can do\n                // is to backtrack from the current vertex, hoping that somewhere up the\n                // stack this can be salvaged.\n                $this->states[$oid] = self::NOT_VISITED;\n                $exception->addToCycle($this->nodes[$oid]);\n\n                throw $exception;\n            }\n        }\n\n        // We have traversed all edges and visited all other nodes reachable from here.\n        // So we're done with this vertex as well.\n\n        $this->states[$oid] = self::VISITED;\n        $this->sortResult[] = $this->nodes[$oid];\n    }\n}\n"
  },
  {
    "path": "src/Internal/UnitOfWork/InsertBatch.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Internal\\UnitOfWork;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * An {@see InsertBatch} represents a set of entities that are safe to be batched\n * together in a single query.\n *\n * These entities are only those that have all fields already assigned, including the\n * identifier field(s).\n *\n * This data structure only exists for internal {@see UnitOfWork} optimisations, and\n * should not be relied upon outside the ORM.\n *\n * @internal\n *\n * @template TEntity of object\n */\nfinal class InsertBatch\n{\n    /**\n     * @param ClassMetadata<TEntity>  $class\n     * @param non-empty-list<TEntity> $entities\n     */\n    public function __construct(\n        public readonly ClassMetadata $class,\n        public array $entities,\n    ) {\n    }\n\n    /**\n     * Note: Code in here is procedural/ugly due to it being in a hot path of the {@see UnitOfWork}\n     *\n     * This method will batch the given entity set by type, preserving their order. For example,\n     * given an input [A1, A2, A3, B1, B2, A4, A5], it will create an [[A1, A2, A3], [B1, B2], [A4, A5]] batch.\n     *\n     * Entities for which the identifier needs to be generated or fetched by a sequence are put as single\n     * items in a batch of their own, since it is unsafe to batch-insert them.\n     *\n     * @param list<TEntities> $entities\n     *\n     * @return list<self<TEntities>>\n     *\n     * @template TEntities of object\n     */\n    public static function batchByEntityType(\n        EntityManagerInterface $entityManager,\n        array $entities,\n    ): array {\n        $currentClass = null;\n        $batches      = [];\n        $batchIndex   = -1;\n\n        foreach ($entities as $entity) {\n            $entityClass = $entityManager->getClassMetadata($entity::class);\n\n            if (\n                $currentClass?->name !== $entityClass->name\n                || ! $entityClass->idGenerator instanceof AssignedGenerator\n            ) {\n                $currentClass = $entityClass;\n                $batches[]    = new InsertBatch($entityClass, [$entity]);\n                $batchIndex  += 1;\n\n                continue;\n            }\n\n            $batches[$batchIndex]->entities[] = $entity;\n        }\n\n        return $batches;\n    }\n}\n"
  },
  {
    "path": "src/LazyCriteriaCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\Common\\Collections\\AbstractLazyCollection;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\ReadableCollection;\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\n\nuse function assert;\n\n/**\n * A lazy collection that allows a fast count when using criteria object\n * Once count gets executed once without collection being initialized, result\n * is cached and returned on subsequent calls until collection gets loaded,\n * then returning the number of loaded results.\n *\n * @template TKey of array-key\n * @template TValue of object\n * @extends AbstractLazyCollection<TKey, TValue>\n * @implements Selectable<TKey, TValue>\n */\nclass LazyCriteriaCollection extends AbstractLazyCollection implements Selectable\n{\n    private int|null $count = null;\n\n    public function __construct(\n        protected EntityPersister $entityPersister,\n        protected Criteria $criteria,\n    ) {\n    }\n\n    /**\n     * Do an efficient count on the collection\n     */\n    public function count(): int\n    {\n        if ($this->isInitialized()) {\n            return $this->collection->count();\n        }\n\n        // Return cached result in case count query was already executed\n        if ($this->count !== null) {\n            return $this->count;\n        }\n\n        return $this->count = $this->entityPersister->count($this->criteria);\n    }\n\n    /**\n     * check if collection is empty without loading it\n     */\n    public function isEmpty(): bool\n    {\n        if ($this->isInitialized()) {\n            return $this->collection->isEmpty();\n        }\n\n        return ! $this->count();\n    }\n\n    /**\n     * Do an optimized search of an element\n     *\n     * @param mixed $element The element to search for.\n     *\n     * @return bool TRUE if the collection contains $element, FALSE otherwise.\n     */\n    public function contains(mixed $element): bool\n    {\n        if ($this->isInitialized()) {\n            return $this->collection->contains($element);\n        }\n\n        return $this->entityPersister->exists($element, $this->criteria);\n    }\n\n    /** @return ReadableCollection<TKey, TValue>&Selectable<TKey, TValue> */\n    public function matching(Criteria $criteria): ReadableCollection&Selectable\n    {\n        $this->initialize();\n        assert($this->collection instanceof Selectable);\n\n        return $this->collection->matching($criteria);\n    }\n\n    protected function doInitialize(): void\n    {\n        $elements         = $this->entityPersister->loadCriteria($this->criteria);\n        $this->collection = new ArrayCollection($elements);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AnsiQuoteStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\n\n/**\n * ANSI compliant quote strategy, this strategy does not apply any quote.\n * To use this strategy all mapped tables and columns should be ANSI compliant.\n */\nclass AnsiQuoteStrategy implements QuoteStrategy\n{\n    use SQLResultCasing;\n\n    public function getColumnName(\n        string $fieldName,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string {\n        return $class->fieldMappings[$fieldName]->columnName;\n    }\n\n    public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return $class->table['name'];\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return $definition['sequenceName'];\n    }\n\n    public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return $joinColumn->name;\n    }\n\n    public function getReferencedJoinColumnName(\n        JoinColumnMapping $joinColumn,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string {\n        return $joinColumn->referencedColumnName;\n    }\n\n    public function getJoinTableName(\n        ManyToManyOwningSideMapping $association,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string {\n        return $association->joinTable->name;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array\n    {\n        return $class->identifier;\n    }\n\n    public function getColumnAlias(\n        string $columnName,\n        int $counter,\n        AbstractPlatform $platform,\n        ClassMetadata|null $class = null,\n    ): string {\n        return $this->getSQLResultCasing($platform, $columnName . '_' . $counter);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ArrayAccessImplementation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse InvalidArgumentException;\n\nuse function property_exists;\n\n/** @internal */\ntrait ArrayAccessImplementation\n{\n    /** @param string $offset */\n    public function offsetExists(mixed $offset): bool\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11211',\n            'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',\n            static::class,\n        );\n\n        return isset($this->$offset);\n    }\n\n    /** @param string $offset */\n    public function offsetGet(mixed $offset): mixed\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11211',\n            'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',\n            static::class,\n        );\n\n        if (! property_exists($this, $offset)) {\n            throw new InvalidArgumentException('Undefined property: ' . $offset);\n        }\n\n        return $this->$offset;\n    }\n\n    /** @param string $offset */\n    public function offsetSet(mixed $offset, mixed $value): void\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11211',\n            'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',\n            static::class,\n        );\n\n        $this->$offset = $value;\n    }\n\n    /** @param string $offset */\n    public function offsetUnset(mixed $offset): void\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11211',\n            'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',\n            static::class,\n        );\n\n        $this->$offset = null;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\nuse Exception;\nuse OutOfRangeException;\n\nuse function assert;\nuse function count;\nuse function in_array;\nuse function property_exists;\nuse function sprintf;\n\n/** @template-implements ArrayAccess<string, mixed> */\nabstract class AssociationMapping implements ArrayAccess\n{\n    /**\n     * The names of persistence operations to cascade on the association.\n     *\n     * @var list<'persist'|'remove'|'detach'|'refresh'|'all'>\n     */\n    public array $cascade = [];\n\n    /**\n     * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.\n     *\n     * @var ClassMetadata::FETCH_*|null\n     */\n    public int|null $fetch = null;\n\n    /**\n     * This is set when the association is inherited by this class from another\n     * (inheritance) parent <em>entity</em> class. The value is the FQCN of the\n     * topmost entity class that contains this association. (If there are\n     * transient classes in the class hierarchy, these are ignored, so the\n     * class property may in fact come from a class further up in the PHP class\n     * hierarchy.) To-many associations initially declared in mapped\n     * superclasses are <em>not</em> considered 'inherited' in the nearest\n     * entity subclasses.\n     *\n     * @var class-string|null\n     */\n    public string|null $inherited = null;\n\n    /**\n     * This is set when the association does not appear in the current class\n     * for the first time, but is initially declared in another parent\n     * <em>entity or mapped superclass</em>. The value is the FQCN of the\n     * topmost non-transient class that contains association information for\n     * this relationship.\n     *\n     * @var class-string|null\n     */\n    public string|null $declared = null;\n\n    public array|null $cache = null;\n\n    public bool|null $id = null;\n\n    public bool|null $isOnDeleteCascade = null;\n\n    /** @var class-string|null */\n    public string|null $originalClass = null;\n\n    public string|null $originalField = null;\n\n    public bool $orphanRemoval = false;\n\n    public bool|null $unique = null;\n\n    /**\n     * @param string       $fieldName    The name of the field in the entity\n     *                                   the association is mapped to.\n     * @param class-string $sourceEntity The class name of the source entity.\n     *                                   In the case of to-many associations\n     *                                   initially present in mapped\n     *                                   superclasses, the nearest\n     *                                   <em>entity</em> subclasses will be\n     *                                   considered the respective source\n     *                                   entities.\n     * @param class-string $targetEntity The class name of the target entity.\n     *                                   If it is fully-qualified it is used as\n     *                                   is. If it is a simple, unqualified\n     *                                   class name the namespace is assumed to\n     *                                   be the same as the namespace of the\n     *                                   source entity.\n     */\n    final public function __construct(\n        public readonly string $fieldName,\n        public string $sourceEntity,\n        public readonly string $targetEntity,\n    ) {\n    }\n\n    /**\n     * @param mixed[] $mappingArray\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): static\n    {\n        unset($mappingArray['isOwningSide'], $mappingArray['type']);\n        $mapping = new static(\n            $mappingArray['fieldName'],\n            $mappingArray['sourceEntity'],\n            $mappingArray['targetEntity'],\n        );\n        unset($mappingArray['fieldName'], $mappingArray['sourceEntity'], $mappingArray['targetEntity']);\n\n        foreach ($mappingArray as $key => $value) {\n            if ($key === 'joinTable') {\n                assert($mapping instanceof ManyToManyAssociationMapping);\n\n                if ($value === [] || $value === null) {\n                    continue;\n                }\n\n                if (! $mapping instanceof ManyToManyOwningSideMapping) {\n                    throw new MappingException(\n                        \"Mapping error on field '\" .\n                        $mapping->fieldName . \"' in \" . $mapping->sourceEntity .\n                        \" : 'joinTable' can only be set on many-to-many owning side.\",\n                    );\n                }\n\n                $mapping->joinTable = JoinTableMapping::fromMappingArray($value);\n\n                continue;\n            }\n\n            if (property_exists($mapping, $key)) {\n                $mapping->$key = $value;\n            } else {\n                throw new OutOfRangeException('Unknown property ' . $key . ' on class ' . static::class);\n            }\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * @phpstan-assert-if-true OwningSideMapping $this\n     * @phpstan-assert-if-false InverseSideMapping $this\n     */\n    final public function isOwningSide(): bool\n    {\n        return $this instanceof OwningSideMapping;\n    }\n\n    /** @phpstan-assert-if-true ToOneAssociationMapping $this */\n    final public function isToOne(): bool\n    {\n        return $this instanceof ToOneAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true ToManyAssociationMapping $this */\n    final public function isToMany(): bool\n    {\n        return $this instanceof ToManyAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true OneToOneOwningSideMapping $this */\n    final public function isOneToOneOwningSide(): bool\n    {\n        return $this->isOneToOne() && $this->isOwningSide();\n    }\n\n    /** @phpstan-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */\n    final public function isToOneOwningSide(): bool\n    {\n        return $this->isToOne() && $this->isOwningSide();\n    }\n\n    /** @phpstan-assert-if-true ManyToManyOwningSideMapping $this */\n    final public function isManyToManyOwningSide(): bool\n    {\n        return $this instanceof ManyToManyOwningSideMapping;\n    }\n\n    /** @phpstan-assert-if-true OneToOneAssociationMapping $this */\n    final public function isOneToOne(): bool\n    {\n        return $this instanceof OneToOneAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true OneToManyAssociationMapping $this */\n    final public function isOneToMany(): bool\n    {\n        return $this instanceof OneToManyAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true ManyToOneAssociationMapping $this */\n    final public function isManyToOne(): bool\n    {\n        return $this instanceof ManyToOneAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true ManyToManyAssociationMapping $this */\n    final public function isManyToMany(): bool\n    {\n        return $this instanceof ManyToManyAssociationMapping;\n    }\n\n    /** @phpstan-assert-if-true ToManyAssociationMapping $this */\n    final public function isOrdered(): bool\n    {\n        return $this->isToMany() && $this->orderBy() !== [];\n    }\n\n    /** @phpstan-assert-if-true ToManyAssociationMapping $this */\n    public function isIndexed(): bool\n    {\n        return false;\n    }\n\n    final public function type(): int\n    {\n        return match (true) {\n            $this instanceof OneToOneAssociationMapping => ClassMetadata::ONE_TO_ONE,\n            $this instanceof OneToManyAssociationMapping => ClassMetadata::ONE_TO_MANY,\n            $this instanceof ManyToOneAssociationMapping => ClassMetadata::MANY_TO_ONE,\n            $this instanceof ManyToManyAssociationMapping => ClassMetadata::MANY_TO_MANY,\n            default => throw new Exception('Cannot determine type for ' . static::class),\n        };\n    }\n\n    /** @param string $offset */\n    public function offsetExists(mixed $offset): bool\n    {\n        return isset($this->$offset) || in_array($offset, ['isOwningSide', 'type'], true);\n    }\n\n    final public function offsetGet(mixed $offset): mixed\n    {\n        return match ($offset) {\n            'isOwningSide' => $this->isOwningSide(),\n            'type' => $this->type(),\n            'isCascadeRemove' => $this->isCascadeRemove(),\n            'isCascadePersist' => $this->isCascadePersist(),\n            'isCascadeRefresh' => $this->isCascadeRefresh(),\n            'isCascadeDetach' => $this->isCascadeDetach(),\n            default => property_exists($this, $offset) ? $this->$offset : throw new OutOfRangeException(sprintf(\n                'Unknown property \"%s\" on class %s',\n                $offset,\n                static::class,\n            )),\n        };\n    }\n\n    public function offsetSet(mixed $offset, mixed $value): void\n    {\n        assert($offset !== null);\n        if (! property_exists($this, $offset)) {\n            throw new OutOfRangeException(sprintf(\n                'Unknown property \"%s\" on class %s',\n                $offset,\n                static::class,\n            ));\n        }\n\n        if ($offset === 'joinTable') {\n            $value = JoinTableMapping::fromMappingArray($value);\n        }\n\n        $this->$offset = $value;\n    }\n\n    /** @param string $offset */\n    public function offsetUnset(mixed $offset): void\n    {\n        if (! property_exists($this, $offset)) {\n            throw new OutOfRangeException(sprintf(\n                'Unknown property \"%s\" on class %s',\n                $offset,\n                static::class,\n            ));\n        }\n\n        $this->$offset = null;\n    }\n\n    final public function isCascadeRemove(): bool\n    {\n        return in_array('remove', $this->cascade, true);\n    }\n\n    final public function isCascadePersist(): bool\n    {\n        return in_array('persist', $this->cascade, true);\n    }\n\n    final public function isCascadeRefresh(): bool\n    {\n        return in_array('refresh', $this->cascade, true);\n    }\n\n    final public function isCascadeDetach(): bool\n    {\n        return in_array('detach', $this->cascade, true);\n    }\n\n    /** @return array<string, mixed> */\n    public function toArray(): array\n    {\n        $array = (array) $this;\n\n        $array['isOwningSide'] = $this->isOwningSide();\n        $array['type']         = $this->type();\n\n        return $array;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = ['fieldName', 'sourceEntity', 'targetEntity'];\n\n        if (count($this->cascade) > 0) {\n            $serialized[] = 'cascade';\n        }\n\n        foreach (\n            [\n                'fetch',\n                'inherited',\n                'declared',\n                'cache',\n                'originalClass',\n                'originalField',\n            ] as $stringOrArrayProperty\n        ) {\n            if ($this->$stringOrArrayProperty !== null) {\n                $serialized[] = $stringOrArrayProperty;\n            }\n        }\n\n        foreach (['id', 'orphanRemoval', 'isOnDeleteCascade', 'unique'] as $boolProperty) {\n            if ($this->$boolProperty) {\n                $serialized[] = $boolProperty;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AssociationOverride.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/** This attribute is used to override association mapping of property for an entity relationship. */\nfinal class AssociationOverride implements MappingAttribute\n{\n    /**\n     * The join column that is being mapped to the persistent attribute.\n     *\n     * @var array<JoinColumn>|null\n     */\n    public readonly array|null $joinColumns;\n\n    /**\n     * The join column that is being mapped to the persistent attribute.\n     *\n     * @var array<JoinColumn>|null\n     */\n    public readonly array|null $inverseJoinColumns;\n\n    /**\n     * @param string                       $name               The name of the relationship property whose mapping is being overridden.\n     * @param JoinColumn|array<JoinColumn> $joinColumns\n     * @param JoinColumn|array<JoinColumn> $inverseJoinColumns\n     * @param JoinTable|null               $joinTable          The join table that maps the relationship.\n     * @param string|null                  $inversedBy         The name of the association-field on the inverse-side.\n     * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch\n     */\n    public function __construct(\n        public readonly string $name,\n        array|JoinColumn|null $joinColumns = null,\n        array|JoinColumn|null $inverseJoinColumns = null,\n        public readonly JoinTable|null $joinTable = null,\n        public readonly string|null $inversedBy = null,\n        public readonly string|null $fetch = null,\n    ) {\n        if ($joinColumns instanceof JoinColumn) {\n            $joinColumns = [$joinColumns];\n        }\n\n        if ($inverseJoinColumns instanceof JoinColumn) {\n            $inverseJoinColumns = [$inverseJoinColumns];\n        }\n\n        $this->joinColumns        = $joinColumns;\n        $this->inverseJoinColumns = $inverseJoinColumns;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AssociationOverrides.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\nuse function array_values;\nuse function is_array;\n\n/** This attribute is used to override association mappings of relationship properties. */\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class AssociationOverrides implements MappingAttribute\n{\n    /**\n     * Mapping overrides of relationship properties.\n     *\n     * @var list<AssociationOverride>\n     */\n    public readonly array $overrides;\n\n    /** @param array<AssociationOverride>|AssociationOverride $overrides */\n    public function __construct(array|AssociationOverride $overrides)\n    {\n        if (! is_array($overrides)) {\n            $overrides = [$overrides];\n        }\n\n        foreach ($overrides as $override) {\n            if (! ($override instanceof AssociationOverride)) {\n                throw MappingException::invalidOverrideType('AssociationOverride', $override);\n            }\n        }\n\n        $this->overrides = array_values($overrides);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AttributeOverride.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/** This attribute is used to override the mapping of a entity property. */\nfinal class AttributeOverride implements MappingAttribute\n{\n    public function __construct(\n        public string $name,\n        public Column $column,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/AttributeOverrides.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\nuse function array_values;\nuse function is_array;\n\n/** This attribute is used to override the mapping of a entity property. */\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class AttributeOverrides implements MappingAttribute\n{\n    /**\n     * One or more field or property mapping overrides.\n     *\n     * @var list<AttributeOverride>\n     */\n    public readonly array $overrides;\n\n    /** @param array<AttributeOverride>|AttributeOverride $overrides */\n    public function __construct(array|AttributeOverride $overrides)\n    {\n        if (! is_array($overrides)) {\n            $overrides = [$overrides];\n        }\n\n        foreach ($overrides as $override) {\n            if (! ($override instanceof AttributeOverride)) {\n                throw MappingException::invalidOverrideType('AttributeOverride', $override);\n            }\n        }\n\n        $this->overrides = array_values($overrides);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/AssociationBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse InvalidArgumentException;\n\nclass AssociationBuilder\n{\n    /** @var mixed[]|null */\n    protected array|null $joinColumns = null;\n\n    /** @param mixed[] $mapping */\n    public function __construct(\n        protected readonly ClassMetadataBuilder $builder,\n        protected array $mapping,\n        protected readonly int $type,\n    ) {\n    }\n\n    /** @return $this */\n    public function mappedBy(string $fieldName): static\n    {\n        $this->mapping['mappedBy'] = $fieldName;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function inversedBy(string $fieldName): static\n    {\n        $this->mapping['inversedBy'] = $fieldName;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function cascadeAll(): static\n    {\n        $this->mapping['cascade'] = ['ALL'];\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function cascadePersist(): static\n    {\n        $this->mapping['cascade'][] = 'persist';\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function cascadeRemove(): static\n    {\n        $this->mapping['cascade'][] = 'remove';\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function cascadeDetach(): static\n    {\n        $this->mapping['cascade'][] = 'detach';\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function cascadeRefresh(): static\n    {\n        $this->mapping['cascade'][] = 'refresh';\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function fetchExtraLazy(): static\n    {\n        $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function fetchEager(): static\n    {\n        $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function fetchLazy(): static\n    {\n        $this->mapping['fetch'] = ClassMetadata::FETCH_LAZY;\n\n        return $this;\n    }\n\n    /**\n     * Add Join Columns.\n     *\n     * @return $this\n     */\n    public function addJoinColumn(\n        string $columnName,\n        string $referencedColumnName,\n        bool $nullable = true,\n        bool $unique = false,\n        string|null $onDelete = null,\n        string|null $columnDef = null,\n    ): static {\n        if ($this->mapping['id'] ?? false) {\n            $nullable = null;\n        }\n\n        $this->joinColumns[] = [\n            'name' => $columnName,\n            'referencedColumnName' => $referencedColumnName,\n            'nullable' => $nullable,\n            'unique' => $unique,\n            'onDelete' => $onDelete,\n            'columnDefinition' => $columnDef,\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Sets field as primary key.\n     *\n     * @return $this\n     */\n    public function makePrimaryKey(): static\n    {\n        $this->mapping['id'] = true;\n        foreach ($this->joinColumns ?? [] as $i => $joinColumn) {\n            $this->joinColumns[$i]['nullable'] = null;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Removes orphan entities when detached from their parent.\n     *\n     * @return $this\n     */\n    public function orphanRemoval(): static\n    {\n        $this->mapping['orphanRemoval'] = true;\n\n        return $this;\n    }\n\n    /** @throws InvalidArgumentException */\n    public function build(): ClassMetadataBuilder\n    {\n        $mapping = $this->mapping;\n        if ($this->joinColumns) {\n            $mapping['joinColumns'] = $this->joinColumns;\n        }\n\n        $cm = $this->builder->getClassMetadata();\n        if ($this->type === ClassMetadata::MANY_TO_ONE) {\n            $cm->mapManyToOne($mapping);\n        } elseif ($this->type === ClassMetadata::ONE_TO_ONE) {\n            $cm->mapOneToOne($mapping);\n        } else {\n            throw new InvalidArgumentException('Type should be a ToOne Association here');\n        }\n\n        return $this->builder;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/ClassMetadataBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\nuse BackedEnum;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * Builder Object for ClassMetadata\n *\n * @link        www.doctrine-project.com\n */\nclass ClassMetadataBuilder\n{\n    public function __construct(\n        private readonly ClassMetadata $cm,\n    ) {\n    }\n\n    public function getClassMetadata(): ClassMetadata\n    {\n        return $this->cm;\n    }\n\n    /**\n     * Marks the class as mapped superclass.\n     *\n     * @return $this\n     */\n    public function setMappedSuperClass(): static\n    {\n        $this->cm->isMappedSuperclass = true;\n        $this->cm->isEmbeddedClass    = false;\n\n        return $this;\n    }\n\n    /**\n     * Marks the class as embeddable.\n     *\n     * @return $this\n     */\n    public function setEmbeddable(): static\n    {\n        $this->cm->isEmbeddedClass    = true;\n        $this->cm->isMappedSuperclass = false;\n\n        return $this;\n    }\n\n    /**\n     * Adds and embedded class\n     *\n     * @param class-string $class\n     *\n     * @return $this\n     */\n    public function addEmbedded(string $fieldName, string $class, string|false|null $columnPrefix = null): static\n    {\n        $this->cm->mapEmbedded(\n            [\n                'fieldName'    => $fieldName,\n                'class'        => $class,\n                'columnPrefix' => $columnPrefix,\n            ],\n        );\n\n        return $this;\n    }\n\n    /**\n     * Sets custom Repository class name.\n     *\n     * @return $this\n     */\n    public function setCustomRepositoryClass(string $repositoryClassName): static\n    {\n        $this->cm->setCustomRepositoryClass($repositoryClassName);\n\n        return $this;\n    }\n\n    /**\n     * Marks class read only.\n     *\n     * @return $this\n     */\n    public function setReadOnly(): static\n    {\n        $this->cm->markReadOnly();\n\n        return $this;\n    }\n\n    /**\n     * Sets the table name.\n     *\n     * @return $this\n     */\n    public function setTable(string $name): static\n    {\n        $this->cm->setPrimaryTable(['name' => $name]);\n\n        return $this;\n    }\n\n    /**\n     * Adds Index.\n     *\n     * @phpstan-param list<string> $columns\n     *\n     * @return $this\n     */\n    public function addIndex(array $columns, string $name): static\n    {\n        if (! isset($this->cm->table['indexes'])) {\n            $this->cm->table['indexes'] = [];\n        }\n\n        $this->cm->table['indexes'][$name] = ['columns' => $columns];\n\n        return $this;\n    }\n\n    /**\n     * Adds Unique Constraint.\n     *\n     * @phpstan-param list<string> $columns\n     *\n     * @return $this\n     */\n    public function addUniqueConstraint(array $columns, string $name): static\n    {\n        if (! isset($this->cm->table['uniqueConstraints'])) {\n            $this->cm->table['uniqueConstraints'] = [];\n        }\n\n        $this->cm->table['uniqueConstraints'][$name] = ['columns' => $columns];\n\n        return $this;\n    }\n\n    /**\n     * Sets class as root of a joined table inheritance hierarchy.\n     *\n     * @return $this\n     */\n    public function setJoinedTableInheritance(): static\n    {\n        $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);\n\n        return $this;\n    }\n\n    /**\n     * Sets class as root of a single table inheritance hierarchy.\n     *\n     * @return $this\n     */\n    public function setSingleTableInheritance(): static\n    {\n        $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);\n\n        return $this;\n    }\n\n    /**\n     * Sets the discriminator column details.\n     *\n     * @param class-string<BackedEnum>|null $enumType\n     * @param array<string, mixed>          $options\n     *\n     * @return $this\n     */\n    public function setDiscriminatorColumn(\n        string $name,\n        string $type = 'string',\n        int $length = 255,\n        string|null $columnDefinition = null,\n        string|null $enumType = null,\n        array $options = [],\n    ): static {\n        $this->cm->setDiscriminatorColumn(\n            [\n                'name' => $name,\n                'type' => $type,\n                'length' => $length,\n                'columnDefinition' => $columnDefinition,\n                'enumType' => $enumType,\n                'options' => $options,\n            ],\n        );\n\n        return $this;\n    }\n\n    /**\n     * Adds a subclass to this inheritance hierarchy.\n     *\n     * @return $this\n     */\n    public function addDiscriminatorMapClass(string $name, string $class): static\n    {\n        $this->cm->addDiscriminatorMapClass($name, $class);\n\n        return $this;\n    }\n\n    /**\n     * Sets deferred explicit change tracking policy.\n     *\n     * @return $this\n     */\n    public function setChangeTrackingPolicyDeferredExplicit(): static\n    {\n        $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);\n\n        return $this;\n    }\n\n    /**\n     * Adds lifecycle event.\n     *\n     * @return $this\n     */\n    public function addLifecycleEvent(string $methodName, string $event): static\n    {\n        $this->cm->addLifecycleCallback($methodName, $event);\n\n        return $this;\n    }\n\n    /**\n     * Adds Field.\n     *\n     * @phpstan-param array<string, mixed> $mapping\n     *\n     * @return $this\n     */\n    public function addField(string $name, string $type, array $mapping = []): static\n    {\n        $mapping['fieldName'] = $name;\n        $mapping['type']      = $type;\n\n        $this->cm->mapField($mapping);\n\n        return $this;\n    }\n\n    /**\n     * Creates a field builder.\n     */\n    public function createField(string $name, string $type): FieldBuilder\n    {\n        return new FieldBuilder(\n            $this,\n            [\n                'fieldName' => $name,\n                'type'      => $type,\n            ],\n        );\n    }\n\n    /**\n     * Creates an embedded builder.\n     */\n    public function createEmbedded(string $fieldName, string $class): EmbeddedBuilder\n    {\n        return new EmbeddedBuilder(\n            $this,\n            [\n                'fieldName'    => $fieldName,\n                'class'        => $class,\n                'columnPrefix' => null,\n            ],\n        );\n    }\n\n    /**\n     * Adds a simple many to one association, optionally with the inversed by field.\n     */\n    public function addManyToOne(\n        string $name,\n        string $targetEntity,\n        string|null $inversedBy = null,\n    ): ClassMetadataBuilder {\n        $builder = $this->createManyToOne($name, $targetEntity);\n\n        if ($inversedBy !== null) {\n            $builder->inversedBy($inversedBy);\n        }\n\n        return $builder->build();\n    }\n\n    /**\n     * Creates a ManyToOne Association Builder.\n     *\n     * Note: This method does not add the association, you have to call build() on the AssociationBuilder.\n     */\n    public function createManyToOne(string $name, string $targetEntity): AssociationBuilder\n    {\n        return new AssociationBuilder(\n            $this,\n            [\n                'fieldName'    => $name,\n                'targetEntity' => $targetEntity,\n            ],\n            ClassMetadata::MANY_TO_ONE,\n        );\n    }\n\n    /**\n     * Creates a OneToOne Association Builder.\n     */\n    public function createOneToOne(string $name, string $targetEntity): AssociationBuilder\n    {\n        return new AssociationBuilder(\n            $this,\n            [\n                'fieldName'    => $name,\n                'targetEntity' => $targetEntity,\n            ],\n            ClassMetadata::ONE_TO_ONE,\n        );\n    }\n\n    /**\n     * Adds simple inverse one-to-one association.\n     */\n    public function addInverseOneToOne(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder\n    {\n        $builder = $this->createOneToOne($name, $targetEntity);\n        $builder->mappedBy($mappedBy);\n\n        return $builder->build();\n    }\n\n    /**\n     * Adds simple owning one-to-one association.\n     */\n    public function addOwningOneToOne(\n        string $name,\n        string $targetEntity,\n        string|null $inversedBy = null,\n    ): ClassMetadataBuilder {\n        $builder = $this->createOneToOne($name, $targetEntity);\n\n        if ($inversedBy !== null) {\n            $builder->inversedBy($inversedBy);\n        }\n\n        return $builder->build();\n    }\n\n    /**\n     * Creates a ManyToMany Association Builder.\n     */\n    public function createManyToMany(string $name, string $targetEntity): ManyToManyAssociationBuilder\n    {\n        return new ManyToManyAssociationBuilder(\n            $this,\n            [\n                'fieldName'    => $name,\n                'targetEntity' => $targetEntity,\n            ],\n            ClassMetadata::MANY_TO_MANY,\n        );\n    }\n\n    /**\n     * Adds a simple owning many to many association.\n     */\n    public function addOwningManyToMany(\n        string $name,\n        string $targetEntity,\n        string|null $inversedBy = null,\n    ): ClassMetadataBuilder {\n        $builder = $this->createManyToMany($name, $targetEntity);\n\n        if ($inversedBy !== null) {\n            $builder->inversedBy($inversedBy);\n        }\n\n        return $builder->build();\n    }\n\n    /**\n     * Adds a simple inverse many to many association.\n     */\n    public function addInverseManyToMany(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder\n    {\n        $builder = $this->createManyToMany($name, $targetEntity);\n        $builder->mappedBy($mappedBy);\n\n        return $builder->build();\n    }\n\n    /**\n     * Creates a one to many association builder.\n     */\n    public function createOneToMany(string $name, string $targetEntity): OneToManyAssociationBuilder\n    {\n        return new OneToManyAssociationBuilder(\n            $this,\n            [\n                'fieldName'    => $name,\n                'targetEntity' => $targetEntity,\n            ],\n            ClassMetadata::ONE_TO_MANY,\n        );\n    }\n\n    /**\n     * Adds simple OneToMany association.\n     */\n    public function addOneToMany(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder\n    {\n        $builder = $this->createOneToMany($name, $targetEntity);\n        $builder->mappedBy($mappedBy);\n\n        return $builder->build();\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/EmbeddedBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\n/**\n * Embedded Builder\n *\n * @link        www.doctrine-project.com\n */\nclass EmbeddedBuilder\n{\n    /** @param mixed[] $mapping */\n    public function __construct(\n        private readonly ClassMetadataBuilder $builder,\n        private array $mapping,\n    ) {\n    }\n\n    /**\n     * Sets the column prefix for all of the embedded columns.\n     *\n     * @return $this\n     */\n    public function setColumnPrefix(string $columnPrefix): static\n    {\n        $this->mapping['columnPrefix'] = $columnPrefix;\n\n        return $this;\n    }\n\n    /**\n     * Finalizes this embeddable and attach it to the ClassMetadata.\n     *\n     * Without this call an EmbeddedBuilder has no effect on the ClassMetadata.\n     */\n    public function build(): ClassMetadataBuilder\n    {\n        $cm = $this->builder->getClassMetadata();\n\n        $cm->mapEmbedded($this->mapping);\n\n        return $this->builder;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/EntityListenerBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\n\nuse function class_exists;\nuse function get_class_methods;\n\n/**\n * Builder for entity listeners.\n */\nclass EntityListenerBuilder\n{\n    /** Hash-map to handle event names. */\n    private const EVENTS = [\n        Events::preRemove   => true,\n        Events::postRemove  => true,\n        Events::prePersist  => true,\n        Events::postPersist => true,\n        Events::preUpdate   => true,\n        Events::postUpdate  => true,\n        Events::postLoad    => true,\n        Events::preFlush    => true,\n    ];\n\n    /**\n     * Lookup the entity class to find methods that match to event lifecycle names\n     *\n     * @param ClassMetadata $metadata  The entity metadata.\n     * @param string        $className The listener class name.\n     *\n     * @throws MappingException When the listener class not found.\n     */\n    public static function bindEntityListener(ClassMetadata $metadata, string $className): void\n    {\n        $class = $metadata->fullyQualifiedClassName($className);\n\n        if (! class_exists($class)) {\n            throw MappingException::entityListenerClassNotFound($class, $className);\n        }\n\n        foreach (get_class_methods($class) as $method) {\n            if (! isset(self::EVENTS[$method])) {\n                continue;\n            }\n\n            $metadata->addEntityListener($method, $class, $method);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/FieldBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\nuse function constant;\n\n/**\n * Field Builder\n *\n * @link        www.doctrine-project.com\n */\nclass FieldBuilder\n{\n    private bool $version               = false;\n    private string|null $generatedValue = null;\n\n    /** @var mixed[]|null */\n    private array|null $sequenceDef = null;\n\n    private string|null $customIdGenerator = null;\n\n    /** @param mixed[] $mapping */\n    public function __construct(\n        private readonly ClassMetadataBuilder $builder,\n        private array $mapping,\n    ) {\n    }\n\n    /**\n     * Sets length.\n     *\n     * @return $this\n     */\n    public function length(int $length): static\n    {\n        $this->mapping['length'] = $length;\n\n        return $this;\n    }\n\n    /**\n     * Sets nullable.\n     *\n     * @return $this\n     */\n    public function nullable(bool $flag = true): static\n    {\n        $this->mapping['nullable'] = $flag;\n\n        return $this;\n    }\n\n    /**\n     * Sets Unique.\n     *\n     * @return $this\n     */\n    public function unique(bool $flag = true): static\n    {\n        $this->mapping['unique'] = $flag;\n\n        return $this;\n    }\n\n    /**\n     * Sets indexed.\n     *\n     * @return $this\n     */\n    public function index(bool $flag = true): static\n    {\n        $this->mapping['index'] = $flag;\n\n        return $this;\n    }\n\n    /**\n     * Sets column name.\n     *\n     * @return $this\n     */\n    public function columnName(string $name): static\n    {\n        $this->mapping['columnName'] = $name;\n\n        return $this;\n    }\n\n    /**\n     * Sets Precision.\n     *\n     * @return $this\n     */\n    public function precision(int $p): static\n    {\n        $this->mapping['precision'] = $p;\n\n        return $this;\n    }\n\n    /**\n     * Sets insertable.\n     *\n     * @return $this\n     */\n    public function insertable(bool $flag = true): self\n    {\n        if (! $flag) {\n            $this->mapping['notInsertable'] = true;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets updatable.\n     *\n     * @return $this\n     */\n    public function updatable(bool $flag = true): self\n    {\n        if (! $flag) {\n            $this->mapping['notUpdatable'] = true;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets scale.\n     *\n     * @return $this\n     */\n    public function scale(int $s): static\n    {\n        $this->mapping['scale'] = $s;\n\n        return $this;\n    }\n\n    /**\n     * Sets field as primary key.\n     *\n     * @return $this\n     */\n    public function makePrimaryKey(): static\n    {\n        $this->mapping['id'] = true;\n\n        return $this;\n    }\n\n    /**\n     * Sets an option.\n     *\n     * @return $this\n     */\n    public function option(string $name, mixed $value): static\n    {\n        $this->mapping['options'][$name] = $value;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function generatedValue(string $strategy = 'AUTO'): static\n    {\n        $this->generatedValue = $strategy;\n\n        return $this;\n    }\n\n    /**\n     * Sets field versioned.\n     *\n     * @return $this\n     */\n    public function isVersionField(): static\n    {\n        $this->version = true;\n\n        return $this;\n    }\n\n    /**\n     * Sets Sequence Generator.\n     *\n     * @return $this\n     */\n    public function setSequenceGenerator(string $sequenceName, int $allocationSize = 1, int $initialValue = 1): static\n    {\n        $this->sequenceDef = [\n            'sequenceName' => $sequenceName,\n            'allocationSize' => $allocationSize,\n            'initialValue' => $initialValue,\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Sets column definition.\n     *\n     * @return $this\n     */\n    public function columnDefinition(string $def): static\n    {\n        $this->mapping['columnDefinition'] = $def;\n\n        return $this;\n    }\n\n    /**\n     * Set the FQCN of the custom ID generator.\n     * This class must extend \\Doctrine\\ORM\\Id\\AbstractIdGenerator.\n     *\n     * @return $this\n     */\n    public function setCustomIdGenerator(string $customIdGenerator): static\n    {\n        $this->customIdGenerator = $customIdGenerator;\n\n        return $this;\n    }\n\n    /**\n     * Finalizes this field and attach it to the ClassMetadata.\n     *\n     * Without this call a FieldBuilder has no effect on the ClassMetadata.\n     */\n    public function build(): ClassMetadataBuilder\n    {\n        $cm = $this->builder->getClassMetadata();\n        if ($this->generatedValue) {\n            $cm->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue));\n        }\n\n        if ($this->version) {\n            $cm->setVersionMapping($this->mapping);\n        }\n\n        $cm->mapField($this->mapping);\n        if ($this->sequenceDef) {\n            $cm->setSequenceGeneratorDefinition($this->sequenceDef);\n        }\n\n        if ($this->customIdGenerator) {\n            $cm->setCustomGeneratorDefinition(['class' => $this->customIdGenerator]);\n        }\n\n        return $this->builder;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/ManyToManyAssociationBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\n/**\n * ManyToMany Association Builder\n *\n * @link        www.doctrine-project.com\n */\nclass ManyToManyAssociationBuilder extends OneToManyAssociationBuilder\n{\n    private string|null $joinTableName = null;\n\n    /** @var mixed[] */\n    private array $inverseJoinColumns = [];\n\n    /** @return $this */\n    public function setJoinTable(string $name): static\n    {\n        $this->joinTableName = $name;\n\n        return $this;\n    }\n\n    /**\n     * Add Join Columns.\n     *\n     * @return $this\n     */\n    public function addJoinColumn(\n        string $columnName,\n        string $referencedColumnName,\n        bool $nullable = true,\n        bool $unique = false,\n        string|null $onDelete = null,\n        string|null $columnDef = null,\n    ): static {\n        $this->joinColumns[] = [\n            'name' => $columnName,\n            'referencedColumnName' => $referencedColumnName,\n            'unique' => $unique,\n            'onDelete' => $onDelete,\n            'columnDefinition' => $columnDef,\n        ];\n\n        return $this;\n    }\n\n    /**\n     * Adds Inverse Join Columns.\n     *\n     * @return $this\n     */\n    public function addInverseJoinColumn(\n        string $columnName,\n        string $referencedColumnName,\n        bool $nullable = true,\n        bool $unique = false,\n        string|null $onDelete = null,\n        string|null $columnDef = null,\n    ): static {\n        $this->inverseJoinColumns[] = [\n            'name' => $columnName,\n            'referencedColumnName' => $referencedColumnName,\n            'unique' => $unique,\n            'onDelete' => $onDelete,\n            'columnDefinition' => $columnDef,\n        ];\n\n        return $this;\n    }\n\n    public function build(): ClassMetadataBuilder\n    {\n        $mapping              = $this->mapping;\n        $mapping['joinTable'] = [];\n        if ($this->joinColumns) {\n            $mapping['joinTable']['joinColumns'] = $this->joinColumns;\n        }\n\n        if ($this->inverseJoinColumns) {\n            $mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns;\n        }\n\n        if ($this->joinTableName) {\n            $mapping['joinTable']['name'] = $this->joinTableName;\n        }\n\n        $cm = $this->builder->getClassMetadata();\n        $cm->mapManyToMany($mapping);\n\n        return $this->builder;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Builder/OneToManyAssociationBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Builder;\n\n/**\n * OneToMany Association Builder\n *\n * @link        www.doctrine-project.com\n */\nclass OneToManyAssociationBuilder extends AssociationBuilder\n{\n    /**\n     * @phpstan-param array<string, string> $fieldNames\n     *\n     * @return $this\n     */\n    public function setOrderBy(array $fieldNames): static\n    {\n        $this->mapping['orderBy'] = $fieldNames;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function setIndexBy(string $fieldName): static\n    {\n        $this->mapping['indexBy'] = $fieldName;\n\n        return $this;\n    }\n\n    public function build(): ClassMetadataBuilder\n    {\n        $mapping = $this->mapping;\n        if ($this->joinColumns) {\n            $mapping['joinColumns'] = $this->joinColumns;\n        }\n\n        $cm = $this->builder->getClassMetadata();\n        $cm->mapOneToMany($mapping);\n\n        return $this->builder;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Cache.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n/** Caching to an entity or a collection. */\n#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]\nfinal class Cache implements MappingAttribute\n{\n    /** @phpstan-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */\n    public function __construct(\n        public readonly string $usage = 'READ_ONLY',\n        public readonly string|null $region = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ChainTypedFieldMapper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Internal\\NoUnknownNamedArguments;\nuse ReflectionProperty;\n\nfinal class ChainTypedFieldMapper implements TypedFieldMapper\n{\n    use NoUnknownNamedArguments;\n\n    /** @var list<TypedFieldMapper> $typedFieldMappers */\n    private readonly array $typedFieldMappers;\n\n    public function __construct(TypedFieldMapper ...$typedFieldMappers)\n    {\n        self::validateVariadicParameter($typedFieldMappers);\n\n        $this->typedFieldMappers = $typedFieldMappers;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function validateAndComplete(array $mapping, ReflectionProperty $field): array\n    {\n        foreach ($this->typedFieldMappers as $typedFieldMapper) {\n            $mapping = $typedFieldMapper->validateAndComplete($mapping, $field);\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ChangeTrackingPolicy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class ChangeTrackingPolicy implements MappingAttribute\n{\n    /** @phpstan-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */\n    public function __construct(\n        public readonly string $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ClassMetadata.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse BackedEnum;\nuse BadMethodCallException;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\Instantiator\\Instantiator;\nuse Doctrine\\Instantiator\\InstantiatorInterface;\nuse Doctrine\\ORM\\Cache\\Exception\\NonCacheableEntityAssociation;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Id\\AbstractIdGenerator;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\EmbeddablePropertyAccessor;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\EnumPropertyAccessor;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessor;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessorFactory;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata as PersistenceClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\ReflectionService;\nuse InvalidArgumentException;\nuse LogicException;\nuse ReflectionClass;\nuse ReflectionNamedType;\nuse ReflectionProperty;\nuse Stringable;\n\nuse function array_column;\nuse function array_count_values;\nuse function array_diff;\nuse function array_filter;\nuse function array_flip;\nuse function array_intersect;\nuse function array_key_exists;\nuse function array_keys;\nuse function array_map;\nuse function array_merge;\nuse function array_pop;\nuse function array_values;\nuse function assert;\nuse function class_exists;\nuse function count;\nuse function defined;\nuse function enum_exists;\nuse function explode;\nuse function implode;\nuse function in_array;\nuse function interface_exists;\nuse function is_string;\nuse function is_subclass_of;\nuse function ltrim;\nuse function method_exists;\nuse function spl_object_id;\nuse function sprintf;\nuse function str_contains;\nuse function str_replace;\nuse function strtolower;\nuse function trait_exists;\nuse function trim;\n\n/**\n * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata\n * of an entity and its associations.\n *\n * Once populated, ClassMetadata instances are usually cached in a serialized form.\n *\n * <b>IMPORTANT NOTE:</b>\n *\n * The fields of this class are only public for 2 reasons:\n * 1) To allow fast READ access.\n * 2) To drastically reduce the size of a serialized instance (private/protected members\n *    get the whole class name, namespace inclusive, prepended to every property in\n *    the serialized representation).\n *\n * @phpstan-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping\n * @template-covariant T of object\n * @template-implements PersistenceClassMetadata<T>\n */\nclass ClassMetadata implements PersistenceClassMetadata, Stringable\n{\n    use GetReflectionClassImplementation;\n\n    /* The inheritance mapping types */\n    /**\n     * NONE means the class does not participate in an inheritance hierarchy\n     * and therefore does not need an inheritance mapping type.\n     */\n    public const INHERITANCE_TYPE_NONE = 1;\n\n    /**\n     * JOINED means the class will be persisted according to the rules of\n     * <tt>Class Table Inheritance</tt>.\n     */\n    public const INHERITANCE_TYPE_JOINED = 2;\n\n    /**\n     * SINGLE_TABLE means the class will be persisted according to the rules of\n     * <tt>Single Table Inheritance</tt>.\n     */\n    public const INHERITANCE_TYPE_SINGLE_TABLE = 3;\n\n    /* The Id generator types. */\n    /**\n     * AUTO means the generator type will depend on what the used platform prefers.\n     * Offers full portability.\n     */\n    public const GENERATOR_TYPE_AUTO = 1;\n\n    /**\n     * SEQUENCE means a separate sequence object will be used. Platforms that do\n     * not have native sequence support may emulate it. Full portability is currently\n     * not guaranteed.\n     */\n    public const GENERATOR_TYPE_SEQUENCE = 2;\n\n    /**\n     * IDENTITY means an identity column is used for id generation. The database\n     * will fill in the id column on insertion. Platforms that do not support\n     * native identity columns may emulate them. Full portability is currently\n     * not guaranteed.\n     */\n    public const GENERATOR_TYPE_IDENTITY = 4;\n\n    /**\n     * NONE means the class does not have a generated id. That means the class\n     * must have a natural, manually assigned id.\n     */\n    public const GENERATOR_TYPE_NONE = 5;\n\n    /**\n     * CUSTOM means that customer will use own ID generator that supposedly work\n     */\n    public const GENERATOR_TYPE_CUSTOM = 7;\n\n    /**\n     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time\n     * by doing a property-by-property comparison with the original data. This will\n     * be done for all entities that are in MANAGED state at commit-time.\n     *\n     * This is the default change tracking policy.\n     */\n    public const CHANGETRACKING_DEFERRED_IMPLICIT = 1;\n\n    /**\n     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time\n     * by doing a property-by-property comparison with the original data. This will\n     * be done only for entities that were explicitly saved (through persist() or a cascade).\n     */\n    public const CHANGETRACKING_DEFERRED_EXPLICIT = 2;\n\n    /**\n     * Specifies that an association is to be fetched when it is first accessed.\n     */\n    public const FETCH_LAZY = 2;\n\n    /**\n     * Specifies that an association is to be fetched when the owner of the\n     * association is fetched.\n     */\n    public const FETCH_EAGER = 3;\n\n    /**\n     * Specifies that an association is to be fetched lazy (on first access) and that\n     * commands such as Collection#count, Collection#slice are issued directly against\n     * the database if the collection is not yet initialized.\n     */\n    public const FETCH_EXTRA_LAZY = 4;\n\n    /**\n     * Identifies a one-to-one association.\n     */\n    public const ONE_TO_ONE = 1;\n\n    /**\n     * Identifies a many-to-one association.\n     */\n    public const MANY_TO_ONE = 2;\n\n    /**\n     * Identifies a one-to-many association.\n     */\n    public const ONE_TO_MANY = 4;\n\n    /**\n     * Identifies a many-to-many association.\n     */\n    public const MANY_TO_MANY = 8;\n\n    /**\n     * Combined bitmask for to-one (single-valued) associations.\n     */\n    public const TO_ONE = 3;\n\n    /**\n     * Combined bitmask for to-many (collection-valued) associations.\n     */\n    public const TO_MANY = 12;\n\n    /**\n     * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks,\n     */\n    public const CACHE_USAGE_READ_ONLY = 1;\n\n    /**\n     * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes.\n     */\n    public const CACHE_USAGE_NONSTRICT_READ_WRITE = 2;\n\n    /**\n     * Read Write Attempts to lock the entity before update/delete.\n     */\n    public const CACHE_USAGE_READ_WRITE = 3;\n\n    /**\n     * The value of this column is never generated by the database.\n     */\n    public const GENERATED_NEVER = 0;\n\n    /**\n     * The value of this column is generated by the database on INSERT, but not on UPDATE.\n     */\n    public const GENERATED_INSERT = 1;\n\n    /**\n     * The value of this column is generated by the database on both INSERT and UDPATE statements.\n     */\n    public const GENERATED_ALWAYS = 2;\n\n    /**\n     * READ-ONLY: The namespace the entity class is contained in.\n     *\n     * @todo Not really needed. Usage could be localized.\n     */\n    public string|null $namespace = null;\n\n    /**\n     * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance\n     * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same\n     * as {@link $name}.\n     *\n     * @phpstan-var class-string\n     */\n    public string $rootEntityName;\n\n    /**\n     * READ-ONLY: The definition of custom generator. Only used for CUSTOM\n     * generator type\n     *\n     * The definition has the following structure:\n     * <code>\n     * array(\n     *     'class' => 'ClassName',\n     * )\n     * </code>\n     *\n     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition\n     * @var array<string, string>|null\n     */\n    public array|null $customGeneratorDefinition = null;\n\n    /**\n     * The name of the custom repository class used for the entity class.\n     * (Optional).\n     *\n     * @phpstan-var ?class-string<EntityRepository>\n     */\n    public string|null $customRepositoryClassName = null;\n\n    /**\n     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.\n     */\n    public bool $isMappedSuperclass = false;\n\n    /**\n     * READ-ONLY: Whether this class describes the mapping of an embeddable class.\n     */\n    public bool $isEmbeddedClass = false;\n\n    /**\n     * READ-ONLY: The names of the parent <em>entity</em> classes (ancestors), starting with the\n     * nearest one and ending with the root entity class.\n     *\n     * @phpstan-var list<class-string>\n     */\n    public array $parentClasses = [];\n\n    /**\n     * READ-ONLY: For classes in inheritance mapping hierarchies, this field contains the names of all\n     * <em>entity</em> subclasses of this class. These may also be abstract classes.\n     *\n     * This list is used, for example, to enumerate all necessary tables in JTI when querying for root\n     * or subclass entities, or to gather all fields comprised in an entity inheritance tree.\n     *\n     * For classes that do not use STI/JTI, this list is empty.\n     *\n     * Implementation note:\n     *\n     * In PHP, there is no general way to discover all subclasses of a given class at runtime. For that\n     * reason, the list of classes given in the discriminator map at the root entity is considered\n     * authoritative. The discriminator map must contain all <em>concrete</em> classes that can\n     * appear in the particular inheritance hierarchy tree. Since there can be no instances of abstract\n     * entity classes, users are not required to list such classes with a discriminator value.\n     *\n     * The possibly remaining \"gaps\" for abstract entity classes are filled after the class metadata for the\n     * root entity has been loaded.\n     *\n     * For subclasses of such root entities, the list can be reused/passed downwards, it only needs to\n     * be filtered accordingly (only keep remaining subclasses)\n     *\n     * @phpstan-var list<class-string>\n     */\n    public array $subClasses = [];\n\n    /**\n     * READ-ONLY: The names of all embedded classes based on properties.\n     *\n     * @phpstan-var array<string, EmbeddedClassMapping>\n     */\n    public array $embeddedClasses = [];\n\n    /**\n     * READ-ONLY: The field names of all fields that are part of the identifier/primary key\n     * of the mapped entity class.\n     *\n     * @phpstan-var list<string>\n     */\n    public array $identifier = [];\n\n    /**\n     * READ-ONLY: The inheritance mapping type used by the class.\n     *\n     * @phpstan-var self::INHERITANCE_TYPE_*\n     */\n    public int $inheritanceType = self::INHERITANCE_TYPE_NONE;\n\n    /**\n     * READ-ONLY: The Id generator type used by the class.\n     *\n     * @phpstan-var self::GENERATOR_TYPE_*\n     */\n    public int $generatorType = self::GENERATOR_TYPE_NONE;\n\n    /**\n     * READ-ONLY: The field mappings of the class.\n     * Keys are field names and values are FieldMapping instances\n     *\n     * @var array<string, FieldMapping>\n     */\n    public array $fieldMappings = [];\n\n    /**\n     * READ-ONLY: An array of field names. Used to look up field names from column names.\n     * Keys are column names and values are field names.\n     *\n     * @phpstan-var array<string, string>\n     */\n    public array $fieldNames = [];\n\n    /**\n     * READ-ONLY: A map of field names to column names. Keys are field names and values column names.\n     * Used to look up column names from field names.\n     * This is the reverse lookup map of $_fieldNames.\n     *\n     * @deprecated 3.0 Remove this.\n     *\n     * @var mixed[]\n     */\n    public array $columnNames = [];\n\n    /**\n     * READ-ONLY: The discriminator value of this class.\n     *\n     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies\n     * where a discriminator column is used.</b>\n     *\n     * @see discriminatorColumn\n     */\n    public mixed $discriminatorValue = null;\n\n    /**\n     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.\n     *\n     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies\n     * where a discriminator column is used.</b>\n     *\n     * @see discriminatorColumn\n     *\n     * @var array<int|string, string>\n     *\n     * @phpstan-var array<int|string, class-string>\n     */\n    public array $discriminatorMap = [];\n\n    /**\n     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE\n     * inheritance mappings.\n     */\n    public DiscriminatorColumnMapping|null $discriminatorColumn = null;\n\n    /**\n     * READ-ONLY: The primary table definition.\n     *\n     * \"quoted\" indicates whether the table name is quoted (with backticks) or not\n     *\n     * @var mixed[]\n     * @phpstan-var array{\n     *               name: string,\n     *               schema?: string,\n     *               indexes?: array,\n     *               uniqueConstraints?: array,\n     *               options?: array<string, mixed>,\n     *               quoted?: bool\n     *           }\n     */\n    public array $table;\n\n    /**\n     * READ-ONLY: The registered lifecycle callbacks for entities of this class.\n     *\n     * @phpstan-var array<string, list<string>>\n     */\n    public array $lifecycleCallbacks = [];\n\n    /**\n     * READ-ONLY: The registered entity listeners.\n     *\n     * @phpstan-var array<string, list<array{class: class-string, method: string}>>\n     */\n    public array $entityListeners = [];\n\n    /**\n     * READ-ONLY: The association mappings of this class.\n     *\n     * A join table definition has the following structure:\n     * <pre>\n     * array(\n     *     'name' => <join table name>,\n     *      'joinColumns' => array(<join column mapping from join table to source table>),\n     *      'inverseJoinColumns' => array(<join column mapping from join table to target table>)\n     * )\n     * </pre>\n     *\n     * @phpstan-var array<string, ConcreteAssociationMapping>\n     */\n    public array $associationMappings = [];\n\n    /**\n     * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.\n     */\n    public bool $isIdentifierComposite = false;\n\n    /**\n     * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association.\n     *\n     * This flag is necessary because some code blocks require special treatment of this cases.\n     */\n    public bool $containsForeignIdentifier = false;\n\n    /**\n     * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one ENUM type.\n     *\n     * This flag is necessary because some code blocks require special treatment of this cases.\n     */\n    public bool $containsEnumIdentifier = false;\n\n    /**\n     * READ-ONLY: The ID generator used for generating IDs for this class.\n     *\n     * @todo Remove!\n     */\n    public AbstractIdGenerator $idGenerator;\n\n    /**\n     * READ-ONLY: The definition of the sequence generator of this class. Only used for the\n     * SEQUENCE generation strategy.\n     *\n     * The definition has the following structure:\n     * <code>\n     * array(\n     *     'sequenceName' => 'name',\n     *     'allocationSize' => '20',\n     *     'initialValue' => '1'\n     * )\n     * </code>\n     *\n     * @var array<string, mixed>|null\n     * @phpstan-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null\n     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition\n     */\n    public array|null $sequenceGeneratorDefinition = null;\n\n    /**\n     * READ-ONLY: The policy used for change-tracking on entities of this class.\n     */\n    public int $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;\n\n    /**\n     * READ-ONLY: A Flag indicating whether one or more columns of this class\n     * have to be reloaded after insert / update operations.\n     */\n    public bool $requiresFetchAfterChange = false;\n\n    /**\n     * READ-ONLY: A flag for whether or not instances of this class are to be versioned\n     * with optimistic locking.\n     */\n    public bool $isVersioned = false;\n\n    /**\n     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).\n     */\n    public string|null $versionField = null;\n\n    /** @var mixed[]|null */\n    public array|null $cache = null;\n\n    /**\n     * The ReflectionClass instance of the mapped class.\n     *\n     * @var ReflectionClass<T>|null\n     */\n    public ReflectionClass|null $reflClass = null;\n\n    /**\n     * Is this entity marked as \"read-only\"?\n     *\n     * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance\n     * optimization for entities that are immutable, either in your domain or through the relation database\n     * (coming from a view, or a history table for example).\n     */\n    public bool $isReadOnly = false;\n\n    /**\n     * NamingStrategy determining the default column and table names.\n     */\n    protected NamingStrategy $namingStrategy;\n\n    /**\n     * The ReflectionProperty instances of the mapped class.\n     *\n     * @deprecated Use $propertyAccessors instead.\n     *\n     * @var LegacyReflectionFields|array<string, ReflectionProperty>\n     */\n    public LegacyReflectionFields|array $reflFields = [];\n\n    /** @var array<string, PropertyAccessor> */\n    public array $propertyAccessors = [];\n\n    private InstantiatorInterface|null $instantiator = null;\n\n    private readonly TypedFieldMapper $typedFieldMapper;\n\n    /**\n     * Initializes a new ClassMetadata instance that will hold the object-relational mapping\n     * metadata of the class with the given name.\n     *\n     * @param string $name The name of the entity class the new instance is used for.\n     * @phpstan-param class-string<T> $name\n     */\n    public function __construct(public string $name, NamingStrategy|null $namingStrategy = null, TypedFieldMapper|null $typedFieldMapper = null)\n    {\n        $this->rootEntityName   = $name;\n        $this->namingStrategy   = $namingStrategy ?? new DefaultNamingStrategy();\n        $this->instantiator     = new Instantiator();\n        $this->typedFieldMapper = $typedFieldMapper ?? new DefaultTypedFieldMapper();\n    }\n\n    /**\n     * Gets the ReflectionProperties of the mapped class.\n     *\n     * @deprecated Use getPropertyAccessors() instead.\n     *\n     * @return LegacyReflectionFields|ReflectionProperty[] An array of ReflectionProperty instances.\n     * @phpstan-return LegacyReflectionFields|array<string, ReflectionProperty>\n     */\n    public function getReflectionProperties(): array|LegacyReflectionFields\n    {\n        return $this->reflFields;\n    }\n\n    /**\n     * Gets the ReflectionProperties of the mapped class.\n     *\n     * @return array<string, PropertyAccessor> An array of PropertyAccessor instances by name.\n     */\n    public function getPropertyAccessors(): array\n    {\n        return $this->propertyAccessors;\n    }\n\n    /**\n     * Gets a ReflectionProperty for a specific field of the mapped class.\n     *\n     * @deprecated Use getPropertyAccessor() instead.\n     */\n    public function getReflectionProperty(string $name): ReflectionProperty|null\n    {\n        return $this->reflFields[$name];\n    }\n\n    public function getPropertyAccessor(string $name): PropertyAccessor|null\n    {\n        return $this->propertyAccessors[$name] ?? null;\n    }\n\n    /**\n     * @deprecated Use getPropertyAccessor() instead.\n     *\n     * @throws BadMethodCallException If the class has a composite identifier.\n     */\n    public function getSingleIdReflectionProperty(): ReflectionProperty|null\n    {\n        if ($this->isIdentifierComposite) {\n            throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.');\n        }\n\n        return $this->reflFields[$this->identifier[0]];\n    }\n\n    /** @throws BadMethodCallException If the class has a composite identifier. */\n    public function getSingleIdPropertyAccessor(): PropertyAccessor|null\n    {\n        if ($this->isIdentifierComposite) {\n            throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.');\n        }\n\n        return $this->propertyAccessors[$this->identifier[0]];\n    }\n\n    /**\n     * Extracts the identifier values of an entity of this class.\n     *\n     * For composite identifiers, the identifier values are returned as an array\n     * with the same order as the field order in {@link identifier}.\n     *\n     * @return array<string, mixed>\n     */\n    public function getIdentifierValues(object $entity): array\n    {\n        if ($this->isIdentifierComposite) {\n            $id = [];\n\n            foreach ($this->identifier as $idField) {\n                $value = $this->propertyAccessors[$idField]->getValue($entity);\n\n                if ($value !== null) {\n                    $id[$idField] = $value;\n                }\n            }\n\n            return $id;\n        }\n\n        $id    = $this->identifier[0];\n        $value = $this->propertyAccessors[$id]->getValue($entity);\n\n        if ($value === null) {\n            return [];\n        }\n\n        return [$id => $value];\n    }\n\n    /**\n     * Populates the entity identifier of an entity.\n     *\n     * @phpstan-param array<string, mixed> $id\n     *\n     * @todo Rename to assignIdentifier()\n     */\n    public function setIdentifierValues(object $entity, array $id): void\n    {\n        foreach ($id as $idField => $idValue) {\n            $this->propertyAccessors[$idField]->setValue($entity, $idValue);\n        }\n    }\n\n    /**\n     * Sets the specified field to the specified value on the given entity.\n     */\n    public function setFieldValue(object $entity, string $field, mixed $value): void\n    {\n        $this->propertyAccessors[$field]->setValue($entity, $value);\n    }\n\n    /**\n     * Gets the specified field's value off the given entity.\n     */\n    public function getFieldValue(object $entity, string $field): mixed\n    {\n        return $this->propertyAccessors[$field]->getValue($entity);\n    }\n\n    /**\n     * Creates a string representation of this instance.\n     *\n     * @return string The string representation of this instance.\n     *\n     * @todo Construct meaningful string representation.\n     */\n    public function __toString(): string\n    {\n        return self::class . '@' . spl_object_id($this);\n    }\n\n    /**\n     * Determines which fields get serialized.\n     *\n     * It is only serialized what is necessary for best unserialization performance.\n     * That means any metadata properties that are not set or empty or simply have\n     * their default value are NOT serialized.\n     *\n     * Parts that are also NOT serialized because they can not be properly unserialized:\n     *      - reflClass (ReflectionClass)\n     *      - reflFields (ReflectionProperty array)\n     *\n     * @return string[] The names of all the fields that should be serialized.\n     */\n    public function __sleep(): array\n    {\n        // This metadata is always serialized/cached.\n        $serialized = [\n            'associationMappings',\n            'columnNames', //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName']\n            'fieldMappings',\n            'fieldNames',\n            'embeddedClasses',\n            'identifier',\n            'isIdentifierComposite', // TODO: REMOVE\n            'name',\n            'namespace', // TODO: REMOVE\n            'table',\n            'rootEntityName',\n            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.\n        ];\n\n        // The rest of the metadata is only serialized if necessary.\n        if ($this->changeTrackingPolicy !== self::CHANGETRACKING_DEFERRED_IMPLICIT) {\n            $serialized[] = 'changeTrackingPolicy';\n        }\n\n        if ($this->customRepositoryClassName) {\n            $serialized[] = 'customRepositoryClassName';\n        }\n\n        if ($this->inheritanceType !== self::INHERITANCE_TYPE_NONE) {\n            $serialized[] = 'inheritanceType';\n            $serialized[] = 'discriminatorColumn';\n            $serialized[] = 'discriminatorValue';\n            $serialized[] = 'discriminatorMap';\n            $serialized[] = 'parentClasses';\n            $serialized[] = 'subClasses';\n        }\n\n        if ($this->generatorType !== self::GENERATOR_TYPE_NONE) {\n            $serialized[] = 'generatorType';\n            if ($this->generatorType === self::GENERATOR_TYPE_SEQUENCE) {\n                $serialized[] = 'sequenceGeneratorDefinition';\n            }\n        }\n\n        if ($this->isMappedSuperclass) {\n            $serialized[] = 'isMappedSuperclass';\n        }\n\n        if ($this->isEmbeddedClass) {\n            $serialized[] = 'isEmbeddedClass';\n        }\n\n        if ($this->containsForeignIdentifier) {\n            $serialized[] = 'containsForeignIdentifier';\n        }\n\n        if ($this->containsEnumIdentifier) {\n            $serialized[] = 'containsEnumIdentifier';\n        }\n\n        if ($this->isVersioned) {\n            $serialized[] = 'isVersioned';\n            $serialized[] = 'versionField';\n        }\n\n        if ($this->lifecycleCallbacks) {\n            $serialized[] = 'lifecycleCallbacks';\n        }\n\n        if ($this->entityListeners) {\n            $serialized[] = 'entityListeners';\n        }\n\n        if ($this->isReadOnly) {\n            $serialized[] = 'isReadOnly';\n        }\n\n        if ($this->customGeneratorDefinition) {\n            $serialized[] = 'customGeneratorDefinition';\n        }\n\n        if ($this->cache) {\n            $serialized[] = 'cache';\n        }\n\n        if ($this->requiresFetchAfterChange) {\n            $serialized[] = 'requiresFetchAfterChange';\n        }\n\n        return $serialized;\n    }\n\n    /**\n     * Creates a new instance of the mapped class, without invoking the constructor.\n     */\n    public function newInstance(): object\n    {\n        return $this->instantiator->instantiate($this->name);\n    }\n\n    /**\n     * Restores some state that can not be serialized/unserialized.\n     */\n    public function wakeupReflection(ReflectionService $reflService): void\n    {\n        // Restore ReflectionClass and properties\n        $this->reflClass = $reflService->getClass($this->name);\n        /** @phpstan-ignore property.deprecated */\n        $this->reflFields   = new LegacyReflectionFields($this, $reflService);\n        $this->instantiator = $this->instantiator ?: new Instantiator();\n\n        $parentAccessors = [];\n\n        foreach ($this->embeddedClasses as $property => $embeddedClass) {\n            if (isset($embeddedClass->declaredField)) {\n                assert($embeddedClass->originalField !== null);\n                $childAccessor = PropertyAccessorFactory::createPropertyAccessor(\n                    $this->embeddedClasses[$embeddedClass->declaredField]->class,\n                    $embeddedClass->originalField,\n                );\n\n                $parentAccessors[$property] = new EmbeddablePropertyAccessor(\n                    $parentAccessors[$embeddedClass->declaredField],\n                    $childAccessor,\n                    $this->embeddedClasses[$embeddedClass->declaredField]->class,\n                );\n\n                continue;\n            }\n\n            $accessor = PropertyAccessorFactory::createPropertyAccessor(\n                $embeddedClass->declared ?? $this->name,\n                $property,\n            );\n\n            $parentAccessors[$property]         = $accessor;\n            $this->propertyAccessors[$property] = $accessor;\n        }\n\n        foreach ($this->fieldMappings as $field => $mapping) {\n            if (isset($mapping->declaredField) && isset($parentAccessors[$mapping->declaredField])) {\n                assert($mapping->originalField !== null);\n                assert($mapping->originalClass !== null);\n                $accessor = PropertyAccessorFactory::createPropertyAccessor($mapping->originalClass, $mapping->originalField);\n\n                if ($mapping->enumType !== null) {\n                    $accessor = new EnumPropertyAccessor(\n                        $accessor,\n                        $mapping->enumType,\n                    );\n                }\n\n                $this->propertyAccessors[$field] = new EmbeddablePropertyAccessor(\n                    $parentAccessors[$mapping->declaredField],\n                    $accessor,\n                    $mapping->originalClass,\n                );\n                continue;\n            }\n\n            $this->propertyAccessors[$field] = isset($mapping->declared)\n                ? PropertyAccessorFactory::createPropertyAccessor($mapping->declared, $field)\n                : PropertyAccessorFactory::createPropertyAccessor($this->name, $field);\n\n            if ($mapping->enumType !== null) {\n                $this->propertyAccessors[$field] = new EnumPropertyAccessor(\n                    $this->propertyAccessors[$field],\n                    $mapping->enumType,\n                );\n            }\n        }\n\n        foreach ($this->associationMappings as $field => $mapping) {\n            $this->propertyAccessors[$field] = isset($mapping->declared)\n                ? PropertyAccessorFactory::createPropertyAccessor($mapping->declared, $field)\n                : PropertyAccessorFactory::createPropertyAccessor($this->name, $field);\n        }\n    }\n\n    /**\n     * Initializes a new ClassMetadata instance that will hold the object-relational mapping\n     * metadata of the class with the given name.\n     *\n     * @param ReflectionService $reflService The reflection service.\n     */\n    public function initializeReflection(ReflectionService $reflService): void\n    {\n        $this->reflClass = $reflService->getClass($this->name);\n        $this->namespace = $reflService->getClassNamespace($this->name);\n\n        if ($this->reflClass) {\n            $this->name = $this->rootEntityName = $this->reflClass->name;\n        }\n\n        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);\n    }\n\n    /**\n     * Validates Identifier.\n     *\n     * @throws MappingException\n     */\n    public function validateIdentifier(): void\n    {\n        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {\n            return;\n        }\n\n        // Verify & complete identifier mapping\n        if (! $this->identifier) {\n            throw MappingException::identifierRequired($this->name);\n        }\n\n        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {\n            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);\n        }\n    }\n\n    /**\n     * Validates association targets actually exist.\n     *\n     * @throws MappingException\n     */\n    public function validateAssociations(): void\n    {\n        foreach ($this->associationMappings as $mapping) {\n            if (\n                ! class_exists($mapping->targetEntity)\n                && ! interface_exists($mapping->targetEntity)\n                && ! trait_exists($mapping->targetEntity)\n            ) {\n                throw MappingException::invalidTargetEntityClass($mapping->targetEntity, $this->name, $mapping->fieldName);\n            }\n        }\n    }\n\n    /**\n     * Validates lifecycle callbacks.\n     *\n     * @throws MappingException\n     */\n    public function validateLifecycleCallbacks(ReflectionService $reflService): void\n    {\n        foreach ($this->lifecycleCallbacks as $callbacks) {\n            foreach ($callbacks as $callbackFuncName) {\n                if (! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {\n                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);\n                }\n            }\n        }\n    }\n\n    /** @phpstan-param array{usage?: mixed, region?: mixed} $cache */\n    public function enableCache(array $cache): void\n    {\n        if (! isset($cache['usage'])) {\n            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;\n        }\n\n        if (! isset($cache['region'])) {\n            $cache['region'] = strtolower(str_replace('\\\\', '_', $this->rootEntityName));\n        }\n\n        $this->cache = $cache;\n    }\n\n    /** @phpstan-param array{usage?: int, region?: string} $cache */\n    public function enableAssociationCache(string $fieldName, array $cache): void\n    {\n        $this->associationMappings[$fieldName]->cache = $this->getAssociationCacheDefaults($fieldName, $cache);\n    }\n\n    /**\n     * @phpstan-param array{usage?: int, region?: string|null} $cache\n     *\n     * @return int[]|string[]\n     * @phpstan-return array{usage: int, region: string|null}\n     */\n    public function getAssociationCacheDefaults(string $fieldName, array $cache): array\n    {\n        if (! isset($cache['usage'])) {\n            $cache['usage'] = $this->cache['usage'] ?? self::CACHE_USAGE_READ_ONLY;\n        }\n\n        if (! isset($cache['region'])) {\n            $cache['region'] = strtolower(str_replace('\\\\', '_', $this->rootEntityName)) . '__' . $fieldName;\n        }\n\n        return $cache;\n    }\n\n    /**\n     * Sets the change tracking policy used by this class.\n     */\n    public function setChangeTrackingPolicy(int $policy): void\n    {\n        $this->changeTrackingPolicy = $policy;\n    }\n\n    /**\n     * Whether the change tracking policy of this class is \"deferred explicit\".\n     */\n    public function isChangeTrackingDeferredExplicit(): bool\n    {\n        return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT;\n    }\n\n    /**\n     * Whether the change tracking policy of this class is \"deferred implicit\".\n     */\n    public function isChangeTrackingDeferredImplicit(): bool\n    {\n        return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT;\n    }\n\n    /**\n     * Checks whether a field is part of the identifier/primary key field(s).\n     */\n    public function isIdentifier(string $fieldName): bool\n    {\n        if (! $this->identifier) {\n            return false;\n        }\n\n        if (! $this->isIdentifierComposite) {\n            return $fieldName === $this->identifier[0];\n        }\n\n        return in_array($fieldName, $this->identifier, true);\n    }\n\n    public function isUniqueField(string $fieldName): bool\n    {\n        $mapping = $this->getFieldMapping($fieldName);\n\n        return $mapping !== false && isset($mapping->unique) && $mapping->unique;\n    }\n\n    public function isNullable(string $fieldName): bool\n    {\n        $mapping = $this->getFieldMapping($fieldName);\n\n        return $mapping !== false && isset($mapping->nullable) && $mapping->nullable;\n    }\n\n    public function isIndexed(string $fieldName): bool\n    {\n        $mapping = $this->getFieldMapping($fieldName);\n\n        return isset($mapping->index) && $mapping->index;\n    }\n\n    /**\n     * Gets a column name for a field name.\n     * If the column name for the field cannot be found, the given field name\n     * is returned.\n     */\n    public function getColumnName(string $fieldName): string\n    {\n        // @phpstan-ignore property.deprecated\n        return $this->columnNames[$fieldName] ?? $fieldName;\n    }\n\n    /**\n     * Gets the mapping of a (regular) field that holds some data but not a\n     * reference to another object.\n     *\n     * @throws MappingException\n     */\n    public function getFieldMapping(string $fieldName): FieldMapping\n    {\n        if (! isset($this->fieldMappings[$fieldName])) {\n            throw MappingException::mappingNotFound($this->name, $fieldName);\n        }\n\n        return $this->fieldMappings[$fieldName];\n    }\n\n    /**\n     * Gets the mapping of an association.\n     *\n     * @see ClassMetadata::$associationMappings\n     *\n     * @param string $fieldName The field name that represents the association in\n     *                          the object model.\n     *\n     * @throws MappingException\n     */\n    public function getAssociationMapping(string $fieldName): AssociationMapping\n    {\n        if (! isset($this->associationMappings[$fieldName])) {\n            throw MappingException::mappingNotFound($this->name, $fieldName);\n        }\n\n        return $this->associationMappings[$fieldName];\n    }\n\n    /**\n     * Gets all association mappings of the class.\n     *\n     * @phpstan-return array<string, AssociationMapping>\n     */\n    public function getAssociationMappings(): array\n    {\n        return $this->associationMappings;\n    }\n\n    /**\n     * Gets the field name for a column name.\n     * If no field name can be found the column name is returned.\n     *\n     * @return string The column alias.\n     */\n    public function getFieldName(string $columnName): string\n    {\n        return $this->fieldNames[$columnName] ?? $columnName;\n    }\n\n    /**\n     * Checks whether given property has type\n     */\n    private function isTypedProperty(string $name): bool\n    {\n        return isset($this->reflClass)\n               && $this->reflClass->hasProperty($name)\n               && $this->reflClass->getProperty($name)->hasType();\n    }\n\n    /**\n     * Validates & completes the given field mapping based on typed property.\n     *\n     * @param  array{fieldName: string, type?: string} $mapping The field mapping to validate & complete.\n     *\n     * @return array{fieldName: string, enumType?: class-string<BackedEnum>, type?: string} The updated mapping.\n     */\n    private function validateAndCompleteTypedFieldMapping(array $mapping): array\n    {\n        $field = $this->reflClass->getProperty($mapping['fieldName']);\n\n        return $this->typedFieldMapper->validateAndComplete($mapping, $field);\n    }\n\n    /**\n     * Validates & completes the basic mapping information based on typed property.\n     *\n     * @param array{type: self::ONE_TO_ONE|self::MANY_TO_ONE|self::ONE_TO_MANY|self::MANY_TO_MANY, fieldName: string, targetEntity?: class-string} $mapping The mapping.\n     *\n     * @return mixed[] The updated mapping.\n     */\n    private function validateAndCompleteTypedAssociationMapping(array $mapping): array\n    {\n        $type = $this->reflClass->getProperty($mapping['fieldName'])->getType();\n\n        if ($type === null || ($mapping['type'] & self::TO_ONE) === 0) {\n            return $mapping;\n        }\n\n        if (! isset($mapping['targetEntity']) && $type instanceof ReflectionNamedType) {\n            $mapping['targetEntity'] = $type->getName();\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * Validates & completes the given field mapping.\n     *\n     * @phpstan-param array{\n     *     fieldName?: string,\n     *     columnName?: string,\n     *     id?: bool,\n     *     generated?: self::GENERATED_*,\n     *     enumType?: class-string,\n     * } $mapping The field mapping to validate & complete.\n     *\n     * @return FieldMapping The updated mapping.\n     *\n     * @throws MappingException\n     */\n    protected function validateAndCompleteFieldMapping(array $mapping): FieldMapping\n    {\n        // Check mandatory fields\n        if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) {\n            throw MappingException::missingFieldName($this->name);\n        }\n\n        if ($this->isTypedProperty($mapping['fieldName'])) {\n            $mapping = $this->validateAndCompleteTypedFieldMapping($mapping);\n        }\n\n        if (! isset($mapping['type'])) {\n            // Default to string\n            $mapping['type'] = 'string';\n        }\n\n        // Complete fieldName and columnName mapping\n        if (! isset($mapping['columnName'])) {\n            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);\n        }\n\n        $mapping = FieldMapping::fromMappingArray($mapping);\n\n        if ($mapping->columnName[0] === '`') {\n            $mapping->columnName = trim($mapping->columnName, '`');\n            $mapping->quoted     = true;\n        }\n\n        // @phpstan-ignore property.deprecated\n        $this->columnNames[$mapping->fieldName] = $mapping->columnName;\n\n        if (isset($this->fieldNames[$mapping->columnName]) || ($this->discriminatorColumn && $this->discriminatorColumn->name === $mapping->columnName)) {\n            throw MappingException::duplicateColumnName($this->name, $mapping->columnName);\n        }\n\n        $this->fieldNames[$mapping->columnName] = $mapping->fieldName;\n\n        // Complete id mapping\n        if (isset($mapping->id) && $mapping->id === true) {\n            if ($this->versionField === $mapping->fieldName) {\n                throw MappingException::cannotVersionIdField($this->name, $mapping->fieldName);\n            }\n\n            if (! in_array($mapping->fieldName, $this->identifier, true)) {\n                $this->identifier[] = $mapping->fieldName;\n            }\n\n            // Check for composite key\n            if (! $this->isIdentifierComposite && count($this->identifier) > 1) {\n                $this->isIdentifierComposite = true;\n            }\n        }\n\n        if (isset($mapping->generated)) {\n            if (! in_array($mapping->generated, [self::GENERATED_NEVER, self::GENERATED_INSERT, self::GENERATED_ALWAYS])) {\n                throw MappingException::invalidGeneratedMode($mapping->generated);\n            }\n\n            if ($mapping->generated === self::GENERATED_NEVER) {\n                unset($mapping->generated);\n            }\n        }\n\n        if (isset($mapping->enumType)) {\n            if (! enum_exists($mapping->enumType)) {\n                throw MappingException::nonEnumTypeMapped($this->name, $mapping->fieldName, $mapping->enumType);\n            }\n\n            if (! empty($mapping->id)) {\n                $this->containsEnumIdentifier = true;\n            }\n\n            if (\n                defined('Doctrine\\DBAL\\Types\\Types::ENUM')\n                && $mapping->type === Types::ENUM\n                && ! isset($mapping->options['values'])\n            ) {\n                $mapping->options['values'] = array_column($mapping->enumType::cases(), 'value');\n            }\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * Validates & completes the basic mapping information that is common to all\n     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).\n     *\n     * @phpstan-param array<string, mixed> $mapping The mapping.\n     *\n     * @return ConcreteAssociationMapping\n     *\n     * @throws MappingException If something is wrong with the mapping.\n     */\n    protected function _validateAndCompleteAssociationMapping(array $mapping): AssociationMapping\n    {\n        if (array_key_exists('mappedBy', $mapping) && $mapping['mappedBy'] === null) {\n            unset($mapping['mappedBy']);\n        }\n\n        if (array_key_exists('inversedBy', $mapping) && $mapping['inversedBy'] === null) {\n            unset($mapping['inversedBy']);\n        }\n\n        if (array_key_exists('joinColumns', $mapping) && in_array($mapping['joinColumns'], [null, []], true)) {\n            unset($mapping['joinColumns']);\n        }\n\n        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy\n\n        if (empty($mapping['indexBy'])) {\n            unset($mapping['indexBy']);\n        }\n\n        // If targetEntity is unqualified, assume it is in the same namespace as\n        // the sourceEntity.\n        $mapping['sourceEntity'] = $this->name;\n\n        if ($this->isTypedProperty($mapping['fieldName'])) {\n            $mapping = $this->validateAndCompleteTypedAssociationMapping($mapping);\n        }\n\n        if (isset($mapping['targetEntity'])) {\n            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);\n            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\\\');\n        }\n\n        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {\n            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);\n        }\n\n        // Complete id mapping\n        if (isset($mapping['id']) && $mapping['id'] === true) {\n            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {\n                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);\n            }\n\n            if (! in_array($mapping['fieldName'], $this->identifier, true)) {\n                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {\n                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(\n                        $mapping['targetEntity'],\n                        $this->name,\n                        $mapping['fieldName'],\n                    );\n                }\n\n                assert(is_string($mapping['fieldName']));\n                $this->identifier[]              = $mapping['fieldName'];\n                $this->containsForeignIdentifier = true;\n            }\n\n            // Check for composite key\n            if (! $this->isIdentifierComposite && count($this->identifier) > 1) {\n                $this->isIdentifierComposite = true;\n            }\n\n            if ($this->cache && ! isset($mapping['cache'])) {\n                throw NonCacheableEntityAssociation::fromEntityAndField(\n                    $this->name,\n                    $mapping['fieldName'],\n                );\n            }\n        }\n\n        // Mandatory attributes for both sides\n        // Mandatory: fieldName, targetEntity\n        if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) {\n            throw MappingException::missingFieldName($this->name);\n        }\n\n        if (! isset($mapping['targetEntity'])) {\n            throw MappingException::missingTargetEntity($mapping['fieldName']);\n        }\n\n        // Mandatory and optional attributes for either side\n        if (! isset($mapping['mappedBy'])) {\n            if (isset($mapping['joinTable'])) {\n                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {\n                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');\n                    $mapping['joinTable']['quoted'] = true;\n                }\n            }\n        } else {\n            $mapping['isOwningSide'] = false;\n        }\n\n        if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {\n            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);\n        }\n\n        // Fetch mode. Default fetch mode to LAZY, if not set.\n        if (! isset($mapping['fetch'])) {\n            $mapping['fetch'] = self::FETCH_LAZY;\n        }\n\n        // Cascades\n        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : [];\n\n        $allCascades = ['remove', 'persist', 'refresh', 'detach'];\n        if (in_array('all', $cascades, true)) {\n            $cascades = $allCascades;\n        } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) {\n            throw MappingException::invalidCascadeOption(\n                array_diff($cascades, $allCascades),\n                $this->name,\n                $mapping['fieldName'],\n            );\n        }\n\n        $mapping['cascade'] = $cascades;\n\n        switch ($mapping['type']) {\n            case self::ONE_TO_ONE:\n                if (isset($mapping['joinColumns']) && $mapping['joinColumns'] && ! $mapping['isOwningSide']) {\n                    throw MappingException::joinColumnNotAllowedOnOneToOneInverseSide(\n                        $this->name,\n                        $mapping['fieldName'],\n                    );\n                }\n\n                return $mapping['isOwningSide'] ?\n                    OneToOneOwningSideMapping::fromMappingArrayAndName(\n                        $mapping,\n                        $this->namingStrategy,\n                        $this->name,\n                        $this->table ?? null,\n                        $this->isInheritanceTypeSingleTable(),\n                    ) :\n                    OneToOneInverseSideMapping::fromMappingArrayAndName($mapping, $this->name);\n\n            case self::MANY_TO_ONE:\n                return ManyToOneAssociationMapping::fromMappingArrayAndName(\n                    $mapping,\n                    $this->namingStrategy,\n                    $this->name,\n                    $this->table ?? null,\n                    $this->isInheritanceTypeSingleTable(),\n                );\n\n            case self::ONE_TO_MANY:\n                return OneToManyAssociationMapping::fromMappingArrayAndName($mapping, $this->name);\n\n            case self::MANY_TO_MANY:\n                if (isset($mapping['joinColumns'])) {\n                    unset($mapping['joinColumns']);\n                }\n\n                return $mapping['isOwningSide'] ?\n                    ManyToManyOwningSideMapping::fromMappingArrayAndNamingStrategy($mapping, $this->namingStrategy) :\n                    ManyToManyInverseSideMapping::fromMappingArray($mapping);\n\n            default:\n                throw MappingException::invalidAssociationType(\n                    $this->name,\n                    $mapping['fieldName'],\n                    $mapping['type'],\n                );\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getIdentifierFieldNames(): array\n    {\n        return $this->identifier;\n    }\n\n    /**\n     * Gets the name of the single id field. Note that this only works on\n     * entity classes that have a single-field pk.\n     *\n     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.\n     */\n    public function getSingleIdentifierFieldName(): string\n    {\n        if ($this->isIdentifierComposite) {\n            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);\n        }\n\n        if (! isset($this->identifier[0])) {\n            throw MappingException::noIdDefined($this->name);\n        }\n\n        return $this->identifier[0];\n    }\n\n    /**\n     * Gets the column name of the single id column. Note that this only works on\n     * entity classes that have a single-field pk.\n     *\n     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.\n     */\n    public function getSingleIdentifierColumnName(): string\n    {\n        return $this->getColumnName($this->getSingleIdentifierFieldName());\n    }\n\n    /**\n     * INTERNAL:\n     * Sets the mapped identifier/primary key fields of this class.\n     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.\n     *\n     * @phpstan-param list<mixed> $identifier\n     */\n    public function setIdentifier(array $identifier): void\n    {\n        $this->identifier            = $identifier;\n        $this->isIdentifierComposite = (count($this->identifier) > 1);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getIdentifier(): array\n    {\n        return $this->identifier;\n    }\n\n    public function hasField(string $fieldName): bool\n    {\n        return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]);\n    }\n\n    /**\n     * Gets an array containing all the column names.\n     *\n     * @phpstan-param list<string>|null $fieldNames\n     *\n     * @return mixed[]\n     * @phpstan-return list<string>\n     */\n    public function getColumnNames(array|null $fieldNames = null): array\n    {\n        if ($fieldNames === null) {\n            return array_keys($this->fieldNames);\n        }\n\n        return array_values(array_map($this->getColumnName(...), $fieldNames));\n    }\n\n    /**\n     * Returns an array with all the identifier column names.\n     *\n     * @phpstan-return list<string>\n     */\n    public function getIdentifierColumnNames(): array\n    {\n        $columnNames = [];\n\n        foreach ($this->identifier as $idProperty) {\n            if (isset($this->fieldMappings[$idProperty])) {\n                $columnNames[] = $this->fieldMappings[$idProperty]->columnName;\n\n                continue;\n            }\n\n            // Association defined as Id field\n            assert($this->associationMappings[$idProperty]->isToOneOwningSide());\n            $joinColumns      = $this->associationMappings[$idProperty]->joinColumns;\n            $assocColumnNames = array_map(static fn (JoinColumnMapping $joinColumn): string => $joinColumn->name, $joinColumns);\n\n            $columnNames = array_merge($columnNames, $assocColumnNames);\n        }\n\n        return $columnNames;\n    }\n\n    /**\n     * Sets the type of Id generator to use for the mapped class.\n     *\n     * @phpstan-param self::GENERATOR_TYPE_* $generatorType\n     */\n    public function setIdGeneratorType(int $generatorType): void\n    {\n        $this->generatorType = $generatorType;\n    }\n\n    /**\n     * Checks whether the mapped class uses an Id generator.\n     */\n    public function usesIdGenerator(): bool\n    {\n        return $this->generatorType !== self::GENERATOR_TYPE_NONE;\n    }\n\n    public function isInheritanceTypeNone(): bool\n    {\n        return $this->inheritanceType === self::INHERITANCE_TYPE_NONE;\n    }\n\n    /**\n     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.\n     *\n     * @return bool TRUE if the class participates in a JOINED inheritance mapping,\n     * FALSE otherwise.\n     */\n    public function isInheritanceTypeJoined(): bool\n    {\n        return $this->inheritanceType === self::INHERITANCE_TYPE_JOINED;\n    }\n\n    /**\n     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.\n     *\n     * @return bool TRUE if the class participates in a SINGLE_TABLE inheritance mapping,\n     * FALSE otherwise.\n     */\n    public function isInheritanceTypeSingleTable(): bool\n    {\n        return $this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_TABLE;\n    }\n\n    /**\n     * Checks whether the class uses an identity column for the Id generation.\n     */\n    public function isIdGeneratorIdentity(): bool\n    {\n        return $this->generatorType === self::GENERATOR_TYPE_IDENTITY;\n    }\n\n    /**\n     * Checks whether the class uses a sequence for id generation.\n     *\n     * @phpstan-assert-if-true !null $this->sequenceGeneratorDefinition\n     */\n    public function isIdGeneratorSequence(): bool\n    {\n        return $this->generatorType === self::GENERATOR_TYPE_SEQUENCE;\n    }\n\n    /**\n     * Checks whether the class has a natural identifier/pk (which means it does\n     * not use any Id generator.\n     */\n    public function isIdentifierNatural(): bool\n    {\n        return $this->generatorType === self::GENERATOR_TYPE_NONE;\n    }\n\n    /**\n     * Gets the type of a field.\n     *\n     * @todo 3.0 Remove this. PersisterHelper should fix it somehow\n     */\n    public function getTypeOfField(string $fieldName): string|null\n    {\n        return isset($this->fieldMappings[$fieldName])\n            ? $this->fieldMappings[$fieldName]->type\n            : null;\n    }\n\n    /**\n     * Gets the name of the primary table.\n     */\n    public function getTableName(): string\n    {\n        return $this->table['name'];\n    }\n\n    /**\n     * Gets primary table's schema name.\n     */\n    public function getSchemaName(): string|null\n    {\n        return $this->table['schema'] ?? null;\n    }\n\n    /**\n     * Gets the table name to use for temporary identifier tables of this class.\n     */\n    public function getTemporaryIdTableName(): string\n    {\n        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema\n        return str_replace('.', '_', $this->getTableName() . '_id_tmp');\n    }\n\n    /**\n     * Sets the mapped subclasses of this class.\n     *\n     * @phpstan-param list<string> $subclasses The names of all mapped subclasses.\n     */\n    public function setSubclasses(array $subclasses): void\n    {\n        foreach ($subclasses as $subclass) {\n            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);\n        }\n    }\n\n    /**\n     * Sets the parent class names. Only <em>entity</em> classes may be given.\n     *\n     * Assumes that the class names in the passed array are in the order:\n     * directParent -> directParentParent -> directParentParentParent ... -> root.\n     *\n     * @phpstan-param list<class-string> $classNames\n     */\n    public function setParentClasses(array $classNames): void\n    {\n        $this->parentClasses = $classNames;\n\n        if (count($classNames) > 0) {\n            $this->rootEntityName = array_pop($classNames);\n        }\n    }\n\n    /**\n     * Sets the inheritance type used by the class and its subclasses.\n     *\n     * @phpstan-param self::INHERITANCE_TYPE_* $type\n     *\n     * @throws MappingException\n     */\n    public function setInheritanceType(int $type): void\n    {\n        if (! $this->isInheritanceType($type)) {\n            throw MappingException::invalidInheritanceType($this->name, $type);\n        }\n\n        $this->inheritanceType = $type;\n    }\n\n    /**\n     * Sets the association to override association mapping of property for an entity relationship.\n     *\n     * @phpstan-param array{joinColumns?: array, inversedBy?: ?string, joinTable?: array, fetch?: ?string, cascade?: string[]} $overrideMapping\n     *\n     * @throws MappingException\n     */\n    public function setAssociationOverride(string $fieldName, array $overrideMapping): void\n    {\n        if (! isset($this->associationMappings[$fieldName])) {\n            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);\n        }\n\n        $mapping = $this->associationMappings[$fieldName]->toArray();\n\n        if (isset($mapping['inherited'])) {\n            throw MappingException::illegalOverrideOfInheritedProperty(\n                $this->name,\n                $fieldName,\n                $mapping['inherited'],\n            );\n        }\n\n        if (isset($overrideMapping['joinColumns'])) {\n            $mapping['joinColumns'] = $overrideMapping['joinColumns'];\n        }\n\n        if (isset($overrideMapping['inversedBy'])) {\n            $mapping['inversedBy'] = $overrideMapping['inversedBy'];\n        }\n\n        if (isset($overrideMapping['joinTable'])) {\n            $mapping['joinTable'] = $overrideMapping['joinTable'];\n        }\n\n        if (isset($overrideMapping['fetch'])) {\n            $mapping['fetch'] = $overrideMapping['fetch'];\n        }\n\n        if (isset($overrideMapping['cascade'])) {\n            $mapping['cascade'] = $overrideMapping['cascade'];\n        }\n\n        switch ($mapping['type']) {\n            case self::ONE_TO_ONE:\n            case self::MANY_TO_ONE:\n                $mapping['joinColumnFieldNames']     = [];\n                $mapping['sourceToTargetKeyColumns'] = [];\n                break;\n            case self::MANY_TO_MANY:\n                $mapping['relationToSourceKeyColumns'] = [];\n                $mapping['relationToTargetKeyColumns'] = [];\n                break;\n        }\n\n        $this->associationMappings[$fieldName] = $this->_validateAndCompleteAssociationMapping($mapping);\n    }\n\n    /**\n     * Sets the override for a mapped field.\n     *\n     * @phpstan-param array<string, mixed> $overrideMapping\n     *\n     * @throws MappingException\n     */\n    public function setAttributeOverride(string $fieldName, array $overrideMapping): void\n    {\n        if (! isset($this->fieldMappings[$fieldName])) {\n            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);\n        }\n\n        $mapping = $this->fieldMappings[$fieldName];\n\n        if (isset($mapping->inherited)) {\n            throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName, $mapping->inherited);\n        }\n\n        if (isset($mapping->id)) {\n            $overrideMapping['id'] = $mapping->id;\n        }\n\n        if (isset($mapping->declared)) {\n            $overrideMapping['declared'] = $mapping->declared;\n        }\n\n        if (! isset($overrideMapping['type'])) {\n            $overrideMapping['type'] = $mapping->type;\n        }\n\n        if (! isset($overrideMapping['fieldName'])) {\n            $overrideMapping['fieldName'] = $mapping->fieldName;\n        }\n\n        if ($overrideMapping['type'] !== $mapping->type) {\n            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);\n        }\n\n        unset($this->fieldMappings[$fieldName]);\n        unset($this->fieldNames[$mapping->columnName]);\n        // @phpstan-ignore property.deprecated\n        unset($this->columnNames[$mapping->fieldName]);\n\n        $overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping);\n\n        $this->fieldMappings[$fieldName] = $overrideMapping;\n    }\n\n    /**\n     * Checks whether a mapped field is inherited from an entity superclass.\n     */\n    public function isInheritedField(string $fieldName): bool\n    {\n        return isset($this->fieldMappings[$fieldName]->inherited);\n    }\n\n    /**\n     * Checks if this entity is the root in any entity-inheritance-hierarchy.\n     */\n    public function isRootEntity(): bool\n    {\n        return $this->name === $this->rootEntityName;\n    }\n\n    /**\n     * Checks whether a mapped association field is inherited from a superclass.\n     */\n    public function isInheritedAssociation(string $fieldName): bool\n    {\n        return isset($this->associationMappings[$fieldName]->inherited);\n    }\n\n    public function isInheritedEmbeddedClass(string $fieldName): bool\n    {\n        return isset($this->embeddedClasses[$fieldName]->inherited);\n    }\n\n    /**\n     * Sets the name of the primary table the class is mapped to.\n     *\n     * @deprecated Use {@link setPrimaryTable}.\n     */\n    public function setTableName(string $tableName): void\n    {\n        $this->table['name'] = $tableName;\n    }\n\n    /**\n     * Sets the primary table definition. The provided array supports the\n     * following structure:\n     *\n     * name => <tableName> (optional, defaults to class name)\n     * indexes => array of indexes (optional)\n     * uniqueConstraints => array of constraints (optional)\n     *\n     * If a key is omitted, the current value is kept.\n     *\n     * @phpstan-param array<string, mixed> $table The table description.\n     */\n    public function setPrimaryTable(array $table): void\n    {\n        if (isset($table['name'])) {\n            // Split schema and table name from a table name like \"myschema.mytable\"\n            if (str_contains($table['name'], '.')) {\n                [$this->table['schema'], $table['name']] = explode('.', $table['name'], 2);\n            }\n\n            if ($table['name'][0] === '`') {\n                $table['name']         = trim($table['name'], '`');\n                $this->table['quoted'] = true;\n            }\n\n            $this->table['name'] = $table['name'];\n        }\n\n        if (isset($table['quoted'])) {\n            $this->table['quoted'] = $table['quoted'];\n        }\n\n        if (isset($table['schema'])) {\n            $this->table['schema'] = $table['schema'];\n        }\n\n        if (isset($table['indexes'])) {\n            $this->table['indexes'] = $table['indexes'];\n        }\n\n        if (isset($table['uniqueConstraints'])) {\n            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];\n        }\n\n        if (isset($table['options'])) {\n            $this->table['options'] = $table['options'];\n        }\n    }\n\n    /**\n     * Checks whether the given type identifies an inheritance type.\n     */\n    private function isInheritanceType(int $type): bool\n    {\n        return $type === self::INHERITANCE_TYPE_NONE ||\n                $type === self::INHERITANCE_TYPE_SINGLE_TABLE ||\n                $type === self::INHERITANCE_TYPE_JOINED;\n    }\n\n    /**\n     * Adds a mapped field to the class.\n     *\n     * @phpstan-param array<string, mixed> $mapping The field mapping.\n     *\n     * @throws MappingException\n     */\n    public function mapField(array $mapping): void\n    {\n        $mapping = $this->validateAndCompleteFieldMapping($mapping);\n        $this->assertFieldNotMapped($mapping->fieldName);\n\n        if (isset($mapping->generated)) {\n            $this->requiresFetchAfterChange = true;\n        }\n\n        $this->fieldMappings[$mapping->fieldName] = $mapping;\n    }\n\n    /**\n     * INTERNAL:\n     * Adds an association mapping without completing/validating it.\n     * This is mainly used to add inherited association mappings to derived classes.\n     *\n     * @param ConcreteAssociationMapping $mapping\n     *\n     * @throws MappingException\n     */\n    public function addInheritedAssociationMapping(AssociationMapping $mapping/*, $owningClassName = null*/): void\n    {\n        if (isset($this->associationMappings[$mapping->fieldName])) {\n            throw MappingException::duplicateAssociationMapping($this->name, $mapping->fieldName);\n        }\n\n        $this->associationMappings[$mapping->fieldName] = $mapping;\n    }\n\n    /**\n     * INTERNAL:\n     * Adds a field mapping without completing/validating it.\n     * This is mainly used to add inherited field mappings to derived classes.\n     */\n    public function addInheritedFieldMapping(FieldMapping $fieldMapping): void\n    {\n        $this->fieldMappings[$fieldMapping->fieldName] = $fieldMapping;\n        // @phpstan-ignore property.deprecated\n        $this->columnNames[$fieldMapping->fieldName] = $fieldMapping->columnName;\n        $this->fieldNames[$fieldMapping->columnName] = $fieldMapping->fieldName;\n\n        if (isset($fieldMapping->generated)) {\n            $this->requiresFetchAfterChange = true;\n        }\n    }\n\n    /**\n     * Adds a one-to-one mapping.\n     *\n     * @param array<string, mixed> $mapping The mapping.\n     */\n    public function mapOneToOne(array $mapping): void\n    {\n        $mapping['type'] = self::ONE_TO_ONE;\n\n        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);\n\n        $this->_storeAssociationMapping($mapping);\n    }\n\n    /**\n     * Adds a one-to-many mapping.\n     *\n     * @phpstan-param array<string, mixed> $mapping The mapping.\n     */\n    public function mapOneToMany(array $mapping): void\n    {\n        $mapping['type'] = self::ONE_TO_MANY;\n\n        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);\n\n        $this->_storeAssociationMapping($mapping);\n    }\n\n    /**\n     * Adds a many-to-one mapping.\n     *\n     * @phpstan-param array<string, mixed> $mapping The mapping.\n     */\n    public function mapManyToOne(array $mapping): void\n    {\n        $mapping['type'] = self::MANY_TO_ONE;\n\n        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);\n\n        $this->_storeAssociationMapping($mapping);\n    }\n\n    /**\n     * Adds a many-to-many mapping.\n     *\n     * @phpstan-param array<string, mixed> $mapping The mapping.\n     */\n    public function mapManyToMany(array $mapping): void\n    {\n        $mapping['type'] = self::MANY_TO_MANY;\n\n        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);\n\n        $this->_storeAssociationMapping($mapping);\n    }\n\n    /**\n     * Stores the association mapping.\n     *\n     * @param ConcreteAssociationMapping $assocMapping\n     *\n     * @throws MappingException\n     */\n    protected function _storeAssociationMapping(AssociationMapping $assocMapping): void\n    {\n        $sourceFieldName = $assocMapping->fieldName;\n\n        $this->assertFieldNotMapped($sourceFieldName);\n\n        $this->associationMappings[$sourceFieldName] = $assocMapping;\n    }\n\n    /**\n     * Registers a custom repository class for the entity class.\n     *\n     * @param string|null $repositoryClassName The class name of the custom mapper.\n     * @phpstan-param class-string<EntityRepository>|null $repositoryClassName\n     */\n    public function setCustomRepositoryClass(string|null $repositoryClassName): void\n    {\n        if ($repositoryClassName === null) {\n            $this->customRepositoryClassName = null;\n\n            return;\n        }\n\n        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);\n    }\n\n    /**\n     * Dispatches the lifecycle event of the given entity to the registered\n     * lifecycle callbacks and lifecycle listeners.\n     *\n     * @deprecated Deprecated since version 2.4 in favor of \\Doctrine\\ORM\\Event\\ListenersInvoker\n     *\n     * @param string $lifecycleEvent The lifecycle event.\n     */\n    public function invokeLifecycleCallbacks(string $lifecycleEvent, object $entity): void\n    {\n        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {\n            $entity->$callback();\n        }\n    }\n\n    /**\n     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.\n     */\n    public function hasLifecycleCallbacks(string $lifecycleEvent): bool\n    {\n        return isset($this->lifecycleCallbacks[$lifecycleEvent]);\n    }\n\n    /**\n     * Gets the registered lifecycle callbacks for an event.\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    public function getLifecycleCallbacks(string $event): array\n    {\n        return $this->lifecycleCallbacks[$event] ?? [];\n    }\n\n    /**\n     * Adds a lifecycle callback for entities of this class.\n     */\n    public function addLifecycleCallback(string $callback, string $event): void\n    {\n        if ($this->isEmbeddedClass) {\n            throw MappingException::illegalLifecycleCallbackOnEmbeddedClass($callback, $this->name);\n        }\n\n        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) {\n            return;\n        }\n\n        $this->lifecycleCallbacks[$event][] = $callback;\n    }\n\n    /**\n     * Sets the lifecycle callbacks for entities of this class.\n     * Any previously registered callbacks are overwritten.\n     *\n     * @phpstan-param array<string, list<string>> $callbacks\n     */\n    public function setLifecycleCallbacks(array $callbacks): void\n    {\n        $this->lifecycleCallbacks = $callbacks;\n    }\n\n    /**\n     * Adds a entity listener for entities of this class.\n     *\n     * @param string $eventName The entity lifecycle event.\n     * @param string $class     The listener class.\n     * @param string $method    The listener callback method.\n     *\n     * @throws MappingException\n     */\n    public function addEntityListener(string $eventName, string $class, string $method): void\n    {\n        $class = $this->fullyQualifiedClassName($class);\n\n        $listener = [\n            'class'  => $class,\n            'method' => $method,\n        ];\n\n        if (! class_exists($class)) {\n            throw MappingException::entityListenerClassNotFound($class, $this->name);\n        }\n\n        if (! method_exists($class, $method)) {\n            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);\n        }\n\n        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) {\n            throw MappingException::duplicateEntityListener($class, $method, $this->name);\n        }\n\n        $this->entityListeners[$eventName][] = $listener;\n    }\n\n    /**\n     * Sets the discriminator column definition.\n     *\n     * @see getDiscriminatorColumn()\n     *\n     * @param DiscriminatorColumnMapping|mixed[]|null $columnDef\n     * @phpstan-param DiscriminatorColumnMapping|array{\n     *     name: string|null,\n     *     fieldName?: string|null,\n     *     type?: string|null,\n     *     length?: int|null,\n     *     columnDefinition?: string|null,\n     *     enumType?: class-string<BackedEnum>|null,\n     *     options?: array<string, mixed>|null\n     * }|null $columnDef\n     *\n     * @throws MappingException\n     */\n    public function setDiscriminatorColumn(DiscriminatorColumnMapping|array|null $columnDef): void\n    {\n        if ($columnDef instanceof DiscriminatorColumnMapping) {\n            $this->discriminatorColumn = $columnDef;\n\n            return;\n        }\n\n        if ($columnDef !== null) {\n            if (! isset($columnDef['name'])) {\n                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);\n            }\n\n            if (isset($this->fieldNames[$columnDef['name']])) {\n                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);\n            }\n\n            $columnDef['fieldName'] ??= $columnDef['name'];\n            $columnDef['type']      ??= 'string';\n            $columnDef['options']   ??= [];\n\n            if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], true)) {\n                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);\n            }\n\n            if (isset($columnDef['enumType'])) {\n                if (! enum_exists($columnDef['enumType'])) {\n                    throw MappingException::nonEnumTypeMapped($this->name, $columnDef['fieldName'], $columnDef['enumType']);\n                }\n\n                if (\n                    defined('Doctrine\\DBAL\\Types\\Types::ENUM')\n                    && $columnDef['type'] === Types::ENUM\n                    && ! isset($columnDef['options']['values'])\n                ) {\n                    $columnDef['options']['values'] = array_column($columnDef['enumType']::cases(), 'value');\n                }\n            }\n\n            $this->discriminatorColumn = DiscriminatorColumnMapping::fromMappingArray($columnDef);\n        }\n    }\n\n    final public function getDiscriminatorColumn(): DiscriminatorColumnMapping\n    {\n        if ($this->discriminatorColumn === null) {\n            throw new LogicException('The discriminator column was not set.');\n        }\n\n        return $this->discriminatorColumn;\n    }\n\n    /**\n     * Sets the discriminator values used by this class.\n     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.\n     *\n     * @param array<int|string, string> $map\n     *\n     * @throws MappingException\n     */\n    public function setDiscriminatorMap(array $map): void\n    {\n        if (count(array_flip($map)) !== count($map)) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/issues/3519',\n                <<<'DEPRECATION'\n                Mapping a class to multiple discriminator values is deprecated,\n                and the discriminator mapping of %s contains duplicate values\n                for the following discriminator values: %s.\n                DEPRECATION,\n                $this->name,\n                implode(', ', array_keys(array_filter(array_count_values($map), static function (int $value): bool {\n                    return $value > 1;\n                }))),\n            );\n        }\n\n        $values = $this->discriminatorColumn->options['values'] ?? null;\n\n        if ($values !== null) {\n            $diff = array_diff(array_keys($map), $values);\n\n            if ($diff !== []) {\n                throw MappingException::invalidEntriesInDiscriminatorMap(array_values($diff), $this->name, $this->discriminatorColumn->enumType);\n            }\n        }\n\n        foreach ($map as $value => $className) {\n            $this->addDiscriminatorMapClass($value, $className);\n        }\n    }\n\n    /**\n     * Adds one entry of the discriminator map with a new class and corresponding name.\n     *\n     * @throws MappingException\n     */\n    public function addDiscriminatorMapClass(int|string $name, string $className): void\n    {\n        $className = $this->fullyQualifiedClassName($className);\n        $className = ltrim($className, '\\\\');\n\n        $this->discriminatorMap[$name] = $className;\n\n        if ($this->name === $className) {\n            $this->discriminatorValue = $name;\n\n            return;\n        }\n\n        if (! (class_exists($className) || interface_exists($className))) {\n            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);\n        }\n\n        $this->addSubClass($className);\n    }\n\n    /** @param array<class-string> $classes */\n    public function addSubClasses(array $classes): void\n    {\n        foreach ($classes as $className) {\n            $this->addSubClass($className);\n        }\n    }\n\n    public function addSubClass(string $className): void\n    {\n        // By ignoring classes that are not subclasses of the current class, we simplify inheriting\n        // the subclass list from a parent class at the beginning of \\Doctrine\\ORM\\Mapping\\ClassMetadataFactory::doLoadMetadata.\n\n        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses, true)) {\n            $this->subClasses[] = $className;\n        }\n    }\n\n    public function hasAssociation(string $fieldName): bool\n    {\n        return isset($this->associationMappings[$fieldName]);\n    }\n\n    public function isSingleValuedAssociation(string $fieldName): bool\n    {\n        return isset($this->associationMappings[$fieldName])\n            && ($this->associationMappings[$fieldName]->isToOne());\n    }\n\n    public function isCollectionValuedAssociation(string $fieldName): bool\n    {\n        return isset($this->associationMappings[$fieldName])\n            && ! $this->associationMappings[$fieldName]->isToOne();\n    }\n\n    /**\n     * Is this an association that only has a single join column?\n     */\n    public function isAssociationWithSingleJoinColumn(string $fieldName): bool\n    {\n        return isset($this->associationMappings[$fieldName])\n            && isset($this->associationMappings[$fieldName]->joinColumns[0])\n            && ! isset($this->associationMappings[$fieldName]->joinColumns[1]);\n    }\n\n    /**\n     * Returns the single association join column (if any).\n     *\n     * @throws MappingException\n     */\n    public function getSingleAssociationJoinColumnName(string $fieldName): string\n    {\n        if (! $this->isAssociationWithSingleJoinColumn($fieldName)) {\n            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);\n        }\n\n        $assoc = $this->associationMappings[$fieldName];\n\n        assert($assoc->isToOneOwningSide());\n\n        return $assoc->joinColumns[0]->name;\n    }\n\n    /**\n     * Returns the single association referenced join column name (if any).\n     *\n     * @throws MappingException\n     */\n    public function getSingleAssociationReferencedJoinColumnName(string $fieldName): string\n    {\n        if (! $this->isAssociationWithSingleJoinColumn($fieldName)) {\n            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);\n        }\n\n        $assoc = $this->associationMappings[$fieldName];\n\n        assert($assoc->isToOneOwningSide());\n\n        return $assoc->joinColumns[0]->referencedColumnName;\n    }\n\n    /**\n     * Used to retrieve a fieldname for either field or association from a given column.\n     *\n     * This method is used in foreign-key as primary-key contexts.\n     *\n     * @throws MappingException\n     */\n    public function getFieldForColumn(string $columnName): string\n    {\n        if (isset($this->fieldNames[$columnName])) {\n            return $this->fieldNames[$columnName];\n        }\n\n        foreach ($this->associationMappings as $assocName => $mapping) {\n            if (\n                $this->isAssociationWithSingleJoinColumn($assocName) &&\n                assert($this->associationMappings[$assocName]->isToOneOwningSide()) &&\n                $this->associationMappings[$assocName]->joinColumns[0]->name === $columnName\n            ) {\n                return $assocName;\n            }\n        }\n\n        throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);\n    }\n\n    /**\n     * Sets the ID generator used to generate IDs for instances of this class.\n     */\n    public function setIdGenerator(AbstractIdGenerator $generator): void\n    {\n        $this->idGenerator = $generator;\n    }\n\n    /**\n     * Sets definition.\n     *\n     * @phpstan-param array<string, string|null> $definition\n     */\n    public function setCustomGeneratorDefinition(array $definition): void\n    {\n        $this->customGeneratorDefinition = $definition;\n    }\n\n    /**\n     * Sets the definition of the sequence ID generator for this class.\n     *\n     * The definition must have the following structure:\n     * <code>\n     * array(\n     *     'sequenceName'   => 'name',\n     *     'allocationSize' => 20,\n     *     'initialValue'   => 1\n     *     'quoted'         => 1\n     * )\n     * </code>\n     *\n     * @phpstan-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition\n     *\n     * @throws MappingException\n     */\n    public function setSequenceGeneratorDefinition(array $definition): void\n    {\n        if (! isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') {\n            throw MappingException::missingSequenceName($this->name);\n        }\n\n        if ($definition['sequenceName'][0] === '`') {\n            $definition['sequenceName'] = trim($definition['sequenceName'], '`');\n            $definition['quoted']       = true;\n        }\n\n        if (! isset($definition['allocationSize']) || trim((string) $definition['allocationSize']) === '') {\n            $definition['allocationSize'] = '1';\n        }\n\n        if (! isset($definition['initialValue']) || trim((string) $definition['initialValue']) === '') {\n            $definition['initialValue'] = '1';\n        }\n\n        $definition['allocationSize'] = (string) $definition['allocationSize'];\n        $definition['initialValue']   = (string) $definition['initialValue'];\n\n        $this->sequenceGeneratorDefinition = $definition;\n    }\n\n    /**\n     * Sets the version field mapping used for versioning. Sets the default\n     * value to use depending on the column type.\n     *\n     * @phpstan-param array<string, mixed> $mapping The version field mapping array.\n     *\n     * @throws MappingException\n     */\n    public function setVersionMapping(array &$mapping): void\n    {\n        $this->isVersioned              = true;\n        $this->versionField             = $mapping['fieldName'];\n        $this->requiresFetchAfterChange = true;\n\n        if (! isset($mapping['default'])) {\n            if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'], true)) {\n                $mapping['options']['default'] = 1;\n            } elseif ($mapping['type'] === 'datetime') {\n                $mapping['options']['default'] = 'CURRENT_TIMESTAMP';\n            } else {\n                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);\n            }\n        }\n    }\n\n    /**\n     * Sets whether this class is to be versioned for optimistic locking.\n     */\n    public function setVersioned(bool $bool): void\n    {\n        $this->isVersioned = $bool;\n\n        if ($bool) {\n            $this->requiresFetchAfterChange = true;\n        }\n    }\n\n    /**\n     * Sets the name of the field that is to be used for versioning if this class is\n     * versioned for optimistic locking.\n     */\n    public function setVersionField(string|null $versionField): void\n    {\n        $this->versionField = $versionField;\n    }\n\n    /**\n     * Marks this class as read only, no change tracking is applied to it.\n     */\n    public function markReadOnly(): void\n    {\n        $this->isReadOnly = true;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getFieldNames(): array\n    {\n        return array_keys($this->fieldMappings);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getAssociationNames(): array\n    {\n        return array_keys($this->associationMappings);\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @phpstan-return class-string\n     *\n     * @throws InvalidArgumentException\n     */\n    public function getAssociationTargetClass(string $assocName): string\n    {\n        return $this->associationMappings[$assocName]->targetEntity\n            ?? throw new InvalidArgumentException(\"Association name expected, '\" . $assocName . \"' is not an association.\");\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function isAssociationInverseSide(string $assocName): bool\n    {\n        return isset($this->associationMappings[$assocName])\n            && ! $this->associationMappings[$assocName]->isOwningSide();\n    }\n\n    public function getAssociationMappedByTargetField(string $assocName): string\n    {\n        $assoc = $this->getAssociationMapping($assocName);\n\n        if (! $assoc instanceof InverseSideMapping) {\n            throw new LogicException(sprintf(\n                <<<'EXCEPTION'\n                Context: Calling %s() with \"%s\", which is the owning side of an association.\n                Problem: The owning side of an association has no \"mappedBy\" field.\n                Solution: Call %s::isAssociationInverseSide() to check first.\n                EXCEPTION,\n                __METHOD__,\n                $assocName,\n                self::class,\n            ));\n        }\n\n        return $assoc->mappedBy;\n    }\n\n    /**\n     * @param C $className\n     *\n     * @return string|null null if and only if the input value is null\n     * @phpstan-return (C is class-string ? class-string : (C is string ? string : null))\n     *\n     * @template C of string|null\n     */\n    public function fullyQualifiedClassName(string|null $className): string|null\n    {\n        if ($className === null) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/11294',\n                'Passing null to %s is deprecated and will not be supported in Doctrine ORM 4.0',\n                __METHOD__,\n            );\n\n            return null;\n        }\n\n        if (! str_contains($className, '\\\\') && $this->namespace) {\n            return $this->namespace . '\\\\' . $className;\n        }\n\n        return $className;\n    }\n\n    public function getMetadataValue(string $name): mixed\n    {\n        return $this->$name ?? null;\n    }\n\n    /**\n     * Map Embedded Class\n     *\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     class?: class-string,\n     *     declaredField?: string,\n     *     columnPrefix?: string|false|null,\n     *     originalField?: string\n     * } $mapping\n     *\n     * @throws MappingException\n     */\n    public function mapEmbedded(array $mapping): void\n    {\n        $this->assertFieldNotMapped($mapping['fieldName']);\n\n        if (! isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) {\n            $type = $this->reflClass->getProperty($mapping['fieldName'])->getType();\n            if ($type instanceof ReflectionNamedType) {\n                $mapping['class'] = $type->getName();\n            }\n        }\n\n        if (! (isset($mapping['class']) && $mapping['class'])) {\n            throw MappingException::missingEmbeddedClass($mapping['fieldName']);\n        }\n\n        $this->embeddedClasses[$mapping['fieldName']] = EmbeddedClassMapping::fromMappingArray([\n            'class' => $this->fullyQualifiedClassName($mapping['class']),\n            'columnPrefix' => $mapping['columnPrefix'] ?? null,\n            'declaredField' => $mapping['declaredField'] ?? null,\n            'originalField' => $mapping['originalField'] ?? null,\n        ]);\n    }\n\n    /**\n     * Inline the embeddable class\n     */\n    public function inlineEmbeddable(string $property, ClassMetadata $embeddable): void\n    {\n        foreach ($embeddable->fieldMappings as $originalFieldMapping) {\n            $fieldMapping                    = (array) $originalFieldMapping;\n            $fieldMapping['originalClass'] ??= $embeddable->name;\n            $fieldMapping['declaredField']   = isset($fieldMapping['declaredField'])\n                ? $property . '.' . $fieldMapping['declaredField']\n                : $property;\n            $fieldMapping['originalField'] ??= $fieldMapping['fieldName'];\n            $fieldMapping['fieldName']       = $property . '.' . $fieldMapping['fieldName'];\n\n            if (! empty($this->embeddedClasses[$property]->columnPrefix)) {\n                $fieldMapping['columnName'] = $this->embeddedClasses[$property]->columnPrefix . $fieldMapping['columnName'];\n            } elseif ($this->embeddedClasses[$property]->columnPrefix !== false) {\n                assert($this->reflClass !== null);\n                assert($embeddable->reflClass !== null);\n                $fieldMapping['columnName'] = $this->namingStrategy\n                    ->embeddedFieldToColumnName(\n                        $property,\n                        $fieldMapping['columnName'],\n                        $this->reflClass->name,\n                        $embeddable->reflClass->name,\n                    );\n            }\n\n            $this->mapField($fieldMapping);\n        }\n    }\n\n    /** @throws MappingException */\n    private function assertFieldNotMapped(string $fieldName): void\n    {\n        if (\n            isset($this->fieldMappings[$fieldName]) ||\n            isset($this->associationMappings[$fieldName]) ||\n            isset($this->embeddedClasses[$fieldName])\n        ) {\n            throw MappingException::duplicateFieldMapping($this->name, $fieldName);\n        }\n    }\n\n    /**\n     * Gets the sequence name based on class metadata.\n     *\n     * @todo Sequence names should be computed in DBAL depending on the platform\n     */\n    public function getSequenceName(AbstractPlatform $platform): string\n    {\n        $sequencePrefix = $this->getSequencePrefix($platform);\n        $columnName     = $this->getSingleIdentifierColumnName();\n\n        return $sequencePrefix . '_' . $columnName . '_seq';\n    }\n\n    /**\n     * Gets the sequence name prefix based on class metadata.\n     *\n     * @todo Sequence names should be computed in DBAL depending on the platform\n     */\n    public function getSequencePrefix(AbstractPlatform $platform): string\n    {\n        $tableName      = $this->getTableName();\n        $sequencePrefix = $tableName;\n\n        // Prepend the schema name to the table name if there is one\n        $schemaName = $this->getSchemaName();\n        if ($schemaName) {\n            $sequencePrefix = $schemaName . '.' . $tableName;\n        }\n\n        return $sequencePrefix;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ClassMetadataFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Platforms;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\nuse Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Id\\BigIntegerIdentityGenerator;\nuse Doctrine\\ORM\\Id\\IdentityGenerator;\nuse Doctrine\\ORM\\Id\\SequenceGenerator;\nuse Doctrine\\ORM\\Mapping\\Exception\\InvalidCustomGenerator;\nuse Doctrine\\ORM\\Mapping\\Exception\\UnknownGeneratorType;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata as ClassMetadataInterface;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Persistence\\Mapping\\ReflectionService;\nuse ReflectionClass;\nuse ReflectionException;\n\nuse function assert;\nuse function class_exists;\nuse function count;\nuse function end;\nuse function explode;\nuse function in_array;\nuse function is_a;\nuse function is_subclass_of;\nuse function method_exists;\nuse function str_contains;\nuse function strlen;\nuse function strtolower;\nuse function substr;\n\nuse const PHP_VERSION_ID;\n\n/**\n * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the\n * metadata mapping information of a class which describes how a class should be mapped\n * to a relational database.\n *\n * @extends AbstractClassMetadataFactory<ClassMetadata>\n */\nclass ClassMetadataFactory extends AbstractClassMetadataFactory\n{\n    private EntityManagerInterface|null $em       = null;\n    private AbstractPlatform|null $targetPlatform = null;\n    private MappingDriver|null $driver            = null;\n    private EventManager|null $evm                = null;\n\n    /** @var mixed[] */\n    private array $embeddablesActiveNesting = [];\n\n    private const NON_IDENTITY_DEFAULT_STRATEGY = [\n        Platforms\\OraclePlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,\n    ];\n\n    public function setEntityManager(EntityManagerInterface $em): void\n    {\n        if (! $em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver());\n        }\n\n        $this->em = $em;\n    }\n\n    /**\n     * @param A $maybeOwningSide\n     *\n     * @return (A is ManyToManyAssociationMapping ? ManyToManyOwningSideMapping : (\n     *     A is OneToOneAssociationMapping ? OneToOneOwningSideMapping : (\n     *     A is OneToManyAssociationMapping ? ManyToOneAssociationMapping : (\n     *     A is ManyToOneAssociationMapping ? ManyToOneAssociationMapping :\n     *     ManyToManyOwningSideMapping|OneToOneOwningSideMapping|ManyToOneAssociationMapping\n     * ))))\n     *\n     * @template A of AssociationMapping\n     */\n    final public function getOwningSide(AssociationMapping $maybeOwningSide): OwningSideMapping\n    {\n        if ($maybeOwningSide instanceof OwningSideMapping) {\n            assert($maybeOwningSide instanceof ManyToManyOwningSideMapping ||\n                $maybeOwningSide instanceof OneToOneOwningSideMapping ||\n                $maybeOwningSide instanceof ManyToOneAssociationMapping);\n\n            return $maybeOwningSide;\n        }\n\n        assert($maybeOwningSide instanceof InverseSideMapping);\n\n        $owningSide = $this->getMetadataFor($maybeOwningSide->targetEntity)\n            ->associationMappings[$maybeOwningSide->mappedBy];\n\n        assert($owningSide instanceof ManyToManyOwningSideMapping ||\n            $owningSide instanceof OneToOneOwningSideMapping ||\n            $owningSide instanceof ManyToOneAssociationMapping);\n\n        return $owningSide;\n    }\n\n    protected function initialize(): void\n    {\n        $this->driver      = $this->em->getConfiguration()->getMetadataDriverImpl();\n        $this->evm         = $this->em->getEventManager();\n        $this->initialized = true;\n    }\n\n    protected function onNotFoundMetadata(string $className): ClassMetadata|null\n    {\n        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {\n            return null;\n        }\n\n        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em);\n\n        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);\n        $classMetadata = $eventArgs->getFoundMetadata();\n        assert($classMetadata instanceof ClassMetadata || $classMetadata === null);\n\n        return $classMetadata;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function doLoadMetadata(\n        ClassMetadataInterface $class,\n        ClassMetadataInterface|null $parent,\n        bool $rootEntityFound,\n        array $nonSuperclassParents,\n    ): void {\n        if ($parent) {\n            $class->setInheritanceType($parent->inheritanceType);\n            $class->setDiscriminatorColumn($parent->discriminatorColumn === null ? null : clone $parent->discriminatorColumn);\n            $class->setIdGeneratorType($parent->generatorType);\n            $this->addInheritedFields($class, $parent);\n            $this->addInheritedRelations($class, $parent);\n            $this->addInheritedEmbeddedClasses($class, $parent);\n            $class->setIdentifier($parent->identifier);\n            $class->setVersioned($parent->isVersioned);\n            $class->setVersionField($parent->versionField);\n            $class->setDiscriminatorMap($parent->discriminatorMap);\n            $class->addSubClasses($parent->subClasses);\n            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);\n            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);\n\n            if (! empty($parent->customGeneratorDefinition)) {\n                $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);\n            }\n\n            if ($parent->isMappedSuperclass) {\n                $class->setCustomRepositoryClass($parent->customRepositoryClassName);\n            }\n        }\n\n        // Invoke driver\n        try {\n            $this->driver->loadMetadataForClass($class->getName(), $class);\n        } catch (ReflectionException $e) {\n            throw MappingException::reflectionFailure($class->getName(), $e);\n        }\n\n        // If this class has a parent the id generator strategy is inherited.\n        // However this is only true if the hierarchy of parents contains the root entity,\n        // if it consists of mapped superclasses these don't necessarily include the id field.\n        if ($parent && $rootEntityFound) {\n            $this->inheritIdGeneratorMapping($class, $parent);\n        } else {\n            $this->completeIdGeneratorMapping($class);\n        }\n\n        if (! $class->isMappedSuperclass) {\n            if ($rootEntityFound && $class->isInheritanceTypeNone()) {\n                throw MappingException::missingInheritanceTypeDeclaration(end($nonSuperclassParents), $class->name);\n            }\n\n            foreach ($class->embeddedClasses as $property => $embeddableClass) {\n                if (isset($embeddableClass->inherited)) {\n                    continue;\n                }\n\n                if (isset($this->embeddablesActiveNesting[$embeddableClass->class])) {\n                    throw MappingException::infiniteEmbeddableNesting($class->name, $property);\n                }\n\n                $this->embeddablesActiveNesting[$class->name] = true;\n\n                $embeddableMetadata = $this->getMetadataFor($embeddableClass->class);\n\n                if ($embeddableMetadata->isEmbeddedClass) {\n                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);\n                }\n\n                $identifier = $embeddableMetadata->getIdentifier();\n\n                if (! empty($identifier)) {\n                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);\n                }\n\n                $class->inlineEmbeddable($property, $embeddableMetadata);\n\n                unset($this->embeddablesActiveNesting[$class->name]);\n            }\n        }\n\n        if ($parent) {\n            if ($parent->isInheritanceTypeSingleTable()) {\n                $class->setPrimaryTable($parent->table);\n            }\n\n            $this->addInheritedIndexes($class, $parent);\n\n            if ($parent->cache) {\n                $class->cache = $parent->cache;\n            }\n\n            if ($parent->containsForeignIdentifier) {\n                $class->containsForeignIdentifier = true;\n            }\n\n            if ($parent->containsEnumIdentifier) {\n                $class->containsEnumIdentifier = true;\n            }\n\n            if (! empty($parent->entityListeners) && empty($class->entityListeners)) {\n                $class->entityListeners = $parent->entityListeners;\n            }\n        }\n\n        $class->setParentClasses($nonSuperclassParents);\n\n        if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {\n            $this->addDefaultDiscriminatorMap($class);\n        }\n\n        // During the following event, there may also be updates to the discriminator map as per GH-1257/GH-8402.\n        // So, we must not discover the missing subclasses before that.\n\n        if ($this->evm->hasListeners(Events::loadClassMetadata)) {\n            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);\n            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);\n        }\n\n        $this->findAbstractEntityClassesNotListedInDiscriminatorMap($class);\n\n        $this->validateRuntimeMetadata($class, $parent);\n    }\n\n    /**\n     * Validate runtime metadata is correctly defined.\n     *\n     * @throws MappingException\n     */\n    protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadataInterface|null $parent): void\n    {\n        if (! $class->reflClass) {\n            // only validate if there is a reflection class instance\n            return;\n        }\n\n        $class->validateIdentifier();\n        $class->validateAssociations();\n        $class->validateLifecycleCallbacks($this->getReflectionService());\n\n        // verify inheritance\n        if (! $class->isMappedSuperclass && ! $class->isInheritanceTypeNone()) {\n            if (! $parent) {\n                if (count($class->discriminatorMap) === 0) {\n                    throw MappingException::missingDiscriminatorMap($class->name);\n                }\n\n                if (! $class->discriminatorColumn) {\n                    throw MappingException::missingDiscriminatorColumn($class->name);\n                }\n\n                foreach ($class->subClasses as $subClass) {\n                    if ((new ReflectionClass($subClass))->name !== $subClass) {\n                        throw MappingException::invalidClassInDiscriminatorMap($subClass, $class->name);\n                    }\n                }\n            } else {\n                assert($parent instanceof ClassMetadata); // https://github.com/doctrine/orm/issues/8746\n                if (\n                    ! $class->reflClass->isAbstract()\n                    && ! in_array($class->name, $class->discriminatorMap, true)\n                ) {\n                    throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);\n                }\n            }\n        } elseif ($class->isMappedSuperclass && $class->name === $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {\n            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy\n            throw MappingException::noInheritanceOnMappedSuperClass($class->name);\n        }\n    }\n\n    protected function newClassMetadataInstance(string $className): ClassMetadata\n    {\n        return new ClassMetadata(\n            $className,\n            $this->em->getConfiguration()->getNamingStrategy(),\n            $this->em->getConfiguration()->getTypedFieldMapper(),\n        );\n    }\n\n    /**\n     * Adds a default discriminator map if no one is given\n     *\n     * If an entity is of any inheritance type and does not contain a\n     * discriminator map, then the map is generated automatically. This process\n     * is expensive computation wise.\n     *\n     * The automatically generated discriminator map contains the lowercase short name of\n     * each class as key.\n     *\n     * @throws MappingException\n     */\n    private function addDefaultDiscriminatorMap(ClassMetadata $class): void\n    {\n        $allClasses = $this->driver->getAllClassNames();\n        $fqcn       = $class->getName();\n        $map        = [$this->getShortName($class->name) => $fqcn];\n\n        $duplicates = [];\n        foreach ($allClasses as $subClassCandidate) {\n            if (is_subclass_of($subClassCandidate, $fqcn)) {\n                $shortName = $this->getShortName($subClassCandidate);\n\n                if (isset($map[$shortName])) {\n                    $duplicates[] = $shortName;\n                }\n\n                $map[$shortName] = $subClassCandidate;\n            }\n        }\n\n        if ($duplicates) {\n            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);\n        }\n\n        $class->setDiscriminatorMap($map);\n    }\n\n    private function findAbstractEntityClassesNotListedInDiscriminatorMap(ClassMetadata $rootEntityClass): void\n    {\n        // Only root classes in inheritance hierarchies need contain a discriminator map,\n        // so skip for other classes.\n        if (! $rootEntityClass->isRootEntity() || $rootEntityClass->isInheritanceTypeNone()) {\n            return;\n        }\n\n        $processedClasses = [$rootEntityClass->name => true];\n        foreach ($rootEntityClass->subClasses as $knownSubClass) {\n            $processedClasses[$knownSubClass] = true;\n        }\n\n        foreach ($rootEntityClass->discriminatorMap as $declaredClassName) {\n            // This fetches non-transient parent classes only\n            $parentClasses = $this->getParentClasses($declaredClassName);\n\n            foreach ($parentClasses as $parentClass) {\n                if (isset($processedClasses[$parentClass])) {\n                    continue;\n                }\n\n                $processedClasses[$parentClass] = true;\n\n                // All non-abstract entity classes must be listed in the discriminator map, and\n                // this will be validated/enforced at runtime (possibly at a later time, when the\n                // subclass is loaded, but anyways). Also, subclasses is about entity classes only.\n                // That means we can ignore non-abstract classes here. The (expensive) driver\n                // check for mapped superclasses need only be run for abstract candidate classes.\n                if (! (new ReflectionClass($parentClass))->isAbstract() || $this->peekIfIsMappedSuperclass($parentClass)) {\n                    continue;\n                }\n\n                // We have found a non-transient, non-mapped-superclass = an entity class (possibly abstract, but that does not matter)\n                $rootEntityClass->addSubClass($parentClass);\n            }\n        }\n    }\n\n    /** @param class-string $className */\n    private function peekIfIsMappedSuperclass(string $className): bool\n    {\n        $reflService = $this->getReflectionService();\n        $class       = $this->newClassMetadataInstance($className);\n        $this->initializeReflection($class, $reflService);\n\n        $this->getDriver()->loadMetadataForClass($className, $class);\n\n        return $class->isMappedSuperclass;\n    }\n\n    /**\n     * Gets the lower-case short name of a class.\n     *\n     * @param class-string $className\n     */\n    private function getShortName(string $className): string\n    {\n        if (! str_contains($className, '\\\\')) {\n            return strtolower($className);\n        }\n\n        $parts = explode('\\\\', $className);\n\n        return strtolower(end($parts));\n    }\n\n    /**\n     * Puts the `inherited` and `declared` values into mapping information for fields, associations\n     * and embedded classes.\n     */\n    private function addMappingInheritanceInformation(\n        AssociationMapping|EmbeddedClassMapping|FieldMapping $mapping,\n        ClassMetadata $parentClass,\n    ): void {\n        if (! isset($mapping->inherited) && ! $parentClass->isMappedSuperclass) {\n            $mapping->inherited = $parentClass->name;\n        }\n\n        if (! isset($mapping->declared)) {\n            $mapping->declared = $parentClass->name;\n        }\n    }\n\n    /**\n     * Adds inherited fields to the subclass mapping.\n     */\n    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass): void\n    {\n        foreach ($parentClass->fieldMappings as $mapping) {\n            $subClassMapping = clone $mapping;\n            $this->addMappingInheritanceInformation($subClassMapping, $parentClass);\n            $subClass->addInheritedFieldMapping($subClassMapping);\n        }\n\n        foreach ($parentClass->propertyAccessors as $name => $field) {\n            $subClass->propertyAccessors[$name] = $field;\n        }\n    }\n\n    /**\n     * Adds inherited association mappings to the subclass mapping.\n     *\n     * @throws MappingException\n     */\n    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass): void\n    {\n        foreach ($parentClass->associationMappings as $field => $mapping) {\n            $subClassMapping = clone $mapping;\n            $this->addMappingInheritanceInformation($subClassMapping, $parentClass);\n            // When the class inheriting the relation ($subClass) is the first entity class since the\n            // relation has been defined in a mapped superclass (or in a chain\n            // of mapped superclasses) above, then declare this current entity class as the source of\n            // the relationship.\n            // According to the definitions given in https://github.com/doctrine/orm/pull/10396/,\n            // this is the case <=> ! isset($mapping['inherited']).\n            if (! isset($subClassMapping->inherited)) {\n                $subClassMapping->sourceEntity = $subClass->name;\n            }\n\n            $subClass->addInheritedAssociationMapping($subClassMapping);\n        }\n    }\n\n    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass): void\n    {\n        foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {\n            $subClassMapping = clone $embeddedClass;\n            $this->addMappingInheritanceInformation($subClassMapping, $parentClass);\n            $subClass->embeddedClasses[$field] = $subClassMapping;\n        }\n    }\n\n    /**\n     * Adds nested embedded classes metadata to a parent class.\n     *\n     * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.\n     * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.\n     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.\n     */\n    private function addNestedEmbeddedClasses(\n        ClassMetadata $subClass,\n        ClassMetadata $parentClass,\n        string $prefix,\n    ): void {\n        foreach ($subClass->embeddedClasses as $property => $embeddableClass) {\n            if (isset($embeddableClass->inherited)) {\n                continue;\n            }\n\n            $embeddableMetadata = $this->getMetadataFor($embeddableClass->class);\n\n            $parentClass->mapEmbedded(\n                [\n                    'fieldName' => $prefix . '.' . $property,\n                    'class' => $embeddableMetadata->name,\n                    'columnPrefix' => $embeddableClass->columnPrefix,\n                    'declaredField' => $embeddableClass->declaredField\n                            ? $prefix . '.' . $embeddableClass->declaredField\n                            : $prefix,\n                    'originalField' => $embeddableClass->originalField ?: $property,\n                ],\n            );\n        }\n    }\n\n    /**\n     * Copy the table indices from the parent class superclass to the child class\n     */\n    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass): void\n    {\n        if (! $parentClass->isMappedSuperclass) {\n            return;\n        }\n\n        foreach (['uniqueConstraints', 'indexes'] as $indexType) {\n            if (isset($parentClass->table[$indexType])) {\n                foreach ($parentClass->table[$indexType] as $indexName => $index) {\n                    if (isset($subClass->table[$indexType][$indexName])) {\n                        continue; // Let the inheriting table override indices\n                    }\n\n                    $subClass->table[$indexType][$indexName] = $index;\n                }\n            }\n        }\n    }\n\n    /**\n     * Completes the ID generator mapping. If \"auto\" is specified we choose the generator\n     * most appropriate for the targeted database platform.\n     *\n     * @throws ORMException\n     */\n    private function completeIdGeneratorMapping(ClassMetadata $class): void\n    {\n        $idGenType = $class->generatorType;\n        if ($idGenType === ClassMetadata::GENERATOR_TYPE_AUTO) {\n            $class->setIdGeneratorType($this->determineIdGeneratorStrategy($this->getTargetPlatform()));\n        }\n\n        // Create & assign an appropriate ID generator instance\n        switch ($class->generatorType) {\n            case ClassMetadata::GENERATOR_TYPE_IDENTITY:\n                $sequenceName = null;\n                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;\n                $platform     = $this->getTargetPlatform();\n\n                $generator = $fieldName && $class->fieldMappings[$fieldName]->type === 'bigint'\n                    ? new BigIntegerIdentityGenerator()\n                    : new IdentityGenerator();\n\n                $class->setIdGenerator($generator);\n\n                break;\n\n            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:\n                // If there is no sequence definition yet, create a default definition\n                $definition = $class->sequenceGeneratorDefinition;\n\n                if (! $definition) {\n                    $fieldName    = $class->getSingleIdentifierFieldName();\n                    $sequenceName = $class->getSequenceName($this->getTargetPlatform());\n                    $quoted       = isset($class->fieldMappings[$fieldName]->quoted) || isset($class->table['quoted']);\n\n                    $definition = [\n                        'sequenceName'      => $this->truncateSequenceName($sequenceName),\n                        'allocationSize'    => 1,\n                        'initialValue'      => 1,\n                    ];\n\n                    if ($quoted) {\n                        $definition['quoted'] = true;\n                    }\n\n                    $class->setSequenceGeneratorDefinition($definition);\n                }\n\n                $sequenceGenerator = new SequenceGenerator(\n                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()),\n                    (int) $definition['allocationSize'],\n                );\n                $class->setIdGenerator($sequenceGenerator);\n                break;\n\n            case ClassMetadata::GENERATOR_TYPE_NONE:\n                $class->setIdGenerator(new AssignedGenerator());\n                break;\n\n            case ClassMetadata::GENERATOR_TYPE_CUSTOM:\n                $definition = $class->customGeneratorDefinition;\n                if ($definition === null) {\n                    throw InvalidCustomGenerator::onClassNotConfigured();\n                }\n\n                if (! class_exists($definition['class'])) {\n                    throw InvalidCustomGenerator::onMissingClass($definition);\n                }\n\n                $class->setIdGenerator(new $definition['class']());\n                break;\n\n            default:\n                throw UnknownGeneratorType::create($class->generatorType);\n        }\n    }\n\n    /** @phpstan-return ClassMetadata::GENERATOR_TYPE_* */\n    private function determineIdGeneratorStrategy(AbstractPlatform $platform): int\n    {\n        assert($this->em !== null);\n        foreach ($this->em->getConfiguration()->getIdentityGenerationPreferences() as $platformFamily => $strategy) {\n            if (is_a($platform, $platformFamily)) {\n                return $strategy;\n            }\n        }\n\n        $nonIdentityDefaultStrategy = self::NON_IDENTITY_DEFAULT_STRATEGY;\n\n        // DBAL 3\n        if (method_exists($platform, 'getIdentitySequenceName')) {\n            $nonIdentityDefaultStrategy[Platforms\\PostgreSQLPlatform::class] = ClassMetadata::GENERATOR_TYPE_SEQUENCE;\n        }\n\n        foreach ($nonIdentityDefaultStrategy as $platformFamily => $strategy) {\n            if (is_a($platform, $platformFamily)) {\n                if ($platform instanceof Platforms\\PostgreSQLPlatform) {\n                    Deprecation::trigger(\n                        'doctrine/orm',\n                        'https://github.com/doctrine/orm/issues/8893',\n                        <<<'DEPRECATION'\n                        Relying on non-optimal defaults for ID generation is deprecated, and IDENTITY\n                        results in SERIAL, which is not recommended.\n                        Instead, configure identifier generation strategies explicitly through\n                        configuration.\n                        We currently recommend \"SEQUENCE\" for \"%s\", when using DBAL 3,\n                        and \"IDENTITY\" when using DBAL 4,\n                        so you should probably use the following configuration before upgrading to DBAL 4,\n                        and remove it after deploying that upgrade:\n\n                        $configuration->setIdentityGenerationPreferences([\n                            \"%s\" => ClassMetadata::GENERATOR_TYPE_SEQUENCE,\n                        ]);\n\n                        DEPRECATION,\n                        $platformFamily,\n                        $platformFamily,\n                    );\n                }\n\n                return $strategy;\n            }\n        }\n\n        return ClassMetadata::GENERATOR_TYPE_IDENTITY;\n    }\n\n    private function truncateSequenceName(string $schemaElementName): string\n    {\n        $platform = $this->getTargetPlatform();\n        if (! $platform instanceof Platforms\\OraclePlatform) {\n            return $schemaElementName;\n        }\n\n        $maxIdentifierLength = $platform->getMaxIdentifierLength();\n\n        if (strlen($schemaElementName) > $maxIdentifierLength) {\n            return substr($schemaElementName, 0, $maxIdentifierLength);\n        }\n\n        return $schemaElementName;\n    }\n\n    /**\n     * Inherits the ID generator mapping from a parent class.\n     */\n    private function inheritIdGeneratorMapping(ClassMetadata $class, ClassMetadata $parent): void\n    {\n        if ($parent->isIdGeneratorSequence()) {\n            $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);\n        }\n\n        if ($parent->generatorType) {\n            $class->setIdGeneratorType($parent->generatorType);\n        }\n\n        if ($parent->idGenerator ?? null) {\n            $class->setIdGenerator($parent->idGenerator);\n        }\n    }\n\n    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService): void\n    {\n        $class->wakeupReflection($reflService);\n\n        if (PHP_VERSION_ID < 80400) {\n            return;\n        }\n\n        foreach ($class->propertyAccessors as $propertyAccessor) {\n            $property = $propertyAccessor->getUnderlyingReflector();\n\n            if ($property->isVirtual()) {\n                throw MappingException::mappingVirtualPropertyNotAllowed($class->name, $property->getName());\n            }\n        }\n    }\n\n    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService): void\n    {\n        $class->initializeReflection($reflService);\n    }\n\n    protected function getDriver(): MappingDriver\n    {\n        assert($this->driver !== null);\n\n        return $this->driver;\n    }\n\n    protected function isEntity(ClassMetadataInterface $class): bool\n    {\n        return ! $class->isMappedSuperclass;\n    }\n\n    private function getTargetPlatform(): Platforms\\AbstractPlatform\n    {\n        if (! $this->targetPlatform) {\n            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();\n        }\n\n        return $this->targetPlatform;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Column.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\nuse BackedEnum;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class Column implements MappingAttribute\n{\n    /**\n     * @param int|null                      $precision The precision for a decimal (exact numeric) column (Applies only for decimal column).\n     * @param int|null                      $scale     The scale for a decimal (exact numeric) column (Applies only for decimal column).\n     * @param class-string<BackedEnum>|null $enumType\n     * @param array<string,mixed>           $options\n     * @phpstan-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated\n     */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly string|null $type = null,\n        public readonly int|null $length = null,\n        public readonly int|null $precision = null,\n        public readonly int|null $scale = null,\n        public readonly bool $unique = false,\n        public readonly bool $nullable = false,\n        public readonly bool $insertable = true,\n        public readonly bool $updatable = true,\n        public readonly string|null $enumType = null,\n        public readonly array $options = [],\n        public readonly string|null $columnDefinition = null,\n        public readonly string|null $generated = null,\n        public readonly bool $index = false,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/CustomIdGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class CustomIdGenerator implements MappingAttribute\n{\n    public function __construct(\n        public readonly string|null $class = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DefaultEntityListenerResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse function trim;\n\n/**\n * The default DefaultEntityListener\n */\nclass DefaultEntityListenerResolver implements EntityListenerResolver\n{\n    /** @var array<class-string, object> Map to store entity listener instances. */\n    private array $instances = [];\n\n    public function clear(string|null $className = null): void\n    {\n        if ($className === null) {\n            $this->instances = [];\n\n            return;\n        }\n\n        $className = trim($className, '\\\\');\n        unset($this->instances[$className]);\n    }\n\n    public function register(object $object): void\n    {\n        $this->instances[$object::class] = $object;\n    }\n\n    public function resolve(string $className): object\n    {\n        $className = trim($className, '\\\\');\n\n        return $this->instances[$className] ??= new $className();\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DefaultNamingStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse function str_contains;\nuse function strrpos;\nuse function strtolower;\nuse function substr;\n\n/**\n * The default NamingStrategy\n *\n * @link    www.doctrine-project.org\n */\nclass DefaultNamingStrategy implements NamingStrategy\n{\n    public function classToTableName(string $className): string\n    {\n        if (str_contains($className, '\\\\')) {\n            return substr($className, strrpos($className, '\\\\') + 1);\n        }\n\n        return $className;\n    }\n\n    public function propertyToColumnName(string $propertyName, string $className): string\n    {\n        return $propertyName;\n    }\n\n    public function embeddedFieldToColumnName(\n        string $propertyName,\n        string $embeddedColumnName,\n        string $className,\n        string $embeddedClassName,\n    ): string {\n        return $propertyName . '_' . $embeddedColumnName;\n    }\n\n    public function referenceColumnName(): string\n    {\n        return 'id';\n    }\n\n    public function joinColumnName(string $propertyName, string $className): string\n    {\n        return $propertyName . '_' . $this->referenceColumnName();\n    }\n\n    public function joinTableName(\n        string $sourceEntity,\n        string $targetEntity,\n        string $propertyName,\n    ): string {\n        return strtolower($this->classToTableName($sourceEntity) . '_' .\n            $this->classToTableName($targetEntity));\n    }\n\n    public function joinKeyColumnName(\n        string $entityName,\n        string|null $referencedColumnName,\n    ): string {\n        return strtolower($this->classToTableName($entityName) . '_' .\n            ($referencedColumnName ?: $this->referenceColumnName()));\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DefaultQuoteStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\n\nuse function array_map;\nuse function array_merge;\nuse function assert;\nuse function explode;\nuse function implode;\nuse function is_numeric;\nuse function preg_replace;\nuse function sprintf;\nuse function substr;\n\n/**\n * A set of rules for determining the physical column, alias and table quotes\n */\nclass DefaultQuoteStrategy implements QuoteStrategy\n{\n    use SQLResultCasing;\n\n    public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return isset($class->fieldMappings[$fieldName]->quoted)\n            ? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName)\n            : $class->fieldMappings[$fieldName]->columnName;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @todo Table names should be computed in DBAL depending on the platform\n     */\n    public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        $tableName = $class->table['name'];\n\n        if (! empty($class->table['schema'])) {\n            return isset($class->table['quoted'])\n                ? sprintf(\n                    '%s.%s',\n                    $platform->quoteSingleIdentifier($class->table['schema']),\n                    $platform->quoteSingleIdentifier($tableName),\n                )\n                : $class->table['schema'] . '.' . $class->table['name'];\n        }\n\n        return isset($class->table['quoted'])\n            ? $platform->quoteSingleIdentifier($tableName)\n            : $tableName;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return isset($definition['quoted'])\n            ? implode('.', array_map(\n                static fn (string $part) => $platform->quoteSingleIdentifier($part),\n                explode('.', $definition['sequenceName']),\n            ))\n            : $definition['sequenceName'];\n    }\n\n    public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string\n    {\n        return isset($joinColumn->quoted)\n            ? $platform->quoteSingleIdentifier($joinColumn->name)\n            : $joinColumn->name;\n    }\n\n    public function getReferencedJoinColumnName(\n        JoinColumnMapping $joinColumn,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string {\n        return isset($joinColumn->quoted)\n            ? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName)\n            : $joinColumn->referencedColumnName;\n    }\n\n    public function getJoinTableName(\n        ManyToManyOwningSideMapping $association,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string {\n        $schema = '';\n\n        if (isset($association->joinTable->schema)) {\n            $schema = $association->joinTable->schema . '.';\n        }\n\n        $tableName = $association->joinTable->name;\n\n        if (isset($association->joinTable->quoted)) {\n            $tableName = $platform->quoteSingleIdentifier($tableName);\n        }\n\n        return $schema . $tableName;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array\n    {\n        $quotedColumnNames = [];\n\n        foreach ($class->identifier as $fieldName) {\n            if (isset($class->fieldMappings[$fieldName])) {\n                $quotedColumnNames[] = $this->getColumnName($fieldName, $class, $platform);\n\n                continue;\n            }\n\n            // Association defined as Id field\n            $assoc = $class->associationMappings[$fieldName];\n            assert($assoc->isToOneOwningSide());\n            $joinColumns            = $assoc->joinColumns;\n            $assocQuotedColumnNames = array_map(\n                static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted)\n                    ? $platform->quoteSingleIdentifier($joinColumn->name)\n                    : $joinColumn->name,\n                $joinColumns,\n            );\n\n            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);\n        }\n\n        return $quotedColumnNames;\n    }\n\n    public function getColumnAlias(\n        string $columnName,\n        int $counter,\n        AbstractPlatform $platform,\n        ClassMetadata|null $class = null,\n    ): string {\n        // 1 ) Concatenate column name and counter\n        // 2 ) Trim the column alias to the maximum identifier length of the platform.\n        //     If the alias is to long, characters are cut off from the beginning.\n        // 3 ) Strip non alphanumeric characters\n        // 4 ) Prefix with \"_\" if the result its numeric\n        $columnName .= '_' . $counter;\n        $columnName  = substr($columnName, -$platform->getMaxIdentifierLength());\n        $columnName  = preg_replace('/[^A-Za-z0-9_]/', '', $columnName);\n        $columnName  = is_numeric($columnName) ? '_' . $columnName : $columnName;\n\n        return $this->getSQLResultCasing($platform, $columnName);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DefaultTypedFieldMapper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse BackedEnum;\nuse BcMath\\Number;\nuse DateInterval;\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse ReflectionEnum;\nuse ReflectionNamedType;\nuse ReflectionProperty;\n\nuse function array_merge;\nuse function assert;\nuse function defined;\nuse function enum_exists;\nuse function is_a;\n\n/** @phpstan-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */\nfinal class DefaultTypedFieldMapper implements TypedFieldMapper\n{\n    /** @var array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */\n    private array $typedFieldMappings;\n\n    private const DEFAULT_TYPED_FIELD_MAPPINGS = [\n        DateInterval::class => Types::DATEINTERVAL,\n        DateTime::class => Types::DATETIME_MUTABLE,\n        DateTimeImmutable::class => Types::DATETIME_IMMUTABLE,\n        'array' => Types::JSON,\n        'bool' => Types::BOOLEAN,\n        'float' => Types::FLOAT,\n        'int' => Types::INTEGER,\n        'string' => Types::STRING,\n    ];\n\n    /** @param array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */\n    public function __construct(array $typedFieldMappings = [])\n    {\n        $defaultMappings = self::DEFAULT_TYPED_FIELD_MAPPINGS;\n        if (defined(Types::class . '::NUMBER')) { // DBAL 4.3+\n            $defaultMappings[Number::class] = Types::NUMBER;\n        }\n\n        $this->typedFieldMappings = array_merge($defaultMappings, $typedFieldMappings);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function validateAndComplete(array $mapping, ReflectionProperty $field): array\n    {\n        $type = $field->getType();\n\n        if (! $type instanceof ReflectionNamedType) {\n            return $mapping;\n        }\n\n        if (\n            ! $type->isBuiltin()\n            && enum_exists($type->getName())\n            && (! isset($mapping['type']) || (\n                defined('Doctrine\\DBAL\\Types\\Types::ENUM')\n                && $mapping['type'] === Types::ENUM\n            ))\n        ) {\n            $reflection = new ReflectionEnum($type->getName());\n            if (! $reflection->isBacked()) {\n                throw MappingException::backedEnumTypeRequired(\n                    $field->class,\n                    $mapping['fieldName'],\n                    $type->getName(),\n                );\n            }\n\n            assert(is_a($type->getName(), BackedEnum::class, true));\n            $mapping['enumType'] = $type->getName();\n            $type                = $reflection->getBackingType();\n        }\n\n        if (isset($mapping['type'])) {\n            return $mapping;\n        }\n\n        if (isset($this->typedFieldMappings[$type->getName()])) {\n            $mapping['type'] = $this->typedFieldMappings[$type->getName()];\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DiscriminatorColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\nuse BackedEnum;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class DiscriminatorColumn implements MappingAttribute\n{\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly string|null $type = null,\n        public readonly int|null $length = null,\n        public readonly string|null $columnDefinition = null,\n        /** @var class-string<BackedEnum>|null */\n        public readonly string|null $enumType = null,\n        /** @var array<string, mixed> */\n        public readonly array $options = [],\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DiscriminatorColumnMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\nuse BackedEnum;\nuse Exception;\n\nuse function in_array;\nuse function property_exists;\n\n/** @template-implements ArrayAccess<string, mixed> */\nfinal class DiscriminatorColumnMapping implements ArrayAccess\n{\n    use ArrayAccessImplementation;\n\n    /** The database length of the column. Optional. Default value taken from the type. */\n    public int|null $length = null;\n\n    public string|null $columnDefinition = null;\n\n    /** @var class-string<BackedEnum>|null */\n    public string|null $enumType = null;\n\n    /** @var array<string, mixed> */\n    public array $options = [];\n\n    public function __construct(\n        public string $type,\n        public string $fieldName,\n        public string $name,\n    ) {\n    }\n\n    /**\n     * @phpstan-param array{\n     *     type: string,\n     *     fieldName: string,\n     *     name: string,\n     *     length?: int|null,\n     *     columnDefinition?: string|null,\n     *     enumType?: class-string<BackedEnum>|null,\n     *     options?: array<string, mixed>|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): self\n    {\n        $mapping = new self(\n            $mappingArray['type'],\n            $mappingArray['fieldName'],\n            $mappingArray['name'],\n        );\n        foreach ($mappingArray as $key => $value) {\n            if (in_array($key, ['type', 'fieldName', 'name'])) {\n                continue;\n            }\n\n            if (property_exists($mapping, $key)) {\n                $mapping->$key = $value ?? $mapping->$key;\n            } else {\n                throw new Exception('Unknown property ' . $key . ' on class ' . static::class);\n            }\n        }\n\n        return $mapping;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = ['type', 'fieldName', 'name'];\n\n        foreach (['length', 'columnDefinition', 'enumType', 'options'] as $stringOrArrayKey) {\n            if ($this->$stringOrArrayKey !== null) {\n                $serialized[] = $stringOrArrayKey;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/DiscriminatorMap.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class DiscriminatorMap implements MappingAttribute\n{\n    /** @param array<int|string, string> $value */\n    public function __construct(\n        public readonly array $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/AttributeDriver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping;\nuse Doctrine\\ORM\\Mapping\\Builder\\EntityListenerBuilder;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata as PersistenceClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ClassLocator;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ColocatedMappingDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse InvalidArgumentException;\nuse ReflectionClass;\nuse ReflectionMethod;\n\nuse function assert;\nuse function class_exists;\nuse function constant;\nuse function defined;\nuse function sprintf;\n\nclass AttributeDriver implements MappingDriver\n{\n    use ColocatedMappingDriver;\n    use ReflectionBasedDriver;\n\n    private const ENTITY_ATTRIBUTE_CLASSES = [\n        Mapping\\Entity::class => 1,\n        Mapping\\MappedSuperclass::class => 2,\n    ];\n\n    private readonly AttributeReader $reader;\n\n    /**\n     * @param string[]|ClassLocator $paths                     a ClassLocator, or an array of directories.\n     * @param true                  $reportFieldsWhereDeclared no-op, to be removed in 4.0\n     */\n    public function __construct(array|ClassLocator $paths, bool $reportFieldsWhereDeclared = true)\n    {\n        if (! $reportFieldsWhereDeclared) {\n            throw new InvalidArgumentException(sprintf(\n                'The $reportFieldsWhereDeclared argument is no longer supported, make sure to omit it when calling %s.',\n                __METHOD__,\n            ));\n        }\n\n        $this->reader = new AttributeReader();\n\n        if ($paths instanceof ClassLocator) {\n            $this->classLocator = $paths;\n        } else {\n            $this->addPaths($paths);\n        }\n    }\n\n    public function isTransient(string $className): bool\n    {\n        $classAttributes = $this->reader->getClassAttributes(new ReflectionClass($className));\n\n        foreach ($classAttributes as $a) {\n            $attr = $a instanceof RepeatableAttributeCollection ? $a[0] : $a;\n            if (isset(self::ENTITY_ATTRIBUTE_CLASSES[$attr::class])) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param class-string<T>  $className\n     * @param ClassMetadata<T> $metadata\n     *\n     * @template T of object\n     */\n    public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void\n    {\n        $reflectionClass = $metadata->getReflectionClass()\n            // this happens when running attribute driver in combination with\n            // static reflection services. This is not the nicest fix\n            ?? new ReflectionClass($metadata->name);\n\n        $classAttributes = $this->reader->getClassAttributes($reflectionClass);\n\n        // Evaluate Entity attribute\n        if (isset($classAttributes[Mapping\\Entity::class])) {\n            $entityAttribute = $classAttributes[Mapping\\Entity::class];\n            if ($entityAttribute->repositoryClass !== null) {\n                $metadata->setCustomRepositoryClass($entityAttribute->repositoryClass);\n            }\n\n            if ($entityAttribute->readOnly) {\n                $metadata->markReadOnly();\n            }\n        } elseif (isset($classAttributes[Mapping\\MappedSuperclass::class])) {\n            $mappedSuperclassAttribute = $classAttributes[Mapping\\MappedSuperclass::class];\n\n            $metadata->setCustomRepositoryClass($mappedSuperclassAttribute->repositoryClass);\n            $metadata->isMappedSuperclass = true;\n        } elseif (isset($classAttributes[Mapping\\Embeddable::class])) {\n            $metadata->isEmbeddedClass = true;\n        } else {\n            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);\n        }\n\n        $primaryTable = [];\n\n        if (isset($classAttributes[Mapping\\Table::class])) {\n            $tableAnnot             = $classAttributes[Mapping\\Table::class];\n            $primaryTable['name']   = $tableAnnot->name;\n            $primaryTable['schema'] = $tableAnnot->schema;\n\n            if ($tableAnnot->options) {\n                $primaryTable['options'] = $tableAnnot->options;\n            }\n        }\n\n        if (isset($classAttributes[Mapping\\Index::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\Index::class);\n            }\n\n            foreach ($classAttributes[Mapping\\Index::class] as $idx => $indexAnnot) {\n                $index = [];\n\n                if (! empty($indexAnnot->columns)) {\n                    $index['columns'] = $indexAnnot->columns;\n                }\n\n                if (! empty($indexAnnot->fields)) {\n                    $index['fields'] = $indexAnnot->fields;\n                }\n\n                if (\n                    isset($index['columns'], $index['fields'])\n                    || (\n                        ! isset($index['columns'])\n                        && ! isset($index['fields'])\n                    )\n                ) {\n                    throw MappingException::invalidIndexConfiguration(\n                        $className,\n                        (string) ($indexAnnot->name ?? $idx),\n                    );\n                }\n\n                if (! empty($indexAnnot->flags)) {\n                    $index['flags'] = $indexAnnot->flags;\n                }\n\n                if (! empty($indexAnnot->options)) {\n                    $index['options'] = $indexAnnot->options;\n                }\n\n                if (! empty($indexAnnot->name)) {\n                    $primaryTable['indexes'][$indexAnnot->name] = $index;\n                } else {\n                    $primaryTable['indexes'][] = $index;\n                }\n            }\n        }\n\n        if (isset($classAttributes[Mapping\\UniqueConstraint::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\UniqueConstraint::class);\n            }\n\n            foreach ($classAttributes[Mapping\\UniqueConstraint::class] as $idx => $uniqueConstraintAnnot) {\n                $uniqueConstraint = [];\n\n                if (! empty($uniqueConstraintAnnot->columns)) {\n                    $uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns;\n                }\n\n                if (! empty($uniqueConstraintAnnot->fields)) {\n                    $uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields;\n                }\n\n                if (\n                    isset($uniqueConstraint['columns'], $uniqueConstraint['fields'])\n                    || (\n                        ! isset($uniqueConstraint['columns'])\n                        && ! isset($uniqueConstraint['fields'])\n                    )\n                ) {\n                    throw MappingException::invalidUniqueConstraintConfiguration(\n                        $className,\n                        (string) ($uniqueConstraintAnnot->name ?? $idx),\n                    );\n                }\n\n                if (! empty($uniqueConstraintAnnot->options)) {\n                    $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;\n                }\n\n                if (! empty($uniqueConstraintAnnot->name)) {\n                    $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;\n                } else {\n                    $primaryTable['uniqueConstraints'][] = $uniqueConstraint;\n                }\n            }\n        }\n\n        $metadata->setPrimaryTable($primaryTable);\n\n        // Evaluate #[Cache] attribute\n        if (isset($classAttributes[Mapping\\Cache::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\Cache::class);\n            }\n\n            $cacheAttribute = $classAttributes[Mapping\\Cache::class];\n            $cacheMap       = [\n                'region' => $cacheAttribute->region,\n                'usage'  => constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $cacheAttribute->usage),\n            ];\n\n            $metadata->enableCache($cacheMap);\n        }\n\n        // Evaluate InheritanceType attribute\n        if (isset($classAttributes[Mapping\\InheritanceType::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\InheritanceType::class);\n            }\n\n            $inheritanceTypeAttribute = $classAttributes[Mapping\\InheritanceType::class];\n\n            $metadata->setInheritanceType(\n                constant('Doctrine\\ORM\\Mapping\\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value),\n            );\n\n            if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {\n                // Evaluate DiscriminatorColumn attribute\n                if (isset($classAttributes[Mapping\\DiscriminatorColumn::class])) {\n                    $discrColumnAttribute = $classAttributes[Mapping\\DiscriminatorColumn::class];\n                    assert($discrColumnAttribute instanceof Mapping\\DiscriminatorColumn);\n\n                    $columnDef = [\n                        'name'             => $discrColumnAttribute->name,\n                        'type'             => $discrColumnAttribute->type ?? 'string',\n                        'length'           => $discrColumnAttribute->length ?? 255,\n                        'columnDefinition' => $discrColumnAttribute->columnDefinition,\n                        'enumType'         => $discrColumnAttribute->enumType,\n                    ];\n\n                    if ($discrColumnAttribute->options) {\n                        $columnDef['options'] = $discrColumnAttribute->options;\n                    }\n\n                    $metadata->setDiscriminatorColumn($columnDef);\n                } else {\n                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);\n                }\n\n                // Evaluate DiscriminatorMap attribute\n                if (isset($classAttributes[Mapping\\DiscriminatorMap::class])) {\n                    $discrMapAttribute = $classAttributes[Mapping\\DiscriminatorMap::class];\n                    $metadata->setDiscriminatorMap($discrMapAttribute->value);\n                }\n            }\n        }\n\n        // Evaluate DoctrineChangeTrackingPolicy attribute\n        if (isset($classAttributes[Mapping\\ChangeTrackingPolicy::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\ChangeTrackingPolicy::class);\n            }\n\n            $changeTrackingAttribute = $classAttributes[Mapping\\ChangeTrackingPolicy::class];\n            $metadata->setChangeTrackingPolicy(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CHANGETRACKING_' . $changeTrackingAttribute->value));\n        }\n\n        foreach ($reflectionClass->getProperties() as $property) {\n            if ($this->isRepeatedPropertyDeclaration($property, $metadata)) {\n                continue;\n            }\n\n            $mapping              = [];\n            $mapping['fieldName'] = $property->name;\n\n            // Evaluate #[Cache] attribute\n            $cacheAttribute = $this->reader->getPropertyAttribute($property, Mapping\\Cache::class);\n            if ($cacheAttribute !== null) {\n                $mapping['cache'] = $metadata->getAssociationCacheDefaults(\n                    $mapping['fieldName'],\n                    [\n                        'usage'  => (int) constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $cacheAttribute->usage),\n                        'region' => $cacheAttribute->region,\n                    ],\n                );\n            }\n\n            // Check for JoinColumn/JoinColumns attributes\n            $joinColumns = [];\n\n            $joinColumnAttributes = $this->reader->getPropertyAttributeCollection($property, Mapping\\JoinColumn::class);\n\n            foreach ($joinColumnAttributes as $joinColumnAttribute) {\n                $joinColumns[] = $this->joinColumnToArray($joinColumnAttribute);\n            }\n\n            // Field can only be attributed with one of:\n            // Column, OneToOne, OneToMany, ManyToOne, ManyToMany, Embedded\n            $columnAttribute     = $this->reader->getPropertyAttribute($property, Mapping\\Column::class);\n            $oneToOneAttribute   = $this->reader->getPropertyAttribute($property, Mapping\\OneToOne::class);\n            $oneToManyAttribute  = $this->reader->getPropertyAttribute($property, Mapping\\OneToMany::class);\n            $manyToOneAttribute  = $this->reader->getPropertyAttribute($property, Mapping\\ManyToOne::class);\n            $manyToManyAttribute = $this->reader->getPropertyAttribute($property, Mapping\\ManyToMany::class);\n            $embeddedAttribute   = $this->reader->getPropertyAttribute($property, Mapping\\Embedded::class);\n\n            if ($columnAttribute !== null) {\n                $mapping = $this->columnToArray($property->name, $columnAttribute);\n\n                if ($this->reader->getPropertyAttribute($property, Mapping\\Id::class)) {\n                    $mapping['id'] = true;\n                }\n\n                $generatedValueAttribute = $this->reader->getPropertyAttribute($property, Mapping\\GeneratedValue::class);\n\n                if ($generatedValueAttribute !== null) {\n                    $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAttribute->strategy));\n                }\n\n                if ($this->reader->getPropertyAttribute($property, Mapping\\Version::class)) {\n                    $metadata->setVersionMapping($mapping);\n                }\n\n                $metadata->mapField($mapping);\n\n                // Check for SequenceGenerator/TableGenerator definition\n                $seqGeneratorAttribute    = $this->reader->getPropertyAttribute($property, Mapping\\SequenceGenerator::class);\n                $customGeneratorAttribute = $this->reader->getPropertyAttribute($property, Mapping\\CustomIdGenerator::class);\n\n                if ($seqGeneratorAttribute !== null) {\n                    $metadata->setSequenceGeneratorDefinition(\n                        [\n                            'sequenceName' => $seqGeneratorAttribute->sequenceName,\n                            'allocationSize' => $seqGeneratorAttribute->allocationSize,\n                            'initialValue' => $seqGeneratorAttribute->initialValue,\n                        ],\n                    );\n                } elseif ($customGeneratorAttribute !== null) {\n                    $metadata->setCustomGeneratorDefinition(\n                        [\n                            'class' => $customGeneratorAttribute->class,\n                        ],\n                    );\n                }\n            } elseif ($oneToOneAttribute !== null) {\n                if ($metadata->isEmbeddedClass) {\n                    throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\OneToOne::class);\n                }\n\n                if ($this->reader->getPropertyAttribute($property, Mapping\\Id::class)) {\n                    $mapping['id'] = true;\n                }\n\n                $mapping['targetEntity']  = $oneToOneAttribute->targetEntity;\n                $mapping['joinColumns']   = $joinColumns;\n                $mapping['mappedBy']      = $oneToOneAttribute->mappedBy;\n                $mapping['inversedBy']    = $oneToOneAttribute->inversedBy;\n                $mapping['cascade']       = $oneToOneAttribute->cascade;\n                $mapping['orphanRemoval'] = $oneToOneAttribute->orphanRemoval;\n                $mapping['fetch']         = $this->getFetchMode($className, $oneToOneAttribute->fetch);\n                $metadata->mapOneToOne($mapping);\n            } elseif ($oneToManyAttribute !== null) {\n                if ($metadata->isEmbeddedClass) {\n                    throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\OneToMany::class);\n                }\n\n                $mapping['mappedBy']      = $oneToManyAttribute->mappedBy;\n                $mapping['targetEntity']  = $oneToManyAttribute->targetEntity;\n                $mapping['cascade']       = $oneToManyAttribute->cascade;\n                $mapping['indexBy']       = $oneToManyAttribute->indexBy;\n                $mapping['orphanRemoval'] = $oneToManyAttribute->orphanRemoval;\n                $mapping['fetch']         = $this->getFetchMode($className, $oneToManyAttribute->fetch);\n\n                $orderByAttribute = $this->reader->getPropertyAttribute($property, Mapping\\OrderBy::class);\n\n                if ($orderByAttribute !== null) {\n                    $mapping['orderBy'] = $orderByAttribute->value;\n                }\n\n                $metadata->mapOneToMany($mapping);\n            } elseif ($manyToOneAttribute !== null) {\n                if ($metadata->isEmbeddedClass) {\n                    throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\ManyToOne::class);\n                }\n\n                $idAttribute = $this->reader->getPropertyAttribute($property, Mapping\\Id::class);\n\n                if ($idAttribute !== null) {\n                    $mapping['id'] = true;\n                }\n\n                $mapping['joinColumns']  = $joinColumns;\n                $mapping['cascade']      = $manyToOneAttribute->cascade;\n                $mapping['inversedBy']   = $manyToOneAttribute->inversedBy;\n                $mapping['targetEntity'] = $manyToOneAttribute->targetEntity;\n                $mapping['fetch']        = $this->getFetchMode($className, $manyToOneAttribute->fetch);\n                $metadata->mapManyToOne($mapping);\n            } elseif ($manyToManyAttribute !== null) {\n                if ($metadata->isEmbeddedClass) {\n                    throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\ManyToMany::class);\n                }\n\n                $joinTable          = [];\n                $joinTableAttribute = $this->reader->getPropertyAttribute($property, Mapping\\JoinTable::class);\n\n                if ($joinTableAttribute !== null) {\n                    $joinTable = [\n                        'name' => $joinTableAttribute->name,\n                        'schema' => $joinTableAttribute->schema,\n                    ];\n\n                    if ($joinTableAttribute->options) {\n                        $joinTable['options'] = $joinTableAttribute->options;\n                    }\n\n                    foreach ($joinTableAttribute->joinColumns as $joinColumn) {\n                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);\n                    }\n\n                    foreach ($joinTableAttribute->inverseJoinColumns as $joinColumn) {\n                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);\n                    }\n                }\n\n                foreach ($this->reader->getPropertyAttributeCollection($property, Mapping\\JoinColumn::class) as $joinColumn) {\n                    $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);\n                }\n\n                foreach ($this->reader->getPropertyAttributeCollection($property, Mapping\\InverseJoinColumn::class) as $joinColumn) {\n                    $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);\n                }\n\n                $mapping['joinTable']     = $joinTable;\n                $mapping['targetEntity']  = $manyToManyAttribute->targetEntity;\n                $mapping['mappedBy']      = $manyToManyAttribute->mappedBy;\n                $mapping['inversedBy']    = $manyToManyAttribute->inversedBy;\n                $mapping['cascade']       = $manyToManyAttribute->cascade;\n                $mapping['indexBy']       = $manyToManyAttribute->indexBy;\n                $mapping['orphanRemoval'] = $manyToManyAttribute->orphanRemoval;\n                $mapping['fetch']         = $this->getFetchMode($className, $manyToManyAttribute->fetch);\n\n                $orderByAttribute = $this->reader->getPropertyAttribute($property, Mapping\\OrderBy::class);\n\n                if ($orderByAttribute !== null) {\n                    $mapping['orderBy'] = $orderByAttribute->value;\n                }\n\n                $metadata->mapManyToMany($mapping);\n            } elseif ($embeddedAttribute !== null) {\n                $mapping['class']        = $embeddedAttribute->class;\n                $mapping['columnPrefix'] = $embeddedAttribute->columnPrefix;\n\n                $metadata->mapEmbedded($mapping);\n            }\n        }\n\n        // Evaluate AssociationOverrides attribute\n        if (isset($classAttributes[Mapping\\AssociationOverrides::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\AssociationOverride::class);\n            }\n\n            $associationOverride = $classAttributes[Mapping\\AssociationOverrides::class];\n\n            foreach ($associationOverride->overrides as $associationOverride) {\n                $override  = [];\n                $fieldName = $associationOverride->name;\n\n                // Check for JoinColumn/JoinColumns attributes\n                if ($associationOverride->joinColumns) {\n                    $joinColumns = [];\n\n                    foreach ($associationOverride->joinColumns as $joinColumn) {\n                        $joinColumns[] = $this->joinColumnToArray($joinColumn);\n                    }\n\n                    $override['joinColumns'] = $joinColumns;\n                }\n\n                if ($associationOverride->inverseJoinColumns) {\n                    $joinColumns = [];\n\n                    foreach ($associationOverride->inverseJoinColumns as $joinColumn) {\n                        $joinColumns[] = $this->joinColumnToArray($joinColumn);\n                    }\n\n                    $override['inverseJoinColumns'] = $joinColumns;\n                }\n\n                // Check for JoinTable attributes\n                if ($associationOverride->joinTable) {\n                    $joinTableAnnot = $associationOverride->joinTable;\n                    $joinTable      = [\n                        'name'      => $joinTableAnnot->name,\n                        'schema'    => $joinTableAnnot->schema,\n                        'joinColumns' => $override['joinColumns'] ?? [],\n                        'inverseJoinColumns' => $override['inverseJoinColumns'] ?? [],\n                    ];\n\n                    unset($override['joinColumns'], $override['inverseJoinColumns']);\n\n                    $override['joinTable'] = $joinTable;\n                }\n\n                // Check for inversedBy\n                if ($associationOverride->inversedBy) {\n                    $override['inversedBy'] = $associationOverride->inversedBy;\n                }\n\n                // Check for `fetch`\n                if ($associationOverride->fetch) {\n                    $override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);\n                }\n\n                $metadata->setAssociationOverride($fieldName, $override);\n            }\n        }\n\n        // Evaluate AttributeOverrides attribute\n        if (isset($classAttributes[Mapping\\AttributeOverrides::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\AttributeOverrides::class);\n            }\n\n            $attributeOverridesAnnot = $classAttributes[Mapping\\AttributeOverrides::class];\n\n            foreach ($attributeOverridesAnnot->overrides as $attributeOverride) {\n                $mapping = $this->columnToArray($attributeOverride->name, $attributeOverride->column);\n\n                $metadata->setAttributeOverride($attributeOverride->name, $mapping);\n            }\n        }\n\n        // Evaluate EntityListeners attribute\n        if (isset($classAttributes[Mapping\\EntityListeners::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\EntityListeners::class);\n            }\n\n            $entityListenersAttribute = $classAttributes[Mapping\\EntityListeners::class];\n\n            foreach ($entityListenersAttribute->value as $item) {\n                $listenerClassName = $metadata->fullyQualifiedClassName($item);\n\n                if (! class_exists($listenerClassName)) {\n                    throw MappingException::entityListenerClassNotFound($listenerClassName, $className);\n                }\n\n                $hasMapping    = false;\n                $listenerClass = new ReflectionClass($listenerClassName);\n\n                foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {\n                    // find method callbacks.\n                    $callbacks  = $this->getMethodCallbacks($method);\n                    $hasMapping = $hasMapping ?: ! empty($callbacks);\n\n                    foreach ($callbacks as $value) {\n                        $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);\n                    }\n                }\n\n                // Evaluate the listener using naming convention.\n                if (! $hasMapping) {\n                    EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName);\n                }\n            }\n        }\n\n        // Evaluate #[HasLifecycleCallbacks] attribute\n        if (isset($classAttributes[Mapping\\HasLifecycleCallbacks::class])) {\n            if ($metadata->isEmbeddedClass) {\n                throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\\HasLifecycleCallbacks::class);\n            }\n\n            foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {\n                foreach ($this->getMethodCallbacks($method) as $value) {\n                    $metadata->addLifecycleCallback($value[0], $value[1]);\n                }\n            }\n        }\n    }\n\n    /**\n     * Attempts to resolve the fetch mode.\n     *\n     * @param class-string $className The class name.\n     * @param string       $fetchMode The fetch mode.\n     *\n     * @return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.\n     *\n     * @throws MappingException If the fetch mode is not valid.\n     */\n    private function getFetchMode(string $className, string $fetchMode): int\n    {\n        if (! defined('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . $fetchMode)) {\n            throw MappingException::invalidFetchMode($className, $fetchMode);\n        }\n\n        return constant('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . $fetchMode);\n    }\n\n    /**\n     * Attempts to resolve the generated mode.\n     *\n     * @throws MappingException If the fetch mode is not valid.\n     */\n    private function getGeneratedMode(string $generatedMode): int\n    {\n        if (! defined('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATED_' . $generatedMode)) {\n            throw MappingException::invalidGeneratedMode($generatedMode);\n        }\n\n        return constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATED_' . $generatedMode);\n    }\n\n    /**\n     * Parses the given method.\n     *\n     * @return list<array{string, string}>\n     * @phpstan-return list<array{string, (Events::*)}>\n     */\n    private function getMethodCallbacks(ReflectionMethod $method): array\n    {\n        $callbacks  = [];\n        $attributes = $this->reader->getMethodAttributes($method);\n\n        foreach ($attributes as $attribute) {\n            if ($attribute instanceof Mapping\\PrePersist) {\n                $callbacks[] = [$method->name, Events::prePersist];\n            }\n\n            if ($attribute instanceof Mapping\\PostPersist) {\n                $callbacks[] = [$method->name, Events::postPersist];\n            }\n\n            if ($attribute instanceof Mapping\\PreUpdate) {\n                $callbacks[] = [$method->name, Events::preUpdate];\n            }\n\n            if ($attribute instanceof Mapping\\PostUpdate) {\n                $callbacks[] = [$method->name, Events::postUpdate];\n            }\n\n            if ($attribute instanceof Mapping\\PreRemove) {\n                $callbacks[] = [$method->name, Events::preRemove];\n            }\n\n            if ($attribute instanceof Mapping\\PostRemove) {\n                $callbacks[] = [$method->name, Events::postRemove];\n            }\n\n            if ($attribute instanceof Mapping\\PostLoad) {\n                $callbacks[] = [$method->name, Events::postLoad];\n            }\n\n            if ($attribute instanceof Mapping\\PreFlush) {\n                $callbacks[] = [$method->name, Events::preFlush];\n            }\n        }\n\n        return $callbacks;\n    }\n\n    /**\n     * Parse the given JoinColumn as array\n     *\n     * @return mixed[]\n     * @phpstan-return array{\n     *                   name: string|null,\n     *                   unique: bool,\n     *                   nullable: bool,\n     *                   onDelete: mixed,\n     *                   columnDefinition: string|null,\n     *                   referencedColumnName: string,\n     *                   options?: array<string, mixed>\n     *               }\n     */\n    private function joinColumnToArray(Mapping\\JoinColumn|Mapping\\InverseJoinColumn $joinColumn): array\n    {\n        $mapping = [\n            'name' => $joinColumn->name,\n            'deferrable' => $joinColumn->deferrable,\n            'unique' => $joinColumn->unique,\n            'nullable' => $joinColumn->nullable,\n            'onDelete' => $joinColumn->onDelete,\n            'columnDefinition' => $joinColumn->columnDefinition,\n            'referencedColumnName' => $joinColumn->referencedColumnName,\n        ];\n\n        if ($joinColumn->options) {\n            $mapping['options'] = $joinColumn->options;\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * Parse the given Column as array\n     *\n     * @return mixed[]\n     * @phpstan-return array{\n     *                   fieldName: string,\n     *                   type: mixed,\n     *                   scale: int,\n     *                   length: int,\n     *                   unique: bool,\n     *                   nullable: bool,\n     *                   index: bool,\n     *                   precision: int,\n     *                   enumType?: class-string,\n     *                   options?: mixed[],\n     *                   columnName?: string,\n     *                   columnDefinition?: string\n     *               }\n     */\n    private function columnToArray(string $fieldName, Mapping\\Column $column): array\n    {\n        $mapping = [\n            'fieldName' => $fieldName,\n            'type'      => $column->type,\n            'scale'     => $column->scale,\n            'length'    => $column->length,\n            'unique'    => $column->unique,\n            'nullable'  => $column->nullable,\n            'index'     => $column->index,\n            'precision' => $column->precision,\n        ];\n\n        if ($column->options) {\n            $mapping['options'] = $column->options;\n        }\n\n        if (isset($column->name)) {\n            $mapping['columnName'] = $column->name;\n        }\n\n        if (isset($column->columnDefinition)) {\n            $mapping['columnDefinition'] = $column->columnDefinition;\n        }\n\n        if ($column->updatable === false) {\n            $mapping['notUpdatable'] = true;\n        }\n\n        if ($column->insertable === false) {\n            $mapping['notInsertable'] = true;\n        }\n\n        if ($column->generated !== null) {\n            $mapping['generated'] = $this->getGeneratedMode($column->generated);\n        }\n\n        if ($column->enumType) {\n            $mapping['enumType'] = $column->enumType;\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/AttributeReader.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Attribute;\nuse Doctrine\\ORM\\Mapping\\MappingAttribute;\nuse LogicException;\nuse ReflectionAttribute;\nuse ReflectionClass;\nuse ReflectionMethod;\nuse ReflectionProperty;\n\nuse function assert;\nuse function is_string;\nuse function is_subclass_of;\nuse function sprintf;\n\n/** @internal */\nfinal class AttributeReader\n{\n    /** @var array<class-string<MappingAttribute>, bool> */\n    private array $isRepeatableAttribute = [];\n\n    /**\n     * @phpstan-return class-string-map<T, T|RepeatableAttributeCollection<T>>\n     *\n     * @template T of MappingAttribute\n     */\n    public function getClassAttributes(ReflectionClass $class): array\n    {\n        return $this->convertToAttributeInstances($class->getAttributes());\n    }\n\n    /**\n     * @return class-string-map<T, T|RepeatableAttributeCollection<T>>\n     *\n     * @template T of MappingAttribute\n     */\n    public function getMethodAttributes(ReflectionMethod $method): array\n    {\n        return $this->convertToAttributeInstances($method->getAttributes());\n    }\n\n    /**\n     * @return class-string-map<T, T|RepeatableAttributeCollection<T>>\n     *\n     * @template T of MappingAttribute\n     */\n    public function getPropertyAttributes(ReflectionProperty $property): array\n    {\n        return $this->convertToAttributeInstances($property->getAttributes());\n    }\n\n    /**\n     * @param class-string<T> $attributeName The name of the annotation.\n     *\n     * @return T|null\n     *\n     * @template T of MappingAttribute\n     */\n    public function getPropertyAttribute(ReflectionProperty $property, string $attributeName)\n    {\n        if ($this->isRepeatable($attributeName)) {\n            throw new LogicException(sprintf(\n                'The attribute \"%s\" is repeatable. Call getPropertyAttributeCollection() instead.',\n                $attributeName,\n            ));\n        }\n\n        return $this->getPropertyAttributes($property)[$attributeName] ?? null;\n    }\n\n    /**\n     * @param class-string<T> $attributeName The name of the annotation.\n     *\n     * @return RepeatableAttributeCollection<T>\n     *\n     * @template T of MappingAttribute\n     */\n    public function getPropertyAttributeCollection(\n        ReflectionProperty $property,\n        string $attributeName,\n    ): RepeatableAttributeCollection {\n        if (! $this->isRepeatable($attributeName)) {\n            throw new LogicException(sprintf(\n                'The attribute \"%s\" is not repeatable. Call getPropertyAttribute() instead.',\n                $attributeName,\n            ));\n        }\n\n        return $this->getPropertyAttributes($property)[$attributeName] ?? new RepeatableAttributeCollection();\n    }\n\n    /**\n     * @param array<ReflectionAttribute> $attributes\n     *\n     * @return class-string-map<T, T|RepeatableAttributeCollection<T>>\n     *\n     * @template T of MappingAttribute\n     */\n    private function convertToAttributeInstances(array $attributes): array\n    {\n        $instances = [];\n\n        foreach ($attributes as $attribute) {\n            $attributeName = $attribute->getName();\n            assert(is_string($attributeName));\n            // Make sure we only get Doctrine Attributes\n            if (! is_subclass_of($attributeName, MappingAttribute::class)) {\n                continue;\n            }\n\n            $instance = $attribute->newInstance();\n            assert($instance instanceof MappingAttribute);\n\n            if ($this->isRepeatable($attributeName)) {\n                if (! isset($instances[$attributeName])) {\n                    $instances[$attributeName] = new RepeatableAttributeCollection();\n                }\n\n                $collection = $instances[$attributeName];\n                assert($collection instanceof RepeatableAttributeCollection);\n                $collection[] = $instance;\n            } else {\n                $instances[$attributeName] = $instance;\n            }\n        }\n\n        return $instances;\n    }\n\n    /** @param class-string<MappingAttribute> $attributeClassName */\n    private function isRepeatable(string $attributeClassName): bool\n    {\n        if (isset($this->isRepeatableAttribute[$attributeClassName])) {\n            return $this->isRepeatableAttribute[$attributeClassName];\n        }\n\n        $reflectionClass = new ReflectionClass($attributeClassName);\n        $attribute       = $reflectionClass->getAttributes()[0]->newInstance();\n\n        return $this->isRepeatableAttribute[$attributeClassName] = ($attribute->flags & Attribute::IS_REPEATABLE) > 0;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/DatabaseDriver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\DBAL\\Schema\\AbstractAsset;\nuse Doctrine\\DBAL\\Schema\\AbstractSchemaManager;\nuse Doctrine\\DBAL\\Schema\\Column;\nuse Doctrine\\DBAL\\Schema\\ForeignKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\Index;\nuse Doctrine\\DBAL\\Schema\\Index\\IndexedColumn;\nuse Doctrine\\DBAL\\Schema\\Index\\IndexType;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\NamedObject;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\SchemaException;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Inflector\\Inflector;\nuse Doctrine\\Inflector\\InflectorFactory;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata as PersistenceClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse InvalidArgumentException;\nuse TypeError;\n\nuse function array_diff;\nuse function array_keys;\nuse function array_map;\nuse function array_merge;\nuse function assert;\nuse function count;\nuse function current;\nuse function enum_exists;\nuse function get_debug_type;\nuse function in_array;\nuse function method_exists;\nuse function preg_replace;\nuse function sort;\nuse function sprintf;\nuse function strtolower;\n\n/**\n * The DatabaseDriver reverse engineers the mapping metadata from a database.\n *\n * @deprecated No replacement planned\n *\n * @link    www.doctrine-project.org\n */\nclass DatabaseDriver implements MappingDriver\n{\n    /**\n     * Replacement for {@see Types::ARRAY}.\n     *\n     * To be removed as soon as support for DBAL 3 is dropped.\n     */\n    private const ARRAY = 'array';\n\n    /**\n     * Replacement for {@see Types::OBJECT}.\n     *\n     * To be removed as soon as support for DBAL 3 is dropped.\n     */\n    private const OBJECT = 'object';\n\n    /** @var array<string,Table>|null */\n    private array|null $tables = null;\n\n    /** @var array<class-string, string> */\n    private array $classToTableNames = [];\n\n    /** @phpstan-var array<string, Table> */\n    private array $manyToManyTables = [];\n\n    /** @var mixed[] */\n    private array $classNamesForTables = [];\n\n    /** @var mixed[] */\n    private array $fieldNamesForColumns = [];\n\n    /**\n     * The namespace for the generated entities.\n     */\n    private string|null $namespace = null;\n\n    private Inflector $inflector;\n\n    public function __construct(private readonly AbstractSchemaManager $sm)\n    {\n        $this->inflector = InflectorFactory::create()->build();\n    }\n\n    /**\n     * Set the namespace for the generated entities.\n     */\n    public function setNamespace(string $namespace): void\n    {\n        $this->namespace = $namespace;\n    }\n\n    public function isTransient(string $className): bool\n    {\n        return true;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getAllClassNames(): array\n    {\n        $this->reverseEngineerMappingFromDatabase();\n\n        return array_keys($this->classToTableNames);\n    }\n\n    /**\n     * Sets class name for a table.\n     */\n    public function setClassNameForTable(string $tableName, string $className): void\n    {\n        $this->classNamesForTables[$tableName] = $className;\n    }\n\n    /**\n     * Sets field name for a column on a specific table.\n     */\n    public function setFieldNameForColumn(string $tableName, string $columnName, string $fieldName): void\n    {\n        $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;\n    }\n\n    /**\n     * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.\n     *\n     * @param Table[] $entityTables\n     * @param Table[] $manyToManyTables\n     * @phpstan-param list<Table> $entityTables\n     * @phpstan-param list<Table> $manyToManyTables\n     */\n    public function setTables(array $entityTables, array $manyToManyTables): void\n    {\n        $this->tables = $this->manyToManyTables = $this->classToTableNames = [];\n\n        foreach ($entityTables as $table) {\n            $className = $this->getClassNameForTable(self::getAssetName($table));\n\n            $this->classToTableNames[$className]      = self::getAssetName($table);\n            $this->tables[self::getAssetName($table)] = $table;\n        }\n\n        foreach ($manyToManyTables as $table) {\n            $this->manyToManyTables[self::getAssetName($table)] = $table;\n        }\n    }\n\n    public function setInflector(Inflector $inflector): void\n    {\n        $this->inflector = $inflector;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param class-string<T>  $className\n     * @param ClassMetadata<T> $metadata\n     *\n     * @template T of object\n     */\n    public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void\n    {\n        if (! $metadata instanceof ClassMetadata) {\n            throw new TypeError(sprintf(\n                'Argument #2 passed to %s() must be an instance of %s, %s given.',\n                __METHOD__,\n                ClassMetadata::class,\n                get_debug_type($metadata),\n            ));\n        }\n\n        $this->reverseEngineerMappingFromDatabase();\n\n        if (! isset($this->classToTableNames[$className])) {\n            throw new InvalidArgumentException('Unknown class ' . $className);\n        }\n\n        $tableName = $this->classToTableNames[$className];\n\n        $metadata->name          = $className;\n        $metadata->table['name'] = $tableName;\n\n        $this->buildIndexes($metadata);\n        $this->buildFieldMappings($metadata);\n        $this->buildToOneAssociationMappings($metadata);\n\n        foreach ($this->manyToManyTables as $manyTable) {\n            foreach ($manyTable->getForeignKeys() as $foreignKey) {\n                // foreign key maps to the table of the current entity, many to many association probably exists\n                if (! (strtolower($tableName) === strtolower(self::getReferencedTableName($foreignKey)))) {\n                    continue;\n                }\n\n                $myFk    = $foreignKey;\n                $otherFk = null;\n\n                foreach ($manyTable->getForeignKeys() as $foreignKey) {\n                    if ($foreignKey !== $myFk) {\n                        $otherFk = $foreignKey;\n                        break;\n                    }\n                }\n\n                if (! $otherFk) {\n                    // the definition of this many to many table does not contain\n                    // enough foreign key information to continue reverse engineering.\n                    continue;\n                }\n\n                $localColumn = current(self::getReferencingColumnNames($myFk));\n\n                $associationMapping                 = [];\n                $associationMapping['fieldName']    = $this->getFieldNameForColumn(self::getAssetName($manyTable), current(self::getReferencingColumnNames($otherFk)), true);\n                $associationMapping['targetEntity'] = $this->getClassNameForTable(self::getReferencedTableName($otherFk));\n\n                if (self::getAssetName(current($manyTable->getColumns())) === $localColumn) {\n                    $associationMapping['inversedBy'] = $this->getFieldNameForColumn(self::getAssetName($manyTable), current(self::getReferencingColumnNames($myFk)), true);\n                    $associationMapping['joinTable']  = [\n                        'name' => strtolower(self::getAssetName($manyTable)),\n                        'joinColumns' => [],\n                        'inverseJoinColumns' => [],\n                    ];\n\n                    $fkCols = self::getReferencedColumnNames($myFk);\n                    $cols   = self::getReferencingColumnNames($myFk);\n\n                    for ($i = 0, $colsCount = count($cols); $i < $colsCount; $i++) {\n                        $associationMapping['joinTable']['joinColumns'][] = [\n                            'name' => $cols[$i],\n                            'referencedColumnName' => $fkCols[$i],\n                        ];\n                    }\n\n                    $fkCols = self::getReferencedColumnNames($otherFk);\n                    $cols   = self::getReferencingColumnNames($otherFk);\n\n                    for ($i = 0, $colsCount = count($cols); $i < $colsCount; $i++) {\n                        $associationMapping['joinTable']['inverseJoinColumns'][] = [\n                            'name' => $cols[$i],\n                            'referencedColumnName' => $fkCols[$i],\n                        ];\n                    }\n                } else {\n                    $associationMapping['mappedBy'] = $this->getFieldNameForColumn(\n                        // @phpstan-ignore function.alreadyNarrowedType (DBAL 3 compatibility)\n                        method_exists(Table::class, 'getObjectName')\n                            ? $manyTable->getObjectName()->toString()\n                            : $manyTable->getName(), // DBAL < 4.4\n                        current(self::getReferencingColumnNames($myFk)),\n                        true,\n                    );\n                }\n\n                $metadata->mapManyToMany($associationMapping);\n\n                break;\n            }\n        }\n    }\n\n    /** @throws MappingException */\n    private function reverseEngineerMappingFromDatabase(): void\n    {\n        if ($this->tables !== null) {\n            return;\n        }\n\n        $this->tables = $this->manyToManyTables = $this->classToTableNames = [];\n\n        foreach ($this->sm->listTables() as $table) {\n            $tableName   = self::getAssetName($table);\n            $foreignKeys = $table->getForeignKeys();\n\n            $allForeignKeyColumns = [];\n\n            foreach ($foreignKeys as $foreignKey) {\n                $allForeignKeyColumns = array_merge($allForeignKeyColumns, self::getReferencingColumnNames($foreignKey));\n            }\n\n            if (method_exists($table, 'getPrimaryKeyConstraint')) {\n                $primaryKey = $table->getPrimaryKeyConstraint();\n            } else {\n                $primaryKey = $table->getPrimaryKey();\n            }\n\n            if ($primaryKey === null) {\n                throw new MappingException(\n                    'Table ' . $tableName . ' has no primary key. Doctrine does not ' .\n                    \"support reverse engineering from tables that don't have a primary key.\",\n                );\n            }\n\n            if ($primaryKey instanceof PrimaryKeyConstraint) {\n                $pkColumns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $primaryKey->getColumnNames());\n            } else {\n                $pkColumns = self::getIndexedColumns($primaryKey);\n            }\n\n            sort($pkColumns);\n            sort($allForeignKeyColumns);\n\n            if ($pkColumns === $allForeignKeyColumns && count($foreignKeys) === 2) {\n                $this->manyToManyTables[$tableName] = $table;\n            } else {\n                // lower-casing is necessary because of Oracle Uppercase Tablenames,\n                // assumption is lower-case + underscore separated.\n                $className = $this->getClassNameForTable($tableName);\n\n                $this->tables[$tableName]            = $table;\n                $this->classToTableNames[$className] = $tableName;\n            }\n        }\n    }\n\n    /**\n     * Build indexes from a class metadata.\n     */\n    private function buildIndexes(ClassMetadata $metadata): void\n    {\n        $tableName  = $metadata->table['name'];\n        $table      = $this->tables[$tableName];\n        $primaryKey = self::getPrimaryKey($table);\n        $indexes    = $table->getIndexes();\n\n        foreach ($indexes as $index) {\n            if ($index === $primaryKey) {\n                continue;\n            }\n\n            if (enum_exists(IndexType::class) && method_exists($index, 'getType')) {\n                $isUnique = $index->getType() === IndexType::UNIQUE;\n            } else {\n                $isUnique = $index->isUnique();\n            }\n\n            $indexName      = self::getAssetName($index);\n            $indexColumns   = self::getIndexedColumns($index);\n            $constraintType = $isUnique\n                ? 'uniqueConstraints'\n                : 'indexes';\n\n            $metadata->table[$constraintType][$indexName]['columns'] = $indexColumns;\n        }\n    }\n\n    /**\n     * Build field mapping from class metadata.\n     */\n    private function buildFieldMappings(ClassMetadata $metadata): void\n    {\n        $tableName      = $metadata->table['name'];\n        $columns        = $this->tables[$tableName]->getColumns();\n        $primaryKeys    = $this->getTablePrimaryKeys($this->tables[$tableName]);\n        $foreignKeys    = $this->tables[$tableName]->getForeignKeys();\n        $allForeignKeys = [];\n\n        foreach ($foreignKeys as $foreignKey) {\n            $allForeignKeys = array_merge($allForeignKeys, self::getReferencingColumnNames($foreignKey));\n        }\n\n        $ids           = [];\n        $fieldMappings = [];\n\n        foreach ($columns as $column) {\n            if (in_array(self::getAssetName($column), $allForeignKeys, true)) {\n                continue;\n            }\n\n            $fieldMapping = $this->buildFieldMapping($tableName, $column);\n\n            if ($primaryKeys && in_array(self::getAssetName($column), $primaryKeys, true)) {\n                $fieldMapping['id'] = true;\n                $ids[]              = $fieldMapping;\n            }\n\n            $fieldMappings[] = $fieldMapping;\n        }\n\n        // We need to check for the columns here, because we might have associations as id as well.\n        if ($ids && count($primaryKeys) === 1) {\n            $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n        }\n\n        foreach ($fieldMappings as $fieldMapping) {\n            $metadata->mapField($fieldMapping);\n        }\n    }\n\n    /**\n     * Build field mapping from a schema column definition\n     *\n     * @return mixed[]\n     * @phpstan-return array{\n     *     fieldName: string,\n     *     columnName: string,\n     *     type: string,\n     *     nullable: bool,\n     *     options: array{\n     *         unsigned?: bool,\n     *         fixed?: bool,\n     *         comment: string|null,\n     *         default?: mixed\n     *     },\n     *     precision?: int,\n     *     scale?: int,\n     *     length?: int|null\n     * }\n     */\n    private function buildFieldMapping(string $tableName, Column $column): array\n    {\n        $fieldMapping = [\n            'fieldName'  => $this->getFieldNameForColumn($tableName, self::getAssetName($column), false),\n            'columnName' => self::getAssetName($column),\n            'type'       => Type::getTypeRegistry()->lookupName($column->getType()),\n            'nullable'   => ! $column->getNotnull(),\n            'options'    => [\n                'comment' => $column->getComment(),\n            ],\n        ];\n\n        // Type specific elements\n        switch ($fieldMapping['type']) {\n            case self::ARRAY:\n            case Types::BLOB:\n            case Types::GUID:\n            case self::OBJECT:\n            case Types::SIMPLE_ARRAY:\n            case Types::STRING:\n            case Types::TEXT:\n                $fieldMapping['length']           = $column->getLength();\n                $fieldMapping['options']['fixed'] = $column->getFixed();\n                break;\n\n            case Types::DECIMAL:\n            case Types::FLOAT:\n                $fieldMapping['precision'] = $column->getPrecision();\n                $fieldMapping['scale']     = $column->getScale();\n                break;\n\n            case Types::INTEGER:\n            case Types::BIGINT:\n            case Types::SMALLINT:\n                $fieldMapping['options']['unsigned'] = $column->getUnsigned();\n                break;\n        }\n\n        // Default\n        $default = $column->getDefault();\n        if ($default !== null) {\n            $fieldMapping['options']['default'] = $default;\n        }\n\n        return $fieldMapping;\n    }\n\n    /**\n     * Build to one (one to one, many to one) association mapping from class metadata.\n     */\n    private function buildToOneAssociationMappings(ClassMetadata $metadata): void\n    {\n        assert($this->tables !== null);\n\n        $tableName   = $metadata->table['name'];\n        $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);\n        $foreignKeys = $this->tables[$tableName]->getForeignKeys();\n\n        foreach ($foreignKeys as $foreignKey) {\n            $foreignTableName   = self::getReferencedTableName($foreignKey);\n            $fkColumns          = self::getReferencingColumnNames($foreignKey);\n            $fkForeignColumns   = self::getReferencedColumnNames($foreignKey);\n            $localColumn        = current($fkColumns);\n            $associationMapping = [\n                'fieldName'    => $this->getFieldNameForColumn($tableName, $localColumn, true),\n                'targetEntity' => $this->getClassNameForTable($foreignTableName),\n            ];\n\n            if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {\n                $associationMapping['fieldName'] .= '2'; // \"foo\" => \"foo2\"\n            }\n\n            if ($primaryKeys && in_array($localColumn, $primaryKeys, true)) {\n                $associationMapping['id'] = true;\n            }\n\n            for ($i = 0, $fkColumnsCount = count($fkColumns); $i < $fkColumnsCount; $i++) {\n                $associationMapping['joinColumns'][] = [\n                    'name'                 => $fkColumns[$i],\n                    'referencedColumnName' => $fkForeignColumns[$i],\n                ];\n            }\n\n            // Here we need to check if $fkColumns are the same as $primaryKeys\n            if (! array_diff($fkColumns, $primaryKeys)) {\n                $metadata->mapOneToOne($associationMapping);\n            } else {\n                $metadata->mapManyToOne($associationMapping);\n            }\n        }\n    }\n\n    /**\n     * Retrieve schema table definition primary keys.\n     *\n     * @return string[]\n     */\n    private function getTablePrimaryKeys(Table $table): array\n    {\n        try {\n            if (method_exists($table, 'getPrimaryKeyConstraint')) {\n                return array_map(static fn (UnqualifiedName $name) => $name->toString(), $table->getPrimaryKeyConstraint()->getColumnNames());\n            }\n\n            return self::getIndexedColumns($table->getPrimaryKey());\n        } catch (SchemaException) {\n            // Do nothing\n        }\n\n        return [];\n    }\n\n    /**\n     * Returns the mapped class name for a table if it exists. Otherwise return \"classified\" version.\n     *\n     * @return class-string\n     */\n    private function getClassNameForTable(string $tableName): string\n    {\n        if (isset($this->classNamesForTables[$tableName])) {\n            return $this->namespace . $this->classNamesForTables[$tableName];\n        }\n\n        return $this->namespace . $this->inflector->classify(strtolower($tableName));\n    }\n\n    /**\n     * Return the mapped field name for a column, if it exists. Otherwise return camelized version.\n     *\n     * @param bool $fk Whether the column is a foreignkey or not.\n     */\n    private function getFieldNameForColumn(\n        string $tableName,\n        string $columnName,\n        bool $fk = false,\n    ): string {\n        if (isset($this->fieldNamesForColumns[$tableName], $this->fieldNamesForColumns[$tableName][$columnName])) {\n            return $this->fieldNamesForColumns[$tableName][$columnName];\n        }\n\n        $columnName = strtolower($columnName);\n\n        // Replace _id if it is a foreignkey column\n        if ($fk) {\n            $columnName = preg_replace('/_id$/', '', $columnName);\n        }\n\n        return $this->inflector->camelize($columnName);\n    }\n\n    private static function getReferencedTableName(ForeignKeyConstraint $foreignKey): string\n    {\n        if (method_exists(ForeignKeyConstraint::class, 'getReferencedTableName')) {\n            return $foreignKey->getReferencedTableName()->toString();\n        }\n\n        return $foreignKey->getForeignTableName();\n    }\n\n    /** @return string[] */\n    private static function getReferencingColumnNames(ForeignKeyConstraint $foreignKey): array\n    {\n        if (method_exists(ForeignKeyConstraint::class, 'getReferencingColumnNames')) {\n            return array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencingColumnNames());\n        }\n\n        return $foreignKey->getLocalColumns();\n    }\n\n    /** @return string[] */\n    private static function getReferencedColumnNames(ForeignKeyConstraint $foreignKey): array\n    {\n        if (method_exists(ForeignKeyConstraint::class, 'getReferencedColumnNames')) {\n            return array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencedColumnNames());\n        }\n\n        return $foreignKey->getForeignColumns();\n    }\n\n    /** @return string[] */\n    private static function getIndexedColumns(Index $index): array\n    {\n        if (method_exists(Index::class, 'getIndexedColumns')) {\n            return array_map(static fn (IndexedColumn $indexedColumn) => $indexedColumn->getColumnName()->toString(), $index->getIndexedColumns());\n        }\n\n        return $index->getColumns();\n    }\n\n    private static function getPrimaryKey(Table $table): Index|null\n    {\n        $primaryKeyConstraint = null;\n\n        if (method_exists(Table::class, 'getPrimaryKeyConstraint')) {\n            $primaryKeyConstraint = $table->getPrimaryKeyConstraint();\n        }\n\n        foreach ($table->getIndexes() as $index) {\n            if ($primaryKeyConstraint !== null) {\n                $primaryKeyConstraintColumns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $primaryKeyConstraint->getColumnNames());\n\n                if ($primaryKeyConstraintColumns === self::getIndexedColumns($index)) {\n                    return $index;\n                }\n            } elseif ($index->isPrimary()) {\n                return $index;\n            }\n        }\n\n        return null;\n    }\n\n    private static function getAssetName(AbstractAsset $asset): string\n    {\n        return $asset instanceof NamedObject\n            ? $asset->getObjectName()->toString()\n            // DBAL < 4.4\n            : $asset->getName();\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/LoadMappingFileImplementation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\Persistence\\Mapping\\StaticReflectionService;\n\nuse function class_exists;\n\nif (! class_exists(StaticReflectionService::class)) {\n    /** @internal */\n    trait LoadMappingFileImplementation\n    {\n        /**\n         * {@inheritDoc}\n         */\n        protected function loadMappingFile($file): array\n        {\n            return $this->doLoadMappingFile($file);\n        }\n    }\n} else {\n    /** @internal */\n    trait LoadMappingFileImplementation\n    {\n        /**\n         * {@inheritDoc}\n         */\n        protected function loadMappingFile($file)\n        {\n            return $this->doLoadMappingFile($file);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/ReflectionBasedDriver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse ReflectionProperty;\n\n/** @internal */\ntrait ReflectionBasedDriver\n{\n    /**\n     * Helps to deal with the case that reflection may report properties inherited from parent classes.\n     * When we know about the fields already (inheritance has been anticipated in ClassMetadataFactory),\n     * the driver must skip them.\n     *\n     * The declaring classes may mismatch when there are private properties: The same property name may be\n     * reported multiple times, but since it is private, it is in fact multiple (different) properties in\n     * different classes. In that case, report the property as an individual field. (ClassMetadataFactory will\n     * probably fail in that case, though.)\n     */\n    private function isRepeatedPropertyDeclaration(ReflectionProperty $property, ClassMetadata $metadata): bool\n    {\n        $declaringClass = $property->class;\n\n        if (\n            isset($metadata->fieldMappings[$property->name]->declared)\n            && $metadata->fieldMappings[$property->name]->declared === $declaringClass\n        ) {\n            return true;\n        }\n\n        if (\n            isset($metadata->associationMappings[$property->name]->declared)\n            && $metadata->associationMappings[$property->name]->declared === $declaringClass\n        ) {\n            return true;\n        }\n\n        return isset($metadata->embeddedClasses[$property->name]->declared)\n            && $metadata->embeddedClasses[$property->name]->declared === $declaringClass;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/RepeatableAttributeCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse ArrayObject;\nuse Doctrine\\ORM\\Mapping\\MappingAttribute;\n\n/**\n * @template-extends ArrayObject<int, T>\n * @template T of MappingAttribute\n */\nfinal class RepeatableAttributeCollection extends ArrayObject\n{\n}\n"
  },
  {
    "path": "src/Mapping/Driver/SimplifiedXmlDriver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\Persistence\\Mapping\\Driver\\SymfonyFileLocator;\n\n/**\n * XmlDriver that additionally looks for mapping information in a global file.\n */\nclass SimplifiedXmlDriver extends XmlDriver\n{\n    public const DEFAULT_FILE_EXTENSION = '.orm.xml';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION, bool $isXsdValidationEnabled = true)\n    {\n        $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension);\n\n        parent::__construct($locator, $fileExtension, $isXsdValidationEnabled);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Driver/XmlDriver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Driver;\n\nuse Doctrine\\ORM\\Mapping\\Builder\\EntityListenerBuilder;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata as PersistenceClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileLocator;\nuse DOMDocument;\nuse InvalidArgumentException;\nuse LogicException;\nuse SimpleXMLElement;\n\nuse function assert;\nuse function class_exists;\nuse function constant;\nuse function count;\nuse function defined;\nuse function explode;\nuse function extension_loaded;\nuse function file_get_contents;\nuse function in_array;\nuse function libxml_clear_errors;\nuse function libxml_get_errors;\nuse function libxml_use_internal_errors;\nuse function simplexml_load_string;\nuse function sprintf;\nuse function str_replace;\nuse function strtoupper;\n\n/**\n * XmlDriver is a metadata driver that enables mapping through XML files.\n *\n * @link        www.doctrine-project.org\n *\n * @template-extends FileDriver<SimpleXMLElement>\n */\nclass XmlDriver extends FileDriver\n{\n    use LoadMappingFileImplementation;\n\n    public const DEFAULT_FILE_EXTENSION = '.dcm.xml';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function __construct(\n        string|array|FileLocator $locator,\n        string $fileExtension = self::DEFAULT_FILE_EXTENSION,\n        private readonly bool $isXsdValidationEnabled = true,\n    ) {\n        if (! extension_loaded('simplexml')) {\n            throw new LogicException(\n                'The XML metadata driver cannot be enabled because the SimpleXML PHP extension is missing.'\n                . ' Please configure PHP with SimpleXML or choose a different metadata driver.',\n            );\n        }\n\n        if ($isXsdValidationEnabled && ! extension_loaded('dom')) {\n            throw new LogicException(\n                'XSD validation cannot be enabled because the DOM extension is missing.',\n            );\n        }\n\n        parent::__construct($locator, $fileExtension);\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param class-string<T>  $className\n     * @param ClassMetadata<T> $metadata\n     *\n     * @template T of object\n     */\n    public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void\n    {\n        $xmlRoot = $this->getElement($className);\n\n        if ($xmlRoot->getName() === 'entity') {\n            if (isset($xmlRoot['repository-class'])) {\n                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);\n            }\n\n            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {\n                $metadata->markReadOnly();\n            }\n        } elseif ($xmlRoot->getName() === 'mapped-superclass') {\n            $metadata->setCustomRepositoryClass(\n                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null,\n            );\n            $metadata->isMappedSuperclass = true;\n        } elseif ($xmlRoot->getName() === 'embeddable') {\n            $metadata->isEmbeddedClass = true;\n        } else {\n            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);\n        }\n\n        // Evaluate <entity...> attributes\n        $primaryTable = [];\n\n        if (isset($xmlRoot['table'])) {\n            $primaryTable['name'] = (string) $xmlRoot['table'];\n        }\n\n        if (isset($xmlRoot['schema'])) {\n            $primaryTable['schema'] = (string) $xmlRoot['schema'];\n        }\n\n        $metadata->setPrimaryTable($primaryTable);\n\n        // Evaluate second level cache\n        if (isset($xmlRoot->cache)) {\n            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));\n        }\n\n        if (isset($xmlRoot['inheritance-type'])) {\n            $inheritanceType = (string) $xmlRoot['inheritance-type'];\n            $metadata->setInheritanceType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));\n\n            if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {\n                // Evaluate <discriminator-column...>\n                if (isset($xmlRoot->{'discriminator-column'})) {\n                    $discrColumn = $xmlRoot->{'discriminator-column'};\n                    $columnDef   = [\n                        'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null,\n                        'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',\n                        'length' => isset($discrColumn['length']) ? (int) $discrColumn['length'] : 255,\n                        'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null,\n                        'enumType' => isset($discrColumn['enum-type']) ? (string) $discrColumn['enum-type'] : null,\n                    ];\n\n                    if (isset($discrColumn['options'])) {\n                        $columnDef['options'] = $this->parseOptions($discrColumn['options']->children());\n                    }\n\n                    $metadata->setDiscriminatorColumn($columnDef);\n                } else {\n                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);\n                }\n\n                // Evaluate <discriminator-map...>\n                if (isset($xmlRoot->{'discriminator-map'})) {\n                    $map = [];\n                    assert($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} instanceof SimpleXMLElement);\n                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {\n                        $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];\n                    }\n\n                    $metadata->setDiscriminatorMap($map);\n                }\n            }\n        }\n\n        // Evaluate <change-tracking-policy...>\n        if (isset($xmlRoot['change-tracking-policy'])) {\n            $metadata->setChangeTrackingPolicy(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CHANGETRACKING_'\n                . strtoupper((string) $xmlRoot['change-tracking-policy'])));\n        }\n\n        // Evaluate <indexes...>\n        if (isset($xmlRoot->indexes)) {\n            $metadata->table['indexes'] = [];\n            foreach ($xmlRoot->indexes->index ?? [] as $indexXml) {\n                $index = [];\n\n                if (isset($indexXml['columns']) && ! empty($indexXml['columns'])) {\n                    $index['columns'] = explode(',', (string) $indexXml['columns']);\n                }\n\n                if (isset($indexXml['fields'])) {\n                    $index['fields'] = explode(',', (string) $indexXml['fields']);\n                }\n\n                if (\n                    isset($index['columns'], $index['fields'])\n                    || (\n                        ! isset($index['columns'])\n                        && ! isset($index['fields'])\n                    )\n                ) {\n                    throw MappingException::invalidIndexConfiguration(\n                        $className,\n                        (string) ($indexXml['name'] ?? count($metadata->table['indexes'])),\n                    );\n                }\n\n                if (isset($indexXml['flags'])) {\n                    $index['flags'] = explode(',', (string) $indexXml['flags']);\n                }\n\n                if (isset($indexXml->options)) {\n                    $index['options'] = $this->parseOptions($indexXml->options->children());\n                }\n\n                if (isset($indexXml['name'])) {\n                    $metadata->table['indexes'][(string) $indexXml['name']] = $index;\n                } else {\n                    $metadata->table['indexes'][] = $index;\n                }\n            }\n        }\n\n        // Evaluate <unique-constraints..>\n        if (isset($xmlRoot->{'unique-constraints'})) {\n            $metadata->table['uniqueConstraints'] = [];\n            foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} ?? [] as $uniqueXml) {\n                $unique = [];\n\n                if (isset($uniqueXml['columns']) && ! empty($uniqueXml['columns'])) {\n                    $unique['columns'] = explode(',', (string) $uniqueXml['columns']);\n                }\n\n                if (isset($uniqueXml['fields'])) {\n                    $unique['fields'] = explode(',', (string) $uniqueXml['fields']);\n                }\n\n                if (\n                    isset($unique['columns'], $unique['fields'])\n                    || (\n                        ! isset($unique['columns'])\n                        && ! isset($unique['fields'])\n                    )\n                ) {\n                    throw MappingException::invalidUniqueConstraintConfiguration(\n                        $className,\n                        (string) ($uniqueXml['name'] ?? count($metadata->table['uniqueConstraints'])),\n                    );\n                }\n\n                if (isset($uniqueXml->options)) {\n                    $unique['options'] = $this->parseOptions($uniqueXml->options->children());\n                }\n\n                if (isset($uniqueXml['name'])) {\n                    $metadata->table['uniqueConstraints'][(string) $uniqueXml['name']] = $unique;\n                } else {\n                    $metadata->table['uniqueConstraints'][] = $unique;\n                }\n            }\n        }\n\n        if (isset($xmlRoot->options)) {\n            $metadata->table['options'] = $this->parseOptions($xmlRoot->options->children());\n        }\n\n        // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions\n        // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception\n        // Evaluate <field ...> mappings\n        if (isset($xmlRoot->field)) {\n            foreach ($xmlRoot->field as $fieldMapping) {\n                $mapping = $this->columnToArray($fieldMapping);\n\n                if (isset($mapping['version'])) {\n                    $metadata->setVersionMapping($mapping);\n                    unset($mapping['version']);\n                }\n\n                $metadata->mapField($mapping);\n            }\n        }\n\n        if (isset($xmlRoot->embedded)) {\n            foreach ($xmlRoot->embedded as $embeddedMapping) {\n                $columnPrefix = isset($embeddedMapping['column-prefix'])\n                    ? (string) $embeddedMapping['column-prefix']\n                    : null;\n\n                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])\n                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])\n                    : true;\n\n                $mapping = [\n                    'fieldName' => (string) $embeddedMapping['name'],\n                    'class' => isset($embeddedMapping['class']) ? (string) $embeddedMapping['class'] : null,\n                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false,\n                ];\n\n                $metadata->mapEmbedded($mapping);\n            }\n        }\n\n        // Evaluate <id ...> mappings\n        $associationIds = [];\n        foreach ($xmlRoot->id ?? [] as $idElement) {\n            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {\n                $associationIds[(string) $idElement['name']] = true;\n                continue;\n            }\n\n            $mapping       = $this->columnToArray($idElement);\n            $mapping['id'] = true;\n\n            $metadata->mapField($mapping);\n\n            if (isset($idElement->generator)) {\n                $strategy = isset($idElement->generator['strategy']) ?\n                        (string) $idElement->generator['strategy'] : 'AUTO';\n                $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_'\n                    . $strategy));\n            }\n\n            // Check for SequenceGenerator/TableGenerator definition\n            if (isset($idElement->{'sequence-generator'})) {\n                $seqGenerator = $idElement->{'sequence-generator'};\n                $metadata->setSequenceGeneratorDefinition(\n                    [\n                        'sequenceName' => (string) $seqGenerator['sequence-name'],\n                        'allocationSize' => (string) $seqGenerator['allocation-size'],\n                        'initialValue' => (string) $seqGenerator['initial-value'],\n                    ],\n                );\n            } elseif (isset($idElement->{'custom-id-generator'})) {\n                $customGenerator = $idElement->{'custom-id-generator'};\n                $metadata->setCustomGeneratorDefinition(\n                    [\n                        'class' => (string) $customGenerator['class'],\n                    ],\n                );\n            }\n        }\n\n        // Evaluate <one-to-one ...> mappings\n        if (isset($xmlRoot->{'one-to-one'})) {\n            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {\n                $mapping = [\n                    'fieldName' => (string) $oneToOneElement['field'],\n                ];\n\n                if (isset($oneToOneElement['target-entity'])) {\n                    $mapping['targetEntity'] = (string) $oneToOneElement['target-entity'];\n                }\n\n                if (isset($associationIds[$mapping['fieldName']])) {\n                    $mapping['id'] = true;\n                }\n\n                if (isset($oneToOneElement['fetch'])) {\n                    $mapping['fetch'] = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . (string) $oneToOneElement['fetch']);\n                }\n\n                if (isset($oneToOneElement['mapped-by'])) {\n                    $mapping['mappedBy'] = (string) $oneToOneElement['mapped-by'];\n                } else {\n                    if (isset($oneToOneElement['inversed-by'])) {\n                        $mapping['inversedBy'] = (string) $oneToOneElement['inversed-by'];\n                    }\n\n                    $joinColumns = [];\n\n                    if (isset($oneToOneElement->{'join-column'})) {\n                        $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});\n                    } elseif (isset($oneToOneElement->{'join-columns'})) {\n                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                            $joinColumns[] = $this->joinColumnToArray($joinColumnElement);\n                        }\n                    }\n\n                    $mapping['joinColumns'] = $joinColumns;\n                }\n\n                if (isset($oneToOneElement->cascade)) {\n                    $mapping['cascade'] = $this->getCascadeMappings($oneToOneElement->cascade);\n                }\n\n                if (isset($oneToOneElement['orphan-removal'])) {\n                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);\n                }\n\n                // Evaluate second level cache\n                if (isset($oneToOneElement->cache)) {\n                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));\n                }\n\n                $metadata->mapOneToOne($mapping);\n            }\n        }\n\n        // Evaluate <one-to-many ...> mappings\n        if (isset($xmlRoot->{'one-to-many'})) {\n            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {\n                $mapping = [\n                    'fieldName' => (string) $oneToManyElement['field'],\n                    'mappedBy' => (string) $oneToManyElement['mapped-by'],\n                ];\n\n                if (isset($oneToManyElement['target-entity'])) {\n                    $mapping['targetEntity'] = (string) $oneToManyElement['target-entity'];\n                }\n\n                if (isset($oneToManyElement['fetch'])) {\n                    $mapping['fetch'] = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);\n                }\n\n                if (isset($oneToManyElement->cascade)) {\n                    $mapping['cascade'] = $this->getCascadeMappings($oneToManyElement->cascade);\n                }\n\n                if (isset($oneToManyElement['orphan-removal'])) {\n                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);\n                }\n\n                if (isset($oneToManyElement->{'order-by'})) {\n                    $orderBy = [];\n                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {\n                        $orderBy[(string) $orderByField['name']] = (string) ($orderByField['direction'] ?? 'ASC');\n                    }\n\n                    $mapping['orderBy'] = $orderBy;\n                }\n\n                if (isset($oneToManyElement['index-by'])) {\n                    $mapping['indexBy'] = (string) $oneToManyElement['index-by'];\n                } elseif (isset($oneToManyElement->{'index-by'})) {\n                    throw new InvalidArgumentException('<index-by /> is not a valid tag');\n                }\n\n                // Evaluate second level cache\n                if (isset($oneToManyElement->cache)) {\n                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache));\n                }\n\n                $metadata->mapOneToMany($mapping);\n            }\n        }\n\n        // Evaluate <many-to-one ...> mappings\n        if (isset($xmlRoot->{'many-to-one'})) {\n            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {\n                $mapping = [\n                    'fieldName' => (string) $manyToOneElement['field'],\n                ];\n\n                if (isset($manyToOneElement['target-entity'])) {\n                    $mapping['targetEntity'] = (string) $manyToOneElement['target-entity'];\n                }\n\n                if (isset($associationIds[$mapping['fieldName']])) {\n                    $mapping['id'] = true;\n                }\n\n                if (isset($manyToOneElement['fetch'])) {\n                    $mapping['fetch'] = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . (string) $manyToOneElement['fetch']);\n                }\n\n                if (isset($manyToOneElement['inversed-by'])) {\n                    $mapping['inversedBy'] = (string) $manyToOneElement['inversed-by'];\n                }\n\n                $joinColumns = [];\n\n                if (isset($manyToOneElement->{'join-column'})) {\n                    $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});\n                } elseif (isset($manyToOneElement->{'join-columns'})) {\n                    foreach ($manyToOneElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);\n                    }\n                }\n\n                $mapping['joinColumns'] = $joinColumns;\n\n                if (isset($manyToOneElement->cascade)) {\n                    $mapping['cascade'] = $this->getCascadeMappings($manyToOneElement->cascade);\n                }\n\n                // Evaluate second level cache\n                if (isset($manyToOneElement->cache)) {\n                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache));\n                }\n\n                $metadata->mapManyToOne($mapping);\n            }\n        }\n\n        // Evaluate <many-to-many ...> mappings\n        if (isset($xmlRoot->{'many-to-many'})) {\n            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {\n                $mapping = [\n                    'fieldName' => (string) $manyToManyElement['field'],\n                ];\n\n                if (isset($manyToManyElement['target-entity'])) {\n                    $mapping['targetEntity'] = (string) $manyToManyElement['target-entity'];\n                }\n\n                if (isset($manyToManyElement['fetch'])) {\n                    $mapping['fetch'] = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);\n                }\n\n                if (isset($manyToManyElement['orphan-removal'])) {\n                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);\n                }\n\n                if (isset($manyToManyElement['mapped-by'])) {\n                    $mapping['mappedBy'] = (string) $manyToManyElement['mapped-by'];\n                } elseif (isset($manyToManyElement->{'join-table'})) {\n                    if (isset($manyToManyElement['inversed-by'])) {\n                        $mapping['inversedBy'] = (string) $manyToManyElement['inversed-by'];\n                    }\n\n                    $joinTableElement = $manyToManyElement->{'join-table'};\n                    $joinTable        = [\n                        'name' => (string) $joinTableElement['name'],\n                    ];\n\n                    if (isset($joinTableElement['schema'])) {\n                        $joinTable['schema'] = (string) $joinTableElement['schema'];\n                    }\n\n                    if (isset($joinTableElement->options)) {\n                        $joinTable['options'] = $this->parseOptions($joinTableElement->options->children());\n                    }\n\n                    foreach ($joinTableElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);\n                    }\n\n                    foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);\n                    }\n\n                    $mapping['joinTable'] = $joinTable;\n                }\n\n                if (isset($manyToManyElement->cascade)) {\n                    $mapping['cascade'] = $this->getCascadeMappings($manyToManyElement->cascade);\n                }\n\n                if (isset($manyToManyElement->{'order-by'})) {\n                    $orderBy = [];\n                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {\n                        $orderBy[(string) $orderByField['name']] = (string) ($orderByField['direction'] ?? 'ASC');\n                    }\n\n                    $mapping['orderBy'] = $orderBy;\n                }\n\n                if (isset($manyToManyElement['index-by'])) {\n                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];\n                } elseif (isset($manyToManyElement->{'index-by'})) {\n                    throw new InvalidArgumentException('<index-by /> is not a valid tag');\n                }\n\n                // Evaluate second level cache\n                if (isset($manyToManyElement->cache)) {\n                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));\n                }\n\n                $metadata->mapManyToMany($mapping);\n            }\n        }\n\n        // Evaluate association-overrides\n        if (isset($xmlRoot->{'attribute-overrides'})) {\n            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} ?? [] as $overrideElement) {\n                $fieldName = (string) $overrideElement['name'];\n                foreach ($overrideElement->field ?? [] as $field) {\n                    $mapping              = $this->columnToArray($field);\n                    $mapping['fieldName'] = $fieldName;\n                    $metadata->setAttributeOverride($fieldName, $mapping);\n                }\n            }\n        }\n\n        // Evaluate association-overrides\n        if (isset($xmlRoot->{'association-overrides'})) {\n            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} ?? [] as $overrideElement) {\n                $fieldName = (string) $overrideElement['name'];\n                $override  = [];\n\n                // Check for join-columns\n                if (isset($overrideElement->{'join-columns'})) {\n                    $joinColumns = [];\n                    foreach ($overrideElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);\n                    }\n\n                    $override['joinColumns'] = $joinColumns;\n                }\n\n                // Check for join-table\n                if ($overrideElement->{'join-table'}) {\n                    $joinTable        = null;\n                    $joinTableElement = $overrideElement->{'join-table'};\n\n                    $joinTable = [\n                        'name'      => (string) $joinTableElement['name'],\n                        'schema'    => (string) $joinTableElement['schema'],\n                    ];\n\n                    if (isset($joinTableElement->options)) {\n                        $joinTable['options'] = $this->parseOptions($joinTableElement->options->children());\n                    }\n\n                    if (isset($joinTableElement->{'join-columns'})) {\n                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                            $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);\n                        }\n                    }\n\n                    if (isset($joinTableElement->{'inverse-join-columns'})) {\n                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {\n                            $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);\n                        }\n                    }\n\n                    $override['joinTable'] = $joinTable;\n                }\n\n                // Check for inversed-by\n                if (isset($overrideElement->{'inversed-by'})) {\n                    $override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];\n                }\n\n                // Check for `fetch`\n                if (isset($overrideElement['fetch'])) {\n                    $override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . (string) $overrideElement['fetch']);\n                }\n\n                $metadata->setAssociationOverride($fieldName, $override);\n            }\n        }\n\n        // Evaluate <lifecycle-callbacks...>\n        if (isset($xmlRoot->{'lifecycle-callbacks'})) {\n            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} ?? [] as $lifecycleCallback) {\n                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\\ORM\\Events::' . (string) $lifecycleCallback['type']));\n            }\n        }\n\n        // Evaluate entity listener\n        if (isset($xmlRoot->{'entity-listeners'})) {\n            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} ?? [] as $listenerElement) {\n                $className = (string) $listenerElement['class'];\n                // Evaluate the listener using naming convention.\n                if ($listenerElement->count() === 0) {\n                    EntityListenerBuilder::bindEntityListener($metadata, $className);\n\n                    continue;\n                }\n\n                foreach ($listenerElement as $callbackElement) {\n                    $eventName  = (string) $callbackElement['type'];\n                    $methodName = (string) $callbackElement['method'];\n\n                    $metadata->addEntityListener($eventName, $className, $methodName);\n                }\n            }\n        }\n    }\n\n    /**\n     * Parses (nested) option elements.\n     *\n     * @return mixed[] The options array.\n     * @phpstan-return array<int|string, array<int|string, mixed|string>|bool|string|object>\n     */\n    private function parseOptions(SimpleXMLElement|null $options): array\n    {\n        $array = [];\n\n        foreach ($options ?? [] as $option) {\n            $value = null;\n            if ($option->count()) {\n                // Check if this option contains an <object> element\n                $children         = $option->children();\n                $hasObjectElement = false;\n\n                foreach ($children as $child) {\n                    if ($child->getName() === 'object') {\n                        $value            = $this->parseObjectElement($child);\n                        $hasObjectElement = true;\n                        break;\n                    }\n                }\n\n                if (! $hasObjectElement) {\n                    $value = $this->parseOptions($children);\n                }\n            } else {\n                $value = (string) $option;\n            }\n\n            $attributes = $option->attributes();\n\n            if (isset($attributes->name)) {\n                $nameAttribute         = (string) $attributes->name;\n                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'], true)\n                    ? $this->evaluateBoolean($value)\n                    : $value;\n            } else {\n                $array[] = $value;\n            }\n        }\n\n        return $array;\n    }\n\n    /**\n     * Parses an <object> element and returns the instantiated object.\n     *\n     * @param SimpleXMLElement $objectElement The XML element.\n     *\n     * @return object The instantiated object.\n     *\n     * @throws MappingException If the object specification is invalid.\n     * @throws InvalidArgumentException If the class does not exist.\n     */\n    private function parseObjectElement(SimpleXMLElement $objectElement): object\n    {\n        $attributes = $objectElement->attributes();\n\n        if (! isset($attributes->class)) {\n            throw MappingException::missingRequiredOption('object', 'class');\n        }\n\n        $className = (string) $attributes->class;\n\n        if (! class_exists($className)) {\n            throw new InvalidArgumentException(sprintf('Class \"%s\" does not exist', $className));\n        }\n\n        return new $className();\n    }\n\n    /**\n     * Constructs a joinColumn mapping array based on the information\n     * found in the given SimpleXMLElement.\n     *\n     * @param SimpleXMLElement $joinColumnElement The XML element.\n     *\n     * @return mixed[] The mapping array.\n     * @phpstan-return array{\n     *                   name: string,\n     *                   referencedColumnName: string,\n     *                   unique?: bool,\n     *                   nullable?: bool,\n     *                   onDelete?: string,\n     *                   columnDefinition?: string,\n     *                   options?: mixed[]\n     *               }\n     */\n    private function joinColumnToArray(SimpleXMLElement $joinColumnElement): array\n    {\n        $joinColumn = [\n            'name' => (string) $joinColumnElement['name'],\n            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name'],\n        ];\n\n        if (isset($joinColumnElement['unique'])) {\n            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);\n        }\n\n        if (isset($joinColumnElement['nullable'])) {\n            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);\n        }\n\n        if (isset($joinColumnElement['on-delete'])) {\n            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];\n        }\n\n        if (isset($joinColumnElement['column-definition'])) {\n            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];\n        }\n\n        if (isset($joinColumnElement['options'])) {\n            $joinColumn['options'] = $this->parseOptions($joinColumnElement['options'] ? $joinColumnElement['options']->children() : null);\n        }\n\n        return $joinColumn;\n    }\n\n     /**\n      * Parses the given field as array.\n      *\n      * @return mixed[]\n      * @phpstan-return array{\n      *                   fieldName: string,\n      *                   type?: string,\n      *                   columnName?: string,\n      *                   length?: int,\n      *                   precision?: int,\n      *                   scale?: int,\n      *                   unique?: bool,\n      *                   nullable?: bool,\n      *                   index?: bool,\n      *                   notInsertable?: bool,\n      *                   notUpdatable?: bool,\n      *                   enumType?: string,\n      *                   version?: bool,\n      *                   columnDefinition?: string,\n      *                   options?: array\n      *               }\n      */\n    private function columnToArray(SimpleXMLElement $fieldMapping): array\n    {\n        $mapping = [\n            'fieldName' => (string) $fieldMapping['name'],\n        ];\n\n        if (isset($fieldMapping['type'])) {\n            $mapping['type'] = (string) $fieldMapping['type'];\n        }\n\n        if (isset($fieldMapping['column'])) {\n            $mapping['columnName'] = (string) $fieldMapping['column'];\n        }\n\n        if (isset($fieldMapping['length'])) {\n            $mapping['length'] = (int) $fieldMapping['length'];\n        }\n\n        if (isset($fieldMapping['precision'])) {\n            $mapping['precision'] = (int) $fieldMapping['precision'];\n        }\n\n        if (isset($fieldMapping['scale'])) {\n            $mapping['scale'] = (int) $fieldMapping['scale'];\n        }\n\n        if (isset($fieldMapping['unique'])) {\n            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);\n        }\n\n        if (isset($fieldMapping['index'])) {\n            $mapping['index'] = $this->evaluateBoolean($fieldMapping['index']);\n        }\n\n        if (isset($fieldMapping['nullable'])) {\n            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);\n        }\n\n        if (isset($fieldMapping['insertable']) && ! $this->evaluateBoolean($fieldMapping['insertable'])) {\n            $mapping['notInsertable'] = true;\n        }\n\n        if (isset($fieldMapping['updatable']) && ! $this->evaluateBoolean($fieldMapping['updatable'])) {\n            $mapping['notUpdatable'] = true;\n        }\n\n        if (isset($fieldMapping['generated'])) {\n            $mapping['generated'] = constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATED_' . (string) $fieldMapping['generated']);\n        }\n\n        if (isset($fieldMapping['version']) && $fieldMapping['version']) {\n            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);\n        }\n\n        if (isset($fieldMapping['column-definition'])) {\n            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];\n        }\n\n        if (isset($fieldMapping['enum-type'])) {\n            $mapping['enumType'] = (string) $fieldMapping['enum-type'];\n        }\n\n        if (isset($fieldMapping->options)) {\n            $mapping['options'] = $this->parseOptions($fieldMapping->options->children());\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * Parse / Normalize the cache configuration\n     *\n     * @return mixed[]\n     * @phpstan-return array{usage: int|null, region?: string}\n     */\n    private function cacheToArray(SimpleXMLElement $cacheMapping): array\n    {\n        $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;\n        $usage  = isset($cacheMapping['usage']) ? strtoupper((string) $cacheMapping['usage']) : null;\n\n        if ($usage && ! defined('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $usage)) {\n            throw new InvalidArgumentException(sprintf('Invalid cache usage \"%s\"', $usage));\n        }\n\n        if ($usage) {\n            $usage = (int) constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $usage);\n        }\n\n        return [\n            'usage'  => $usage,\n            'region' => $region,\n        ];\n    }\n\n    /**\n     * Gathers a list of cascade options found in the given cascade element.\n     *\n     * @param SimpleXMLElement $cascadeElement The cascade element.\n     *\n     * @return string[] The list of cascade options.\n     * @phpstan-return list<string>\n     */\n    private function getCascadeMappings(SimpleXMLElement $cascadeElement): array\n    {\n        $cascades = [];\n        $children = $cascadeElement->children();\n        assert($children !== null);\n\n        foreach ($children as $action) {\n            // According to the JPA specifications, XML uses \"cascade-persist\"\n            // instead of \"persist\". Here, both variations\n            // are supported because Attribute uses \"persist\"\n            // and we want to make sure that this driver doesn't need to know\n            // anything about the supported cascading actions\n            $cascades[] = str_replace('cascade-', '', $action->getName());\n        }\n\n        return $cascades;\n    }\n\n    /** @return array<class-string, SimpleXMLElement> */\n    private function doLoadMappingFile(string $file): array\n    {\n        $this->validateMapping($file);\n        $result = [];\n        // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577\n        $xmlElement = simplexml_load_string(file_get_contents($file));\n        assert($xmlElement !== false);\n\n        if (isset($xmlElement->entity)) {\n            foreach ($xmlElement->entity as $entityElement) {\n                /** @var class-string $entityName */\n                $entityName          = (string) $entityElement['name'];\n                $result[$entityName] = $entityElement;\n            }\n        } elseif (isset($xmlElement->{'mapped-superclass'})) {\n            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {\n                /** @var class-string $className */\n                $className          = (string) $mappedSuperClass['name'];\n                $result[$className] = $mappedSuperClass;\n            }\n        } elseif (isset($xmlElement->embeddable)) {\n            foreach ($xmlElement->embeddable as $embeddableElement) {\n                /** @var class-string $embeddableName */\n                $embeddableName          = (string) $embeddableElement['name'];\n                $result[$embeddableName] = $embeddableElement;\n            }\n        }\n\n        return $result;\n    }\n\n    private function validateMapping(string $file): void\n    {\n        if (! $this->isXsdValidationEnabled) {\n            return;\n        }\n\n        $backedUpErrorSetting = libxml_use_internal_errors(true);\n\n        try {\n            $document = new DOMDocument();\n            $document->load($file);\n\n            if (! $document->schemaValidate(__DIR__ . '/../../../doctrine-mapping.xsd')) {\n                throw MappingException::fromLibXmlErrors(libxml_get_errors());\n            }\n        } finally {\n            libxml_clear_errors();\n            libxml_use_internal_errors($backedUpErrorSetting);\n        }\n    }\n\n    protected function evaluateBoolean(mixed $element): bool\n    {\n        $flag = (string) $element;\n\n        return $flag === 'true' || $flag === '1';\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Embeddable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class Embeddable implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/Embedded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class Embedded implements MappingAttribute\n{\n    public function __construct(\n        public readonly string|null $class = null,\n        public readonly string|bool|null $columnPrefix = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/EmbeddedClassMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\n\nuse function property_exists;\n\n/** @template-implements ArrayAccess<string, mixed> */\nfinal class EmbeddedClassMapping implements ArrayAccess\n{\n    use ArrayAccessImplementation;\n\n    public string|false|null $columnPrefix = null;\n    public string|null $declaredField      = null;\n    public string|null $originalField      = null;\n\n    /**\n     * This is set when this embedded-class field is inherited by this class\n     * from another (inheritance) parent <em>entity</em> class. The value is\n     * the FQCN of the topmost entity class that contains mapping information\n     * for this field. (If there are transient classes in the class hierarchy,\n     * these are ignored, so the class property may in fact come from a class\n     * further up in the PHP class hierarchy.) Fields initially declared in\n     * mapped superclasses are <em>not</em> considered 'inherited' in the\n     * nearest entity subclasses.\n     *\n     * @var class-string|null\n     */\n    public string|null $inherited = null;\n\n    /**\n     * This is set when the embedded-class field does not appear for the first\n     * time in this class, but is originally declared in another parent\n     * <em>entity or mapped superclass</em>. The value is the FQCN of the\n     * topmost non-transient class that contains mapping information for this\n     * field.\n     *\n     * @var class-string|null\n     */\n    public string|null $declared = null;\n\n    /** @param class-string $class */\n    public function __construct(public string $class)\n    {\n    }\n\n    /**\n     * @phpstan-param array{\n     *     class: class-string,\n     *     columnPrefix?: false|string|null,\n     *     declaredField?: string|null,\n     *     originalField?: string|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): self\n    {\n        $mapping = new self($mappingArray['class']);\n        foreach ($mappingArray as $key => $value) {\n            if ($key === 'class') {\n                continue;\n            }\n\n            if (property_exists($mapping, $key)) {\n                $mapping->$key = $value;\n            }\n        }\n\n        return $mapping;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = ['class'];\n\n        if ($this->columnPrefix) {\n            $serialized[] = 'columnPrefix';\n        }\n\n        foreach (['declaredField', 'originalField', 'inherited', 'declared'] as $property) {\n            if ($this->$property !== null) {\n                $serialized[] = $property;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\nuse Doctrine\\ORM\\EntityRepository;\n\n/** @template T of object */\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class Entity implements MappingAttribute\n{\n    /** @phpstan-param class-string<EntityRepository<T>>|null $repositoryClass */\n    public function __construct(\n        public readonly string|null $repositoryClass = null,\n        public readonly bool $readOnly = false,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/EntityListenerResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/**\n * A resolver is used to instantiate an entity listener.\n */\ninterface EntityListenerResolver\n{\n    /**\n     * Clear all instances from the set, or a specific instance when given its identifier.\n     *\n     * @param string|null $className May be any arbitrary string. Name kept for BC only.\n     */\n    public function clear(string|null $className = null): void;\n\n    /**\n     * Returns a entity listener instance for the given identifier.\n     *\n     * @param string $className May be any arbitrary string. Name kept for BC only.\n     */\n    public function resolve(string $className): object;\n\n    /**\n     * Register a entity listener instance.\n     */\n    public function register(object $object): void;\n}\n"
  },
  {
    "path": "src/Mapping/EntityListeners.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n/**\n * The EntityListeners attribute specifies the callback listener classes to be used for an entity or mapped superclass.\n * The EntityListeners attribute may be applied to an entity class or mapped superclass.\n */\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class EntityListeners implements MappingAttribute\n{\n    /** @param array<string> $value */\n    public function __construct(\n        public readonly array $value = [],\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Exception/InvalidCustomGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Exception;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nuse function sprintf;\nuse function var_export;\n\nfinal class InvalidCustomGenerator extends LogicException implements ORMException\n{\n    public static function onClassNotConfigured(): self\n    {\n        return new self('Cannot instantiate custom generator, no class has been defined');\n    }\n\n    /** @param mixed[] $definition */\n    public static function onMissingClass(array $definition): self\n    {\n        return new self(sprintf(\n            'Cannot instantiate custom generator : %s',\n            var_export($definition, true),\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Exception/UnknownGeneratorType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\Exception;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nfinal class UnknownGeneratorType extends LogicException implements ORMException\n{\n    public static function create(int $generatorType): self\n    {\n        return new self('Unknown generator type: ' . $generatorType);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/FieldMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\nuse BackedEnum;\n\nuse function in_array;\nuse function property_exists;\n\n/** @template-implements ArrayAccess<string, mixed> */\nfinal class FieldMapping implements ArrayAccess\n{\n    use ArrayAccessImplementation;\n\n    /** The database length of the column. Optional. Default value taken from the type. */\n    public int|null $length = null;\n    /**\n     * Marks the field as the primary key of the entity. Multiple\n     * fields of an entity can have the id attribute, forming a composite key.\n     */\n    public bool|null $id                 = null;\n    public bool|null $nullable           = null;\n    public bool|null $notInsertable      = null;\n    public bool|null $notUpdatable       = null;\n    public string|null $columnDefinition = null;\n    /** @phpstan-var ClassMetadata::GENERATED_*|null */\n    public int|null $generated = null;\n    /** @var class-string<BackedEnum>|null */\n    public string|null $enumType = null;\n    /**\n     * The precision of a decimal column.\n     * Only valid if the column type is decimal\n     */\n    public int|null $precision = null;\n    /**\n     * The scale of a decimal column.\n     * Only valid if the column type is decimal\n     */\n    public int|null $scale = null;\n    /** Whether a unique constraint should be generated for the column. */\n    public bool|null $unique = null;\n    /** Whether an index should be generated for the column. */\n    public bool|null $index = null;\n    /**\n     * @var class-string|null This is set when the field is inherited by this\n     * class from another (inheritance) parent <em>entity</em> class. The value\n     * is the FQCN of the topmost entity class that contains mapping information\n     * for this field. (If there are transient classes in the class hierarchy,\n     * these are ignored, so the class property may in fact come from a class\n     * further up in the PHP class hierarchy.)\n     * Fields initially declared in mapped superclasses are\n     * <em>not</em> considered 'inherited' in the nearest entity subclasses.\n     */\n    public string|null $inherited = null;\n\n    /** @var class-string|null */\n    public string|null $originalClass = null;\n    public string|null $originalField = null;\n    public bool|null $quoted          = null;\n    /**\n     * @var class-string|null This is set when the field does not appear for\n     * the first time in this class, but is originally declared in another\n     * parent <em>entity or mapped superclass</em>. The value is the FQCN of\n     * the topmost non-transient class that contains mapping information for\n     * this field.\n     */\n    public string|null $declared      = null;\n    public string|null $declaredField = null;\n    public array|null $options        = null;\n    public bool|null $version         = null;\n\n    /** @deprecated Use options with 'default' key instead */\n    public string|int|null $default = null;\n\n    /**\n     * @param string $type       The type name of the mapped field. Can be one of\n     *                           Doctrine's mapping types or a custom mapping type.\n     * @param string $fieldName  The name of the field in the Entity.\n     * @param string $columnName The column name. Optional. Defaults to the field name.\n     */\n    public function __construct(\n        public string $type,\n        public string $fieldName,\n        public string $columnName,\n    ) {\n    }\n\n    /**\n     * @param array<string, mixed> $mappingArray\n     * @phpstan-param array{\n     *     type: string,\n     *     fieldName: string,\n     *     columnName: string,\n     *     length?: int|null,\n     *     id?: bool|null,\n     *     nullable?: bool|null,\n     *     index?: bool|null,\n     *     notInsertable?: bool|null,\n     *     notUpdatable?: bool|null,\n     *     columnDefinition?: string|null,\n     *     generated?: ClassMetadata::GENERATED_*|null,\n     *     enumType?: string|null,\n     *     precision?: int|null,\n     *     scale?: int|null,\n     *     unique?: bool|null,\n     *     inherited?: string|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     quoted?: bool|null,\n     *     declared?: string|null,\n     *     declaredField?: string|null,\n     *     options?: array<string, mixed>|null,\n     *     version?: bool|null,\n     *     default?: string|int|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): self\n    {\n        $mapping = new self(\n            $mappingArray['type'],\n            $mappingArray['fieldName'],\n            $mappingArray['columnName'],\n        );\n        foreach ($mappingArray as $key => $value) {\n            if (in_array($key, ['type', 'fieldName', 'columnName'])) {\n                continue;\n            }\n\n            if (property_exists($mapping, $key)) {\n                $mapping->$key = $value;\n            }\n        }\n\n        return $mapping;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = ['type', 'fieldName', 'columnName'];\n\n        foreach (['nullable', 'notInsertable', 'notUpdatable', 'id', 'unique', 'version', 'quoted', 'index'] as $boolKey) {\n            if ($this->$boolKey) {\n                $serialized[] = $boolKey;\n            }\n        }\n\n        foreach (\n            [\n                'length',\n                'columnDefinition',\n                'generated',\n                'enumType',\n                'precision',\n                'scale',\n                'inherited',\n                'originalClass',\n                'originalField',\n                'declared',\n                'declaredField',\n                'options',\n                'default',\n            ] as $key\n        ) {\n            if ($this->$key !== null) {\n                $serialized[] = $key;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/GeneratedValue.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class GeneratedValue implements MappingAttribute\n{\n    /** @phpstan-param 'AUTO'|'SEQUENCE'|'IDENTITY'|'NONE'|'CUSTOM' $strategy */\n    public function __construct(\n        public readonly string $strategy = 'AUTO',\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/GetReflectionClassImplementation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Persistence\\Mapping\\StaticReflectionService;\nuse ReflectionClass;\n\nuse function class_exists;\n\nif (! class_exists(StaticReflectionService::class)) {\n    trait GetReflectionClassImplementation\n    {\n        public function getReflectionClass(): ReflectionClass\n        {\n            return $this->reflClass;\n        }\n    }\n} else {\n    trait GetReflectionClassImplementation\n    {\n        /**\n         * {@inheritDoc}\n         *\n         * Can return null when using static reflection, in violation of the LSP\n         */\n        public function getReflectionClass(): ReflectionClass|null\n        {\n            return $this->reflClass;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/HasLifecycleCallbacks.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class HasLifecycleCallbacks implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/Id.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class Id implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/Index.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]\nfinal class Index implements MappingAttribute\n{\n    /**\n     * @param array<string>|null       $columns\n     * @param array<string>|null       $fields\n     * @param array<string>|null       $flags\n     * @param array<string,mixed>|null $options\n     */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly array|null $columns = null,\n        public readonly array|null $fields = null,\n        public readonly array|null $flags = null,\n        public readonly array|null $options = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/InheritanceType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class InheritanceType implements MappingAttribute\n{\n    /** @phpstan-param 'NONE'|'JOINED'|'SINGLE_TABLE' $value */\n    public function __construct(\n        public readonly string $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/InverseJoinColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]\nfinal class InverseJoinColumn implements MappingAttribute\n{\n    use JoinColumnProperties;\n}\n"
  },
  {
    "path": "src/Mapping/InverseSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nabstract class InverseSideMapping extends AssociationMapping\n{\n    /**\n     * required for bidirectional associations\n     * The name of the field that completes the bidirectional association on\n     * the owning side. This key must be specified on the inverse side of a\n     * bidirectional association.\n     */\n    public string $mappedBy;\n\n    final public function backRefFieldName(): string\n    {\n        return $this->mappedBy;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        return [\n            ...parent::__sleep(),\n            'mappedBy',\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Mapping/JoinColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]\nfinal class JoinColumn implements MappingAttribute\n{\n    use JoinColumnProperties;\n}\n"
  },
  {
    "path": "src/Mapping/JoinColumnMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\n\nuse function property_exists;\n\n/** @template-implements ArrayAccess<string, mixed> */\nfinal class JoinColumnMapping implements ArrayAccess\n{\n    use ArrayAccessImplementation;\n\n    public bool|null $deferrable         = null;\n    public bool|null $unique             = null;\n    public bool|null $quoted             = null;\n    public string|null $fieldName        = null;\n    public string|null $onDelete         = null;\n    public string|null $columnDefinition = null;\n    public bool|null $nullable           = null;\n\n    /** @var array<string, mixed>|null */\n    public array|null $options = null;\n\n    public function __construct(\n        public string $name,\n        public string $referencedColumnName,\n    ) {\n    }\n\n    /**\n     * @param array<string, mixed> $mappingArray\n     * @phpstan-param array{\n     *     name: string,\n     *     referencedColumnName: string|null,\n     *     unique?: bool|null,\n     *     quoted?: bool|null,\n     *     fieldName?: string|null,\n     *     onDelete?: string|null,\n     *     columnDefinition?: string|null,\n     *     nullable?: bool|null,\n     *     options?: array<string, mixed>|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): self\n    {\n        $mapping = new self($mappingArray['name'], $mappingArray['referencedColumnName']);\n        foreach ($mappingArray as $key => $value) {\n            if (property_exists($mapping, $key) && $value !== null) {\n                $mapping->$key = $value;\n            }\n        }\n\n        return $mapping;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = [];\n\n        foreach (['name', 'fieldName', 'onDelete', 'columnDefinition', 'referencedColumnName', 'options'] as $stringOrArrayKey) {\n            if ($this->$stringOrArrayKey !== null) {\n                $serialized[] = $stringOrArrayKey;\n            }\n        }\n\n        foreach (['deferrable', 'unique', 'quoted', 'nullable'] as $boolKey) {\n            if ($this->$boolKey !== null) {\n                $serialized[] = $boolKey;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/JoinColumnProperties.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\ntrait JoinColumnProperties\n{\n    /** @param array<string, mixed> $options */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly string|null $referencedColumnName = null,\n        public readonly bool $deferrable = false,\n        public readonly bool $unique = false,\n        public readonly bool|null $nullable = null,\n        public readonly mixed $onDelete = null,\n        public readonly string|null $columnDefinition = null,\n        public readonly string|null $fieldName = null,\n        public readonly array $options = [],\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/JoinColumns.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nfinal class JoinColumns implements MappingAttribute\n{\n    /** @param array<JoinColumn> $value */\n    public function __construct(\n        public readonly array $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/JoinTable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class JoinTable implements MappingAttribute\n{\n    /** @var array<JoinColumn> */\n    public readonly array $joinColumns;\n\n    /** @var array<JoinColumn> */\n    public readonly array $inverseJoinColumns;\n\n    /**\n     * @param array<JoinColumn>|JoinColumn $joinColumns\n     * @param array<JoinColumn>|JoinColumn $inverseJoinColumns\n     * @param array<string, mixed>         $options\n     */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly string|null $schema = null,\n        array|JoinColumn $joinColumns = [],\n        array|JoinColumn $inverseJoinColumns = [],\n        public readonly array $options = [],\n    ) {\n        $this->joinColumns        = $joinColumns instanceof JoinColumn ? [$joinColumns] : $joinColumns;\n        $this->inverseJoinColumns = $inverseJoinColumns instanceof JoinColumn\n            ? [$inverseJoinColumns]\n            : $inverseJoinColumns;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/JoinTableMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\n\nuse function array_map;\nuse function in_array;\n\n/** @template-implements ArrayAccess<string, mixed> */\nfinal class JoinTableMapping implements ArrayAccess\n{\n    use ArrayAccessImplementation;\n\n    public bool|null $quoted = null;\n\n    /** @var list<JoinColumnMapping> */\n    public array $joinColumns = [];\n\n    /** @var list<JoinColumnMapping> */\n    public array $inverseJoinColumns = [];\n\n    /** @var array<string, mixed> */\n    public array $options = [];\n\n    public string|null $schema = null;\n\n    public function __construct(public string $name)\n    {\n    }\n\n    /**\n     * @param mixed[] $mappingArray\n     * @phpstan-param array{\n     *    name: string,\n     *    quoted?: bool|null,\n     *    joinColumns?: mixed[],\n     *    inverseJoinColumns?: mixed[],\n     *    schema?: string|null,\n     *    options?: array<string, mixed>\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): self\n    {\n        $mapping = new self($mappingArray['name']);\n\n        foreach (['quoted', 'schema', 'options'] as $key) {\n            if (isset($mappingArray[$key])) {\n                $mapping->$key = $mappingArray[$key];\n            }\n        }\n\n        if (isset($mappingArray['joinColumns'])) {\n            foreach ($mappingArray['joinColumns'] as $column) {\n                $mapping->joinColumns[] = JoinColumnMapping::fromMappingArray($column);\n            }\n        }\n\n        if (isset($mappingArray['inverseJoinColumns'])) {\n            foreach ($mappingArray['inverseJoinColumns'] as $column) {\n                $mapping->inverseJoinColumns[] = JoinColumnMapping::fromMappingArray($column);\n            }\n        }\n\n        return $mapping;\n    }\n\n    public function offsetSet(mixed $offset, mixed $value): void\n    {\n        if (in_array($offset, ['joinColumns', 'inverseJoinColumns'], true)) {\n            $joinColumns = [];\n            foreach ($value as $column) {\n                $joinColumns[] = JoinColumnMapping::fromMappingArray($column);\n            }\n\n            $value = $joinColumns;\n        }\n\n        $this->$offset = $value;\n    }\n\n    /** @return mixed[] */\n    public function toArray(): array\n    {\n        $array                       = (array) $this;\n        $toArray                     = static function (JoinColumnMapping $column) {\n            $array = (array) $column;\n\n            unset($array['nullable']);\n\n            return $array;\n        };\n        $array['joinColumns']        = array_map($toArray, $array['joinColumns']);\n        $array['inverseJoinColumns'] = array_map($toArray, $array['inverseJoinColumns']);\n\n        return $array;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = [];\n\n        foreach (['joinColumns', 'inverseJoinColumns', 'name', 'schema', 'options'] as $stringOrArrayKey) {\n            if ($this->$stringOrArrayKey !== null) {\n                $serialized[] = $stringOrArrayKey;\n            }\n        }\n\n        foreach (['quoted'] as $boolKey) {\n            if ($this->$boolKey) {\n                $serialized[] = $boolKey;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/LegacyReflectionFields.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse ArrayAccess;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\Persistence\\Mapping\\ReflectionService;\nuse Doctrine\\Persistence\\Reflection\\EnumReflectionProperty;\nuse Generator;\nuse IteratorAggregate;\nuse OutOfBoundsException;\nuse ReflectionProperty;\nuse Traversable;\n\nuse function array_keys;\nuse function assert;\nuse function is_string;\nuse function str_contains;\nuse function str_replace;\n\n/**\n * @template-implements ArrayAccess<string, ReflectionProperty|null>\n * @template-implements IteratorAggregate<string, ReflectionProperty|null>\n */\nclass LegacyReflectionFields implements ArrayAccess, IteratorAggregate\n{\n    /** @var array<string, ReflectionProperty|null> */\n    private array $reflFields = [];\n\n    public function __construct(private ClassMetadata $classMetadata, private ReflectionService $reflectionService)\n    {\n    }\n\n    /** @param string $offset */\n    public function offsetExists($offset): bool // phpcs:ignore\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11659',\n            'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',\n        );\n\n        return isset($this->classMetadata->propertyAccessors[$offset]);\n    }\n\n    /**\n     * @param string $field\n     *\n     * @psalm-suppress LessSpecificImplementedReturnType\n     */\n    public function offsetGet($field): mixed // phpcs:ignore\n    {\n        if (isset($this->reflFields[$field])) {\n            return $this->reflFields[$field];\n        }\n\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11659',\n            'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',\n        );\n\n        if (isset($this->classMetadata->propertyAccessors[$field])) {\n            $fieldName = str_contains($field, '.') ? $this->classMetadata->fieldMappings[$field]->originalField : $field;\n            $className = $this->classMetadata->name;\n\n            assert(is_string($fieldName));\n\n            if (isset($this->classMetadata->fieldMappings[$field]) && $this->classMetadata->fieldMappings[$field]->originalClass !== null) {\n                $className = $this->classMetadata->fieldMappings[$field]->originalClass;\n            } elseif (isset($this->classMetadata->fieldMappings[$field]) && $this->classMetadata->fieldMappings[$field]->declared !== null) {\n                $className = $this->classMetadata->fieldMappings[$field]->declared;\n            } elseif (isset($this->classMetadata->associationMappings[$field]) && $this->classMetadata->associationMappings[$field]->declared !== null) {\n                $className = $this->classMetadata->associationMappings[$field]->declared;\n            } elseif (isset($this->classMetadata->embeddedClasses[$field]) && $this->classMetadata->embeddedClasses[$field]->declared !== null) {\n                $className = $this->classMetadata->embeddedClasses[$field]->declared;\n            }\n\n            /** @psalm-suppress ArgumentTypeCoercion */\n            $this->reflFields[$field] = $this->getAccessibleProperty($className, $fieldName);\n\n            if (isset($this->classMetadata->fieldMappings[$field])) {\n                if ($this->classMetadata->fieldMappings[$field]->enumType !== null) {\n                    $this->reflFields[$field] = new EnumReflectionProperty(\n                        $this->reflFields[$field],\n                        $this->classMetadata->fieldMappings[$field]->enumType,\n                    );\n                }\n\n                if ($this->classMetadata->fieldMappings[$field]->originalField !== null) {\n                    $parentField   = str_replace('.' . $fieldName, '', $field);\n                    $originalClass = $this->classMetadata->fieldMappings[$field]->originalClass;\n\n                    if (! str_contains($parentField, '.')) {\n                        $parentClass = $this->classMetadata->name;\n                    } else {\n                        $parentClass = $this->classMetadata->fieldMappings[$parentField]->originalClass;\n                    }\n\n                    /** @psalm-var class-string $parentClass */\n                    /** @psalm-var class-string $originalClass */\n\n                    $this->reflFields[$field] = new ReflectionEmbeddedProperty(\n                        $this->getAccessibleProperty($parentClass, $parentField),\n                        $this->reflFields[$field],\n                        $originalClass,\n                    );\n                }\n            }\n\n            return $this->reflFields[$field];\n        }\n\n        throw new OutOfBoundsException('Unknown field: ' . $this->classMetadata->name . ' ::$' . $field);\n    }\n\n    /**\n     * @param string             $offset\n     * @param ReflectionProperty $value\n     */\n    public function offsetSet($offset, $value): void // phpcs:ignore\n    {\n        $this->reflFields[$offset] = $value;\n    }\n\n    /** @param string $offset */\n    public function offsetUnset($offset): void // phpcs:ignore\n    {\n        unset($this->reflFields[$offset]);\n    }\n\n    /** @psalm-param class-string $class */\n    private function getAccessibleProperty(string $class, string $field): ReflectionProperty\n    {\n        $reflectionProperty = $this->reflectionService->getAccessibleProperty($class, $field);\n\n        assert($reflectionProperty !== null);\n\n        if ($reflectionProperty->isReadOnly()) {\n            $declaringClass = $reflectionProperty->class;\n            if ($declaringClass !== $class) {\n                $reflectionProperty = $this->reflectionService->getAccessibleProperty($declaringClass, $field);\n\n                assert($reflectionProperty !== null);\n            }\n\n            $reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty);\n        }\n\n        return $reflectionProperty;\n    }\n\n    /** @return Generator<string, ReflectionProperty> */\n    public function getIterator(): Traversable\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11659',\n            'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',\n        );\n\n        $keys = array_keys($this->classMetadata->propertyAccessors);\n\n        foreach ($keys as $key) {\n            yield $key => $this->offsetGet($key);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ManyToMany.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class ManyToMany implements MappingAttribute\n{\n    /**\n     * @param class-string  $targetEntity\n     * @param string[]|null $cascade\n     * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch\n     */\n    public function __construct(\n        public readonly string $targetEntity,\n        public readonly string|null $mappedBy = null,\n        public readonly string|null $inversedBy = null,\n        public readonly array|null $cascade = null,\n        public readonly string $fetch = 'LAZY',\n        public readonly bool $orphanRemoval = false,\n        public readonly string|null $indexBy = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ManyToManyAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\ninterface ManyToManyAssociationMapping extends ToManyAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/ManyToManyInverseSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nfinal class ManyToManyInverseSideMapping extends ToManyInverseSideMapping implements ManyToManyAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/ManyToManyOwningSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\Deprecation;\n\nuse function strtolower;\nuse function trim;\n\nfinal class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implements ManyToManyAssociationMapping\n{\n    /**\n     * Specification of the join table and its join columns (foreign keys).\n     * Only valid for many-to-many mappings. Note that one-to-many associations\n     * can be mapped through a join table by simply mapping the association as\n     * many-to-many with a unique constraint on the join table.\n     */\n    public JoinTableMapping $joinTable;\n\n    /** @var list<mixed> */\n    public array $joinTableColumns = [];\n\n    /** @var array<string, string> */\n    public array $relationToSourceKeyColumns = [];\n    /** @var array<string, string> */\n    public array $relationToTargetKeyColumns = [];\n\n    /** @return array<string, mixed> */\n    public function toArray(): array\n    {\n        $array = parent::toArray();\n\n        $array['joinTable'] = $this->joinTable->toArray();\n\n        return $array;\n    }\n\n    /**\n     * @param mixed[] $mappingArray\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     * } $mappingArray\n     */\n    public static function fromMappingArrayAndNamingStrategy(array $mappingArray, NamingStrategy $namingStrategy): self\n    {\n        if (isset($mappingArray['joinTable']['joinColumns'])) {\n            foreach ($mappingArray['joinTable']['joinColumns'] as $key => $joinColumn) {\n                if (empty($joinColumn['referencedColumnName'])) {\n                    $mappingArray['joinTable']['joinColumns'][$key]['referencedColumnName'] = $namingStrategy->referenceColumnName();\n                }\n\n                if (empty($joinColumn['name'])) {\n                    $mappingArray['joinTable']['joinColumns'][$key]['name'] = $namingStrategy->joinKeyColumnName(\n                        $mappingArray['sourceEntity'],\n                        $joinColumn['referencedColumnName'] ?? $namingStrategy->referenceColumnName(),\n                    );\n                }\n            }\n        }\n\n        if (isset($mappingArray['joinTable']['inverseJoinColumns'])) {\n            foreach ($mappingArray['joinTable']['inverseJoinColumns'] as $key => $joinColumn) {\n                if (empty($joinColumn['referencedColumnName'])) {\n                    $mappingArray['joinTable']['inverseJoinColumns'][$key]['referencedColumnName'] = $namingStrategy->referenceColumnName();\n                }\n\n                if (empty($joinColumn['name'])) {\n                    $mappingArray['joinTable']['inverseJoinColumns'][$key]['name'] = $namingStrategy->joinKeyColumnName(\n                        $mappingArray['targetEntity'],\n                        $joinColumn['referencedColumnName'] ?? $namingStrategy->referenceColumnName(),\n                    );\n                }\n            }\n        }\n\n        // owning side MUST have a join table\n        if (! isset($mappingArray['joinTable']) || ! isset($mappingArray['joinTable']['name'])) {\n            $mappingArray['joinTable']['name'] = $namingStrategy->joinTableName(\n                $mappingArray['sourceEntity'],\n                $mappingArray['targetEntity'],\n                $mappingArray['fieldName'],\n            );\n        }\n\n        $mapping = parent::fromMappingArray($mappingArray);\n\n        $selfReferencingEntityWithoutJoinColumns = $mapping->sourceEntity === $mapping->targetEntity\n            && $mapping->joinTable->joinColumns === []\n            && $mapping->joinTable->inverseJoinColumns === [];\n\n        if ($mapping->joinTable->joinColumns === []) {\n            $mapping->joinTable->joinColumns = [\n                JoinColumnMapping::fromMappingArray([\n                    'name' => $namingStrategy->joinKeyColumnName($mapping->sourceEntity, $selfReferencingEntityWithoutJoinColumns ? 'source' : null),\n                    'referencedColumnName' => $namingStrategy->referenceColumnName(),\n                    'onDelete' => 'CASCADE',\n                ]),\n            ];\n        }\n\n        if ($mapping->joinTable->inverseJoinColumns === []) {\n            $mapping->joinTable->inverseJoinColumns = [\n                JoinColumnMapping::fromMappingArray([\n                    'name' => $namingStrategy->joinKeyColumnName($mapping->targetEntity, $selfReferencingEntityWithoutJoinColumns ? 'target' : null),\n                    'referencedColumnName' => $namingStrategy->referenceColumnName(),\n                    'onDelete' => 'CASCADE',\n                ]),\n            ];\n        }\n\n        $mapping->joinTableColumns = [];\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            if ($joinColumn->nullable !== null) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/pull/12126',\n                    <<<'DEPRECATION'\n                    Specifying the \"nullable\" attribute for join columns in many-to-many associations (here, %s::$%s) is a no-op.\n                    The ORM will always set it to false.\n                    Doing so is deprecated and will be an error in 4.0.\n                    DEPRECATION,\n                    $mapping->sourceEntity,\n                    $mapping->fieldName,\n                );\n            }\n\n            $joinColumn->nullable = false;\n\n            if (empty($joinColumn->referencedColumnName)) {\n                $joinColumn->referencedColumnName = $namingStrategy->referenceColumnName();\n            }\n\n            if ($joinColumn->name[0] === '`') {\n                $joinColumn->name   = trim($joinColumn->name, '`');\n                $joinColumn->quoted = true;\n            }\n\n            if ($joinColumn->referencedColumnName[0] === '`') {\n                $joinColumn->referencedColumnName = trim($joinColumn->referencedColumnName, '`');\n                $joinColumn->quoted               = true;\n            }\n\n            if (isset($joinColumn->onDelete) && strtolower($joinColumn->onDelete) === 'cascade') {\n                $mapping->isOnDeleteCascade = true;\n            }\n\n            $mapping->relationToSourceKeyColumns[$joinColumn->name] = $joinColumn->referencedColumnName;\n            $mapping->joinTableColumns[]                            = $joinColumn->name;\n        }\n\n        foreach ($mapping->joinTable->inverseJoinColumns as $inverseJoinColumn) {\n            if ($inverseJoinColumn->nullable !== null) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/pull/12126',\n                    <<<'DEPRECATION'\n                    Specifying the \"nullable\" attribute for join columns in many-to-many associations (here, %s::$%s) is a no-op.\n                    The ORM will always set it to false.\n                    Doing so is deprecated and will be an error in 4.0.\n                    DEPRECATION,\n                    $mapping->targetEntity,\n                    $mapping->fieldName,\n                );\n            }\n\n            $inverseJoinColumn->nullable = false;\n\n            if (empty($inverseJoinColumn->referencedColumnName)) {\n                $inverseJoinColumn->referencedColumnName = $namingStrategy->referenceColumnName();\n            }\n\n            if ($inverseJoinColumn->name[0] === '`') {\n                $inverseJoinColumn->name   = trim($inverseJoinColumn->name, '`');\n                $inverseJoinColumn->quoted = true;\n            }\n\n            if ($inverseJoinColumn->referencedColumnName[0] === '`') {\n                $inverseJoinColumn->referencedColumnName = trim($inverseJoinColumn->referencedColumnName, '`');\n                $inverseJoinColumn->quoted               = true;\n            }\n\n            if (isset($inverseJoinColumn->onDelete) && strtolower($inverseJoinColumn->onDelete) === 'cascade') {\n                $mapping->isOnDeleteCascade = true;\n            }\n\n            $mapping->relationToTargetKeyColumns[$inverseJoinColumn->name] = $inverseJoinColumn->referencedColumnName;\n            $mapping->joinTableColumns[]                                   = $inverseJoinColumn->name;\n        }\n\n        return $mapping;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized   = parent::__sleep();\n        $serialized[] = 'joinTable';\n        $serialized[] = 'joinTableColumns';\n\n        foreach (['relationToSourceKeyColumns', 'relationToTargetKeyColumns'] as $arrayKey) {\n            if ($this->$arrayKey !== null) {\n                $serialized[] = $arrayKey;\n            }\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ManyToOne.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class ManyToOne implements MappingAttribute\n{\n    /**\n     * @param class-string|null $targetEntity\n     * @param string[]|null     $cascade\n     * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch\n     */\n    public function __construct(\n        public readonly string|null $targetEntity = null,\n        public readonly array|null $cascade = null,\n        public readonly string $fetch = 'LAZY',\n        public readonly string|null $inversedBy = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ManyToOneAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/**\n * The \"many\" side of a many-to-one association mapping is always the owning side.\n */\nfinal class ManyToOneAssociationMapping extends ToOneOwningSideMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/MappedSuperclass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\nuse Doctrine\\ORM\\EntityRepository;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class MappedSuperclass implements MappingAttribute\n{\n    /** @param class-string<EntityRepository>|null $repositoryClass */\n    public function __construct(\n        public readonly string|null $repositoryClass = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/MappingAttribute.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/** A marker interface for mapping attributes. */\ninterface MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/MappingException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse BackedEnum;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\Persistence\\Mapping\\MappingException as PersistenceMappingException;\nuse LibXMLError;\nuse ReflectionException;\nuse ValueError;\n\nuse function array_keys;\nuse function array_map;\nuse function array_values;\nuse function get_debug_type;\nuse function get_parent_class;\nuse function implode;\nuse function sprintf;\n\nuse const PHP_EOL;\n\n/**\n * A MappingException indicates that something is wrong with the mapping setup.\n */\nclass MappingException extends PersistenceMappingException implements ORMException\n{\n    /** @param class-string $entityName */\n    public static function identifierRequired(string $entityName): self\n    {\n        $parent = get_parent_class($entityName);\n        if ($parent !== false) {\n            return new self(sprintf(\n                'No identifier/primary key specified for Entity \"%s\" sub class of \"%s\". Every Entity must have an identifier/primary key.',\n                $entityName,\n                $parent,\n            ));\n        }\n\n        return new self(sprintf(\n            'No identifier/primary key specified for Entity \"%s\". Every Entity must have an identifier/primary key.',\n            $entityName,\n        ));\n    }\n\n    public static function invalidAssociationType(string $entityName, string $fieldName, int $type): self\n    {\n        return new self(sprintf(\n            'The association \"%s#%s\" must be of type \"ClassMetadata::ONE_TO_MANY\", \"ClassMetadata::MANY_TO_MANY\" or \"ClassMetadata::MANY_TO_ONE\", \"%d\" given.',\n            $entityName,\n            $fieldName,\n            $type,\n        ));\n    }\n\n    public static function invalidInheritanceType(string $entityName, int $type): self\n    {\n        return new self(sprintf(\"The inheritance type '%s' specified for '%s' does not exist.\", $type, $entityName));\n    }\n\n    public static function generatorNotAllowedWithCompositeId(): self\n    {\n        return new self(\"Id generators can't be used with a composite id.\");\n    }\n\n    public static function missingFieldName(string $entity): self\n    {\n        return new self(sprintf(\n            \"The field or association mapping misses the 'fieldName' attribute in entity '%s'.\",\n            $entity,\n        ));\n    }\n\n    public static function missingTargetEntity(string $fieldName): self\n    {\n        return new self(sprintf(\"The association mapping '%s' misses the 'targetEntity' attribute.\", $fieldName));\n    }\n\n    public static function missingSourceEntity(string $fieldName): self\n    {\n        return new self(sprintf(\"The association mapping '%s' misses the 'sourceEntity' attribute.\", $fieldName));\n    }\n\n    public static function missingEmbeddedClass(string $fieldName): self\n    {\n        return new self(sprintf(\"The embed mapping '%s' misses the 'class' attribute.\", $fieldName));\n    }\n\n    public static function mappingFileNotFound(string $entityName, string $fileName): self\n    {\n        return new self(sprintf(\"No mapping file found named '%s' for class '%s'.\", $fileName, $entityName));\n    }\n\n    /**\n     * Exception for invalid property name override.\n     *\n     * @param string $className The entity's name.\n     */\n    public static function invalidOverrideFieldName(string $className, string $fieldName): self\n    {\n        return new self(sprintf(\"Invalid field override named '%s' for class '%s'.\", $fieldName, $className));\n    }\n\n    /**\n     * Exception for invalid property type override.\n     *\n     * @param string $className The entity's name.\n     */\n    public static function invalidOverrideFieldType(string $className, string $fieldName): self\n    {\n        return new self(sprintf(\n            \"The column type of attribute '%s' on class '%s' could not be changed.\",\n            $fieldName,\n            $className,\n        ));\n    }\n\n    public static function mappingNotFound(string $className, string $fieldName): self\n    {\n        return new self(sprintf(\"No mapping found for field '%s' on class '%s'.\", $fieldName, $className));\n    }\n\n    public static function queryNotFound(string $className, string $queryName): self\n    {\n        return new self(sprintf(\"No query found named '%s' on class '%s'.\", $queryName, $className));\n    }\n\n    public static function resultMappingNotFound(string $className, string $resultName): self\n    {\n        return new self(sprintf(\"No result set mapping found named '%s' on class '%s'.\", $resultName, $className));\n    }\n\n    public static function emptyQueryMapping(string $entity, string $queryName): self\n    {\n        return new self(sprintf('Query named \"%s\" in \"%s\" could not be empty.', $queryName, $entity));\n    }\n\n    public static function nameIsMandatoryForQueryMapping(string $className): self\n    {\n        return new self(sprintf(\"Query name on entity class '%s' is not defined.\", $className));\n    }\n\n    public static function missingQueryMapping(string $entity, string $queryName): self\n    {\n        return new self(sprintf(\n            'Query named \"%s\" in \"%s requires a result class or result set mapping.',\n            $queryName,\n            $entity,\n        ));\n    }\n\n    public static function missingResultSetMappingEntity(string $entity, string $resultName): self\n    {\n        return new self(sprintf(\n            'Result set mapping named \"%s\" in \"%s requires a entity class name.',\n            $resultName,\n            $entity,\n        ));\n    }\n\n    public static function missingResultSetMappingFieldName(string $entity, string $resultName): self\n    {\n        return new self(sprintf(\n            'Result set mapping named \"%s\" in \"%s requires a field name.',\n            $resultName,\n            $entity,\n        ));\n    }\n\n    public static function oneToManyRequiresMappedBy(string $entityName, string $fieldName): MappingException\n    {\n        return new self(sprintf(\n            \"OneToMany mapping on entity '%s' field '%s' requires the 'mappedBy' attribute.\",\n            $entityName,\n            $fieldName,\n        ));\n    }\n\n    public static function joinTableRequired(string $fieldName): self\n    {\n        return new self(sprintf(\"The mapping of field '%s' requires the 'joinTable' attribute.\", $fieldName));\n    }\n\n    /**\n     * Called if a required option was not found but is required\n     *\n     * @param string $field          Which field cannot be processed?\n     * @param string $expectedOption Which option is required\n     * @param string $hint           Can optionally be used to supply a tip for common mistakes,\n     *                               e.g. \"Did you think of the plural s?\"\n     */\n    public static function missingRequiredOption(string $field, string $expectedOption, string $hint = ''): self\n    {\n        $message = \"The mapping of field '\" . $field . \"' is invalid: The option '\" . $expectedOption . \"' is required.\";\n\n        if (! empty($hint)) {\n            $message .= ' (Hint: ' . $hint . ')';\n        }\n\n        return new self($message);\n    }\n\n    /**\n     * Generic exception for invalid mappings.\n     */\n    public static function invalidMapping(string $fieldName): self\n    {\n        return new self(sprintf(\"The mapping of field '%s' is invalid.\", $fieldName));\n    }\n\n    /**\n     * Exception for reflection exceptions - adds the entity name,\n     * because there might be long classnames that will be shortened\n     * within the stacktrace\n     *\n     * @param string $entity The entity's name\n     */\n    public static function reflectionFailure(string $entity, ReflectionException $previousException): self\n    {\n        return new self(sprintf('An error occurred in %s', $entity), 0, $previousException);\n    }\n\n    public static function joinColumnMustPointToMappedField(string $className, string $joinColumn): self\n    {\n        return new self(sprintf(\n            'The column %s must be mapped to a field in class %s since it is referenced by a join column of another class.',\n            $joinColumn,\n            $className,\n        ));\n    }\n\n    public static function joinColumnNotAllowedOnOneToOneInverseSide(string $className, string $fieldName): self\n    {\n        return new self(sprintf(\n            '%s#%s is a OneToOne inverse side, which does not allow join columns.',\n            $className,\n            $fieldName,\n        ));\n    }\n\n    /** @param class-string $className */\n    public static function classIsNotAValidEntityOrMappedSuperClass(string $className): self\n    {\n        $parent = get_parent_class($className);\n        if ($parent !== false) {\n            return new self(sprintf(\n                'Class \"%s\" sub class of \"%s\" is not a valid entity or mapped super class.',\n                $className,\n                $parent,\n            ));\n        }\n\n        return new self(sprintf(\n            'Class \"%s\" is not a valid entity or mapped super class.',\n            $className,\n        ));\n    }\n\n    /**\n     * @param string $entity    The entity's name.\n     * @param string $fieldName The name of the field that was already declared.\n     */\n    public static function duplicateFieldMapping(string $entity, string $fieldName): self\n    {\n        return new self(sprintf(\n            'Property \"%s\" in \"%s\" was already declared, but it must be declared only once',\n            $fieldName,\n            $entity,\n        ));\n    }\n\n    public static function duplicateAssociationMapping(string $entity, string $fieldName): self\n    {\n        return new self(sprintf(\n            'Property \"%s\" in \"%s\" was already declared, but it must be declared only once',\n            $fieldName,\n            $entity,\n        ));\n    }\n\n    public static function duplicateQueryMapping(string $entity, string $queryName): self\n    {\n        return new self(sprintf(\n            'Query named \"%s\" in \"%s\" was already declared, but it must be declared only once',\n            $queryName,\n            $entity,\n        ));\n    }\n\n    public static function duplicateResultSetMapping(string $entity, string $resultName): self\n    {\n        return new self(sprintf(\n            'Result set mapping named \"%s\" in \"%s\" was already declared, but it must be declared only once',\n            $resultName,\n            $entity,\n        ));\n    }\n\n    public static function singleIdNotAllowedOnCompositePrimaryKey(string $entity): self\n    {\n        return new self('Single id is not allowed on composite primary key in entity ' . $entity);\n    }\n\n    public static function noIdDefined(string $entity): self\n    {\n        return new self('No ID defined for entity ' . $entity);\n    }\n\n    public static function unsupportedOptimisticLockingType(string $entity, string $fieldName, string $unsupportedType): self\n    {\n        return new self(sprintf(\n            'Locking type \"%s\" (specified in \"%s\", field \"%s\") is not supported by Doctrine.',\n            $unsupportedType,\n            $entity,\n            $fieldName,\n        ));\n    }\n\n    public static function fileMappingDriversRequireConfiguredDirectoryPath(string|null $path = null): self\n    {\n        if (! empty($path)) {\n            $path = '[' . $path . ']';\n        }\n\n        return new self(\n            'File mapping drivers must have a valid directory path, ' .\n            'however the given path ' . $path . ' seems to be incorrect!',\n        );\n    }\n\n    /**\n     * Returns an exception that indicates that discriminator entries used in a discriminator map\n     * does not exist in the backed enum provided by enumType option.\n     *\n     * @param array<int,int|string> $entries     The discriminator entries that could not be found.\n     * @param string                $owningClass The class that declares the discriminator map.\n     * @param string                $enumType    The enum that entries were checked against.\n     */\n    public static function invalidEntriesInDiscriminatorMap(array $entries, string $owningClass, string $enumType): self\n    {\n        return new self(sprintf(\n            \"The entries %s in the discriminator map of class '%s' do not correspond to enum cases of '%s'.\",\n            implode(', ', array_map(static fn ($entry): string => sprintf(\"'%s'\", $entry), $entries)),\n            $owningClass,\n            $enumType,\n        ));\n    }\n\n    /**\n     * Returns an exception that indicates that a class used in a discriminator map does not exist.\n     * An example would be an outdated (maybe renamed) classname.\n     *\n     * @param string $className   The class that could not be found\n     * @param string $owningClass The class that declares the discriminator map.\n     */\n    public static function invalidClassInDiscriminatorMap(string $className, string $owningClass): self\n    {\n        return new self(sprintf(\n            \"Entity class '%s' used in the discriminator map of class '%s' \" .\n            'does not exist.',\n            $className,\n            $owningClass,\n        ));\n    }\n\n    /**\n     * @param string[]             $entries\n     * @param array<string,string> $map\n     */\n    public static function duplicateDiscriminatorEntry(string $className, array $entries, array $map): self\n    {\n        return new self(\n            'The entries ' . implode(', ', $entries) . \" in discriminator map of class '\" . $className . \"' is duplicated. \" .\n            'If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. ' .\n            'The entries of the current map are: @DiscriminatorMap({' . implode(', ', array_map(\n                static fn ($a, $b) => sprintf(\"'%s': '%s'\", $a, $b),\n                array_keys($map),\n                array_values($map),\n            )) . '})',\n        );\n    }\n\n    /**\n     * @param class-string $rootEntityClass\n     * @param class-string $childEntityClass\n     */\n    public static function missingInheritanceTypeDeclaration(string $rootEntityClass, string $childEntityClass): self\n    {\n        return new self(sprintf(\n            \"Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared.\",\n            $childEntityClass,\n            $rootEntityClass,\n        ));\n    }\n\n    public static function missingDiscriminatorMap(string $className): self\n    {\n        return new self(sprintf(\n            \"Entity class '%s' is using inheritance but no discriminator map was defined.\",\n            $className,\n        ));\n    }\n\n    public static function missingDiscriminatorColumn(string $className): self\n    {\n        return new self(sprintf(\n            \"Entity class '%s' is using inheritance but no discriminator column was defined.\",\n            $className,\n        ));\n    }\n\n    public static function invalidDiscriminatorColumnType(string $className, string $type): self\n    {\n        return new self(sprintf(\n            \"Discriminator column type on entity class '%s' is not allowed to be '%s'. 'string' or 'integer' type variables are suggested!\",\n            $className,\n            $type,\n        ));\n    }\n\n    public static function nameIsMandatoryForDiscriminatorColumns(string $className): self\n    {\n        return new self(sprintf(\"Discriminator column name on entity class '%s' is not defined.\", $className));\n    }\n\n    public static function cannotVersionIdField(string $className, string $fieldName): self\n    {\n        return new self(sprintf(\n            \"Setting Id field '%s' as versionable in entity class '%s' is not supported.\",\n            $fieldName,\n            $className,\n        ));\n    }\n\n    public static function duplicateColumnName(string $className, string $columnName): self\n    {\n        return new self(\"Duplicate definition of column '\" . $columnName . \"' on entity '\" . $className . \"' in a field or discriminator column mapping.\");\n    }\n\n    public static function illegalToManyAssociationOnMappedSuperclass(string $className, string $field): self\n    {\n        return new self(\"It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '\" . $className . '#' . $field . \"'.\");\n    }\n\n    public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId(string $className, string $targetEntity, string $targetField): self\n    {\n        return new self(\"It is not possible to map entity '\" . $className . \"' with a composite primary key \" .\n            \"as part of the primary key of another entity '\" . $targetEntity . '#' . $targetField . \"'.\");\n    }\n\n    public static function noSingleAssociationJoinColumnFound(string $className, string $field): self\n    {\n        return new self(sprintf(\"'%s#%s' is not an association with a single join column.\", $className, $field));\n    }\n\n    public static function noFieldNameFoundForColumn(string $className, string $column): self\n    {\n        return new self(sprintf(\n            \"Cannot find a field on '%s' that is mapped to column '%s'. Either the \" .\n            'field does not exist or an association exists but it has multiple join columns.',\n            $className,\n            $column,\n        ));\n    }\n\n    public static function illegalOrphanRemovalOnIdentifierAssociation(string $className, string $field): self\n    {\n        return new self(sprintf(\n            \"The orphan removal option is not allowed on an association that is part of the identifier in '%s#%s'.\",\n            $className,\n            $field,\n        ));\n    }\n\n    public static function illegalOrphanRemoval(string $className, string $field): self\n    {\n        return new self('Orphan removal is only allowed on one-to-one and one-to-many ' .\n            'associations, but ' . $className . '#' . $field . ' is not.');\n    }\n\n    public static function illegalInverseIdentifierAssociation(string $className, string $field): self\n    {\n        return new self(sprintf(\n            \"An inverse association is not allowed to be identifier in '%s#%s'.\",\n            $className,\n            $field,\n        ));\n    }\n\n    public static function illegalToManyIdentifierAssociation(string $className, string $field): self\n    {\n        return new self(sprintf(\n            \"Many-to-many or one-to-many associations are not allowed to be identifier in '%s#%s'.\",\n            $className,\n            $field,\n        ));\n    }\n\n    public static function noInheritanceOnMappedSuperClass(string $className): self\n    {\n        return new self(\"It is not supported to define inheritance information on a mapped superclass '\" . $className . \"'.\");\n    }\n\n    public static function mappedClassNotPartOfDiscriminatorMap(string $className, string $rootClassName): self\n    {\n        return new self(\n            \"Entity '\" . $className . \"' has to be part of the discriminator map of '\" . $rootClassName . \"' \" .\n            \"to be properly mapped in the inheritance hierarchy. Alternatively you can make '\" . $className . \"' an abstract class \" .\n            'to avoid this exception from occurring.',\n        );\n    }\n\n    public static function lifecycleCallbackMethodNotFound(string $className, string $methodName): self\n    {\n        return new self(\"Entity '\" . $className . \"' has no method '\" . $methodName . \"' to be registered as lifecycle callback.\");\n    }\n\n    /** @param class-string $className */\n    public static function illegalLifecycleCallbackOnEmbeddedClass(string $event, string $className): self\n    {\n        return new self(sprintf(\n            <<<'EXCEPTION'\n            Context: Attempt to register lifecycle callback \"%s\" on embedded class \"%s\".\n            Problem: Registering lifecycle callbacks on embedded classes is not allowed.\n            EXCEPTION,\n            $event,\n            $className,\n        ));\n    }\n\n    public static function entityListenerClassNotFound(string $listenerName, string $className): self\n    {\n        return new self(sprintf('Entity Listener \"%s\" declared on \"%s\" not found.', $listenerName, $className));\n    }\n\n    public static function entityListenerMethodNotFound(string $listenerName, string $methodName, string $className): self\n    {\n        return new self(sprintf('Entity Listener \"%s\" declared on \"%s\" has no method \"%s\".', $listenerName, $className, $methodName));\n    }\n\n    public static function duplicateEntityListener(string $listenerName, string $methodName, string $className): self\n    {\n        return new self(sprintf('Entity Listener \"%s#%s()\" in \"%s\" was already declared, but it must be declared only once.', $listenerName, $methodName, $className));\n    }\n\n    /** @param class-string $className */\n    public static function invalidFetchMode(string $className, string $fetchMode): self\n    {\n        return new self(\"Entity '\" . $className . \"' has a mapping with invalid fetch mode '\" . $fetchMode . \"'\");\n    }\n\n    public static function invalidGeneratedMode(int|string $generatedMode): self\n    {\n        return new self(\"Invalid generated mode '\" . $generatedMode . \"'\");\n    }\n\n    public static function compositeKeyAssignedIdGeneratorRequired(string $className): self\n    {\n        return new self(\"Entity '\" . $className . \"' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported.\");\n    }\n\n    public static function invalidTargetEntityClass(string $targetEntity, string $sourceEntity, string $associationName): self\n    {\n        return new self('The target-entity ' . $targetEntity . \" cannot be found in '\" . $sourceEntity . '#' . $associationName . \"'.\");\n    }\n\n    /** @param string[] $cascades */\n    public static function invalidCascadeOption(array $cascades, string $className, string $propertyName): self\n    {\n        $cascades = implode(', ', array_map(static fn (string $e): string => \"'\" . $e . \"'\", $cascades));\n\n        return new self(sprintf(\n            \"You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', and 'detach'\",\n            $className,\n            $propertyName,\n            $cascades,\n        ));\n    }\n\n    public static function missingSequenceName(string $className): self\n    {\n        return new self(\n            sprintf('Missing \"sequenceName\" attribute for sequence id generator definition on class \"%s\".', $className),\n        );\n    }\n\n    public static function infiniteEmbeddableNesting(string $className, string $propertyName): self\n    {\n        return new self(\n            sprintf(\n                'Infinite nesting detected for embedded property %s::%s. ' .\n                'You cannot embed an embeddable from the same type inside an embeddable.',\n                $className,\n                $propertyName,\n            ),\n        );\n    }\n\n    public static function illegalOverrideOfInheritedProperty(string $className, string $propertyName, string $inheritFromClass): self\n    {\n        return new self(\n            sprintf(\n                'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s.',\n                $className,\n                $propertyName,\n                $inheritFromClass,\n            ),\n        );\n    }\n\n    public static function invalidIndexConfiguration(string $className, string $indexName): self\n    {\n        return new self(\n            sprintf(\n                'Index %s for entity %s should contain columns or fields values, but not both.',\n                $indexName,\n                $className,\n            ),\n        );\n    }\n\n    public static function invalidUniqueConstraintConfiguration(string $className, string $indexName): self\n    {\n        return new self(\n            sprintf(\n                'Unique constraint %s for entity %s should contain columns or fields values, but not both.',\n                $indexName,\n                $className,\n            ),\n        );\n    }\n\n    public static function invalidOverrideType(string $expectdType, mixed $givenValue): self\n    {\n        return new self(sprintf(\n            'Expected %s, but %s was given.',\n            $expectdType,\n            get_debug_type($givenValue),\n        ));\n    }\n\n    public static function backedEnumTypeRequired(string $className, string $fieldName, string $enumType): self\n    {\n        return new self(sprintf(\n            'Attempting to map a non-backed enum type %s in entity %s::$%s. Please use backed enums only',\n            $enumType,\n            $className,\n            $fieldName,\n        ));\n    }\n\n    public static function nonEnumTypeMapped(string $className, string $fieldName, string $enumType): self\n    {\n        return new self(sprintf(\n            'Attempting to map non-enum type %s as enum in entity %s::$%s',\n            $enumType,\n            $className,\n            $fieldName,\n        ));\n    }\n\n    /**\n     * @param class-string             $className\n     * @param class-string<BackedEnum> $enumType\n     */\n    public static function invalidEnumValue(\n        string $className,\n        string $fieldName,\n        string $value,\n        string $enumType,\n        ValueError $previous,\n    ): self {\n        return new self(sprintf(\n            <<<'EXCEPTION'\nContext: Trying to hydrate enum property \"%s::$%s\"\nProblem: Case \"%s\" is not listed in enum \"%s\"\nSolution: Either add the case to the enum type or migrate the database column to use another case of the enum\nEXCEPTION\n            ,\n            $className,\n            $fieldName,\n            $value,\n            $enumType,\n        ), 0, $previous);\n    }\n\n    /** @param LibXMLError[] $errors */\n    public static function fromLibXmlErrors(array $errors): self\n    {\n        $formatter = static fn (LibXMLError $error): string => sprintf(\n            'libxml error: %s in %s at line %d',\n            $error->message,\n            $error->file,\n            $error->line,\n        );\n\n        return new self(implode(PHP_EOL, array_map($formatter, $errors)));\n    }\n\n    public static function invalidAttributeOnEmbeddable(string $entityName, string $attributeName): self\n    {\n        return new self(sprintf(\n            'Attribute \"%s\" on embeddable \"%s\" is not allowed.',\n            $attributeName,\n            $entityName,\n        ));\n    }\n\n    public static function mappingVirtualPropertyNotAllowed(string $entityName, string $propertyName): self\n    {\n        return new self(sprintf(\n            'Mapping virtual property \"%s\" on entity \"%s\" is not allowed.',\n            $propertyName,\n            $entityName,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Mapping/NamingStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\n/**\n * A set of rules for determining the physical column and table names\n *\n * @link    www.doctrine-project.org\n */\ninterface NamingStrategy\n{\n    /**\n     * Returns a table name for an entity class.\n     *\n     * @param class-string $className\n     */\n    public function classToTableName(string $className): string;\n\n    /**\n     * Returns a column name for a property.\n     *\n     * @param class-string $className\n     */\n    public function propertyToColumnName(string $propertyName, string $className): string;\n\n    /**\n     * Returns a column name for an embedded property.\n     *\n     * @param class-string $className\n     * @param class-string $embeddedClassName\n     */\n    public function embeddedFieldToColumnName(\n        string $propertyName,\n        string $embeddedColumnName,\n        string $className,\n        string $embeddedClassName,\n    ): string;\n\n    /**\n     * Returns the default reference column name.\n     */\n    public function referenceColumnName(): string;\n\n    /**\n     * Returns a join column name for a property.\n     *\n     * @param class-string $className\n     */\n    public function joinColumnName(string $propertyName, string $className): string;\n\n    /**\n     * Returns a join table name.\n     *\n     * @param class-string $sourceEntity\n     * @param class-string $targetEntity\n     */\n    public function joinTableName(string $sourceEntity, string $targetEntity, string $propertyName): string;\n\n    /**\n     * Returns the foreign key column name for the given parameters.\n     *\n     * @param class-string $entityName           An entity.\n     * @param string|null  $referencedColumnName A property name or null in\n     *                                           case of a self-referencing\n     *                                           entity with join columns\n     *                                           defined in the mapping\n     */\n    public function joinKeyColumnName(string $entityName, string|null $referencedColumnName): string;\n}\n"
  },
  {
    "path": "src/Mapping/OneToMany.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class OneToMany implements MappingAttribute\n{\n    /**\n     * @param class-string|null $targetEntity\n     * @param string[]|null     $cascade\n     * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch\n     */\n    public function __construct(\n        public readonly string|null $targetEntity = null,\n        public readonly string|null $mappedBy = null,\n        public readonly array|null $cascade = null,\n        public readonly string $fetch = 'LAZY',\n        public readonly bool $orphanRemoval = false,\n        public readonly string|null $indexBy = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/OneToManyAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nfinal class OneToManyAssociationMapping extends ToManyInverseSideMapping\n{\n    /**\n     * @param mixed[] $mappingArray\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): static\n    {\n        $mapping = parent::fromMappingArray($mappingArray);\n\n        if ($mapping->orphanRemoval && ! $mapping->isCascadeRemove()) {\n            $mapping->cascade[] = 'remove';\n        }\n\n        return $mapping;\n    }\n\n    /**\n     * @param mixed[] $mappingArray\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     * } $mappingArray\n     */\n    public static function fromMappingArrayAndName(array $mappingArray, string $name): static\n    {\n        $mapping = self::fromMappingArray($mappingArray);\n\n        // OneToMany-side MUST be inverse (must have mappedBy)\n        if (! isset($mapping->mappedBy)) {\n            throw MappingException::oneToManyRequiresMappedBy($name, $mapping->fieldName);\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/OneToOne.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class OneToOne implements MappingAttribute\n{\n    /**\n     * @param class-string|null  $targetEntity\n     * @param array<string>|null $cascade\n     * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch\n     */\n    public function __construct(\n        public readonly string|null $targetEntity = null,\n        public readonly string|null $mappedBy = null,\n        public readonly string|null $inversedBy = null,\n        public readonly array|null $cascade = null,\n        public readonly string $fetch = 'LAZY',\n        public readonly bool $orphanRemoval = false,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/OneToOneAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\ninterface OneToOneAssociationMapping extends ToOneAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/OneToOneInverseSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nfinal class OneToOneInverseSideMapping extends ToOneInverseSideMapping implements OneToOneAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/OneToOneOwningSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nfinal class OneToOneOwningSideMapping extends ToOneOwningSideMapping implements OneToOneAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/OrderBy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class OrderBy implements MappingAttribute\n{\n    /** @param array<string> $value */\n    public function __construct(\n        public readonly array $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/OwningSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nabstract class OwningSideMapping extends AssociationMapping\n{\n    /**\n     * required for bidirectional associations\n     * The name of the field that completes the bidirectional association on\n     * the inverse side. This key must be specified on the owning side of a\n     * bidirectional association.\n     */\n    public string|null $inversedBy = null;\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = parent::__sleep();\n\n        if ($this->inversedBy !== null) {\n            $serialized[] = 'inversedBy';\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PostLoad.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PostLoad implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PostPersist.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PostPersist implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PostRemove.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PostRemove implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PostUpdate.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PostUpdate implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PreFlush.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PreFlush implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PrePersist.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PrePersist implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PreRemove.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PreRemove implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PreUpdate.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nfinal class PreUpdate implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/EmbeddablePropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\Instantiator\\Instantiator;\nuse ReflectionProperty;\n\n/** @internal */\nclass EmbeddablePropertyAccessor implements PropertyAccessor\n{\n    private static Instantiator|null $instantiator = null;\n\n    public function __construct(\n        private PropertyAccessor $parent,\n        private PropertyAccessor $child,\n        /** @var class-string */\n        private string $embeddedClass,\n    ) {\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        $embeddedObject = $this->parent->getValue($object);\n\n        if ($embeddedObject === null) {\n            self::$instantiator ??= new Instantiator();\n\n            $embeddedObject = self::$instantiator->instantiate($this->embeddedClass);\n\n            $this->parent->setValue($object, $embeddedObject);\n        }\n\n        $this->child->setValue($embeddedObject, $value);\n    }\n\n    public function getValue(object $object): mixed\n    {\n        $embeddedObject = $this->parent->getValue($object);\n\n        if ($embeddedObject === null) {\n            return null;\n        }\n\n        return $this->child->getValue($embeddedObject);\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->child->getUnderlyingReflector();\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/EnumPropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse BackedEnum;\nuse ReflectionProperty;\n\nuse function array_map;\nuse function is_array;\nuse function reset;\n\n/** @internal */\nclass EnumPropertyAccessor implements PropertyAccessor\n{\n    /** @param class-string<BackedEnum> $enumType */\n    public function __construct(private PropertyAccessor $parent, private string $enumType)\n    {\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        if ($value !== null) {\n            $value = $this->toEnum($value);\n        }\n\n        $this->parent->setValue($object, $value);\n    }\n\n    public function getValue(object $object): mixed\n    {\n        $enum = $this->parent->getValue($object);\n\n        if ($enum === null) {\n            return null;\n        }\n\n        return $this->fromEnum($enum);\n    }\n\n    /**\n     * @param BackedEnum|BackedEnum[] $enum\n     *\n     * @return ($enum is BackedEnum ? (string|int) : (string[]|int[]))\n     */\n    private function fromEnum($enum) // phpcs:ignore\n    {\n        if (is_array($enum)) {\n            return array_map(static function (BackedEnum $enum) {\n                return $enum->value;\n            }, $enum);\n        }\n\n        return $enum->value;\n    }\n\n    /**\n     * @phpstan-param BackedEnum|BackedEnum[]|int|string|int[]|string[] $value\n     *\n     * @return ($value is int|string|BackedEnum ? BackedEnum : BackedEnum[])\n     */\n    private function toEnum($value): BackedEnum|array\n    {\n        if ($value instanceof BackedEnum) {\n            return $value;\n        }\n\n        if (is_array($value)) {\n            $v = reset($value);\n            if ($v instanceof BackedEnum) {\n                return $value;\n            }\n\n            return array_map([$this->enumType, 'from'], $value);\n        }\n\n        return $this->enumType::from($value);\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->parent->getUnderlyingReflector();\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/ObjectCastPropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Proxy\\InternalProxy;\nuse ReflectionProperty;\n\nuse function ltrim;\n\n/** @internal */\nclass ObjectCastPropertyAccessor implements PropertyAccessor\n{\n    /** @param class-string $class */\n    public static function fromNames(string $class, string $name): self\n    {\n        $reflectionProperty = new ReflectionProperty($class, $name);\n\n        $key = $reflectionProperty->isPrivate() ? \"\\0\" . ltrim($class, '\\\\') . \"\\0\" . $name : ($reflectionProperty->isProtected() ? \"\\0*\\0\" . $name : $name);\n\n        return new self($reflectionProperty, $key);\n    }\n\n    public static function fromReflectionProperty(ReflectionProperty $reflectionProperty): self\n    {\n        $name = $reflectionProperty->getName();\n        $key  = $reflectionProperty->isPrivate() ? \"\\0\" . ltrim($reflectionProperty->getDeclaringClass()->getName(), '\\\\') . \"\\0\" . $name : ($reflectionProperty->isProtected() ? \"\\0*\\0\" . $name : $name);\n\n        return new self($reflectionProperty, $key);\n    }\n\n    private function __construct(private ReflectionProperty $reflectionProperty, private string $key)\n    {\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        if (! ($object instanceof InternalProxy && ! $object->__isInitialized())) {\n            $this->reflectionProperty->setValue($object, $value);\n\n            return;\n        }\n\n        $object->__setInitialized(true);\n\n        $this->reflectionProperty->setValue($object, $value);\n\n        $object->__setInitialized(false);\n    }\n\n    public function getValue(object $object): mixed\n    {\n        return ((array) $object)[$this->key] ?? null;\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->reflectionProperty;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/PropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse ReflectionProperty;\n\n/**\n * A property accessor is a class that allows to read and write properties on objects regardless of visibility.\n *\n * We use them while creating objects from database rows in {@link UnitOfWork::createEntity()} or when\n * computing changesets from objects that are about to be written back to the database in {@link UnitOfWork::computeChangeSet()}.\n *\n * This abstraction over ReflectionProperty is necessary, because for several features of either Doctrine or PHP, we\n * need to handle edge cases in reflection at a central location in the code.\n *\n * @internal\n */\ninterface PropertyAccessor\n{\n    public function setValue(object $object, mixed $value): void;\n\n    public function getValue(object $object): mixed;\n\n    public function getUnderlyingReflector(): ReflectionProperty;\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/PropertyAccessorFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse ReflectionProperty;\n\nuse const PHP_VERSION_ID;\n\nclass PropertyAccessorFactory\n{\n    /** @phpstan-param class-string $className */\n    public static function createPropertyAccessor(string $className, string $propertyName): PropertyAccessor\n    {\n        $reflectionProperty = new ReflectionProperty($className, $propertyName);\n\n        $accessor = PHP_VERSION_ID >= 80400\n            ? RawValuePropertyAccessor::fromReflectionProperty($reflectionProperty)\n            : ObjectCastPropertyAccessor::fromReflectionProperty($reflectionProperty);\n\n        if ($reflectionProperty->hasType() && ! $reflectionProperty->getType()->allowsNull()) {\n            $accessor = new TypedNoDefaultPropertyAccessor($accessor, $reflectionProperty);\n        }\n\n        if ($reflectionProperty->isReadOnly()) {\n            $accessor = new ReadonlyAccessor($accessor, $reflectionProperty);\n        }\n\n        return $accessor;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/RawValuePropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Proxy\\InternalProxy;\nuse LogicException;\nuse ReflectionProperty;\n\nuse function ltrim;\n\nuse const PHP_VERSION_ID;\n\n/**\n * This is a PHP 8.4 and up only class and replaces ObjectCastPropertyAccessor.\n *\n * It works based on the raw values of a property, which for a case of property hooks\n * is the backed value. If we kept using setValue/getValue, this would go through the hooks,\n * which potentially change the data.\n */\nclass RawValuePropertyAccessor implements PropertyAccessor\n{\n    public static function fromReflectionProperty(ReflectionProperty $reflectionProperty): self\n    {\n        $name = $reflectionProperty->getName();\n        $key  = $reflectionProperty->isPrivate() ? \"\\0\" . ltrim($reflectionProperty->getDeclaringClass()->getName(), '\\\\') . \"\\0\" . $name : ($reflectionProperty->isProtected() ? \"\\0*\\0\" . $name : $name);\n\n        return new self($reflectionProperty, $key);\n    }\n\n    private function __construct(private ReflectionProperty $reflectionProperty, private string $key)\n    {\n        if (PHP_VERSION_ID < 80400) {\n            throw new LogicException('This class requires PHP 8.4 or higher.');\n        }\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        if (! ($object instanceof InternalProxy && ! $object->__isInitialized())) {\n            $this->reflectionProperty->setRawValueWithoutLazyInitialization($object, $value);\n\n            return;\n        }\n\n        $object->__setInitialized(true);\n\n        $this->reflectionProperty->setRawValue($object, $value);\n\n        $object->__setInitialized(false);\n    }\n\n    public function getValue(object $object): mixed\n    {\n        return ((array) $object)[$this->key] ?? null;\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->reflectionProperty;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/ReadonlyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse InvalidArgumentException;\nuse LogicException;\nuse ReflectionProperty;\n\nuse function sprintf;\n\nuse const PHP_VERSION_ID;\n\n/** @internal */\nclass ReadonlyAccessor implements PropertyAccessor\n{\n    public function __construct(private PropertyAccessor $parent, private ReflectionProperty $reflectionProperty)\n    {\n        if (! $this->reflectionProperty->isReadOnly()) {\n            throw new InvalidArgumentException(sprintf(\n                '%s::$%s must be readonly property',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        /* For lazy properties, skip the isInitialized() check\n           because it would trigger the initialization of the whole object. */\n        if (\n            PHP_VERSION_ID >= 80400 && $this->reflectionProperty->isLazy($object)\n            || ! $this->reflectionProperty->isInitialized($object)\n        ) {\n            $this->parent->setValue($object, $value);\n\n            return;\n        }\n\n        if ($this->parent->getValue($object) !== $value) {\n            throw new LogicException(sprintf(\n                'Attempting to change readonly property %s::$%s.',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n    }\n\n    public function getValue(object $object): mixed\n    {\n        return $this->parent->getValue($object);\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->reflectionProperty;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse Closure;\nuse InvalidArgumentException;\nuse ReflectionProperty;\n\nuse function assert;\nuse function sprintf;\n\n/** @internal */\nclass TypedNoDefaultPropertyAccessor implements PropertyAccessor\n{\n    private Closure|null $unsetter = null;\n\n    public function __construct(private PropertyAccessor $parent, private ReflectionProperty $reflectionProperty)\n    {\n        if (! $this->reflectionProperty->hasType()) {\n            throw new InvalidArgumentException(sprintf(\n                '%s::$%s must have a type when used with TypedNoDefaultPropertyAccessor',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n\n        if ($this->reflectionProperty->getType()->allowsNull()) {\n            throw new InvalidArgumentException(sprintf(\n                '%s::$%s must not be nullable when used with TypedNoDefaultPropertyAccessor',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        if ($value === null) {\n            if ($this->unsetter === null) {\n                $propertyName   = $this->reflectionProperty->getName();\n                $this->unsetter = function () use ($propertyName): void {\n                    unset($this->$propertyName);\n                };\n            }\n\n            $unsetter = $this->unsetter->bindTo($object, $this->reflectionProperty->getDeclaringClass()->getName());\n\n            assert($unsetter instanceof Closure);\n\n            $unsetter();\n\n            return;\n        }\n\n        $this->parent->setValue($object, $value);\n    }\n\n    public function getValue(object $object): mixed\n    {\n        return $this->reflectionProperty->isInitialized($object) ? $this->parent->getValue($object) : null;\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->reflectionProperty;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/QuoteStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\n\n/**\n * A set of rules for determining the column, alias and table quotes.\n */\ninterface QuoteStrategy\n{\n    /**\n     * Gets the (possibly quoted) column name for safe use in an SQL statement.\n     */\n    public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string;\n\n    /**\n     * Gets the (possibly quoted) primary table name for safe use in an SQL statement.\n     */\n    public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string;\n\n    /**\n     * Gets the (possibly quoted) sequence name for safe use in an SQL statement.\n     *\n     * @param mixed[] $definition\n     */\n    public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string;\n\n    /** Gets the (possibly quoted) name of the join table. */\n    public function getJoinTableName(\n        ManyToManyOwningSideMapping $association,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string;\n\n    /**\n     * Gets the (possibly quoted) join column name.\n     */\n    public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string;\n\n    /**\n     * Gets the (possibly quoted) join column name.\n     */\n    public function getReferencedJoinColumnName(\n        JoinColumnMapping $joinColumn,\n        ClassMetadata $class,\n        AbstractPlatform $platform,\n    ): string;\n\n    /**\n     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.\n     *\n     * @phpstan-return list<string>\n     */\n    public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array;\n\n    /**\n     * Gets the column alias.\n     */\n    public function getColumnAlias(\n        string $columnName,\n        int $counter,\n        AbstractPlatform $platform,\n        ClassMetadata|null $class = null,\n    ): string;\n}\n"
  },
  {
    "path": "src/Mapping/ReflectionEmbeddedProperty.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Instantiator\\Instantiator;\nuse ReflectionProperty;\n\n/**\n * Acts as a proxy to a nested Property structure, making it look like\n * just a single scalar property.\n *\n * This way value objects \"just work\" without UnitOfWork, Persisters or Hydrators\n * needing any changes.\n *\n * TODO: Move this class into Common\\Reflection\n */\nfinal class ReflectionEmbeddedProperty extends ReflectionProperty\n{\n    private Instantiator|null $instantiator = null;\n\n    /**\n     * @param ReflectionProperty $parentProperty reflection property of the class where the embedded object has to be put\n     * @param ReflectionProperty $childProperty  reflection property of the embedded object\n     * @phpstan-param class-string $embeddedClass\n     */\n    public function __construct(\n        private readonly ReflectionProperty $parentProperty,\n        private readonly ReflectionProperty $childProperty,\n        private readonly string $embeddedClass,\n    ) {\n        parent::__construct($childProperty->getDeclaringClass()->name, $childProperty->getName());\n    }\n\n    public function getValue(object|null $object = null): mixed\n    {\n        $embeddedObject = $this->parentProperty->getValue($object);\n\n        if ($embeddedObject === null) {\n            return null;\n        }\n\n        return $this->childProperty->getValue($embeddedObject);\n    }\n\n    public function setValue(mixed $object, mixed $value = null): void\n    {\n        $embeddedObject = $this->parentProperty->getValue($object);\n\n        if ($embeddedObject === null) {\n            $this->instantiator ??= new Instantiator();\n\n            $embeddedObject = $this->instantiator->instantiate($this->embeddedClass);\n\n            $this->parentProperty->setValue($object, $embeddedObject);\n        }\n\n        $this->childProperty->setValue($embeddedObject, $value);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ReflectionEnumProperty.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse BackedEnum;\nuse ReflectionProperty;\nuse ValueError;\n\nuse function array_map;\nuse function is_array;\n\n/** @deprecated use Doctrine\\Persistence\\Reflection\\EnumReflectionProperty instead */\nfinal class ReflectionEnumProperty extends ReflectionProperty\n{\n    /** @param class-string<BackedEnum> $enumType */\n    public function __construct(\n        private readonly ReflectionProperty $originalReflectionProperty,\n        private readonly string $enumType,\n    ) {\n        parent::__construct(\n            $originalReflectionProperty->class,\n            $originalReflectionProperty->name,\n        );\n    }\n\n    public function getValue(object|null $object = null): int|string|array|null\n    {\n        if ($object === null) {\n            return null;\n        }\n\n        $enum = $this->originalReflectionProperty->getValue($object);\n\n        if ($enum === null) {\n            return null;\n        }\n\n        if (is_array($enum)) {\n            return array_map(\n                static fn (BackedEnum $item): int|string => $item->value,\n                $enum,\n            );\n        }\n\n        return $enum->value;\n    }\n\n    /**\n     * @param object                                                 $object\n     * @param int|string|int[]|string[]|BackedEnum|BackedEnum[]|null $value\n     */\n    public function setValue(mixed $object, mixed $value = null): void\n    {\n        if ($value !== null) {\n            if (is_array($value)) {\n                $value = array_map(fn (int|string|BackedEnum $item): BackedEnum => $this->initializeEnumValue($object, $item), $value);\n            } else {\n                $value = $this->initializeEnumValue($object, $value);\n            }\n        }\n\n        $this->originalReflectionProperty->setValue($object, $value);\n    }\n\n    private function initializeEnumValue(object $object, int|string|BackedEnum $value): BackedEnum\n    {\n        if ($value instanceof BackedEnum) {\n            return $value;\n        }\n\n        $enumType = $this->enumType;\n\n        try {\n            return $enumType::from($value);\n        } catch (ValueError $e) {\n            throw MappingException::invalidEnumValue(\n                $object::class,\n                $this->originalReflectionProperty->name,\n                (string) $value,\n                $enumType,\n                $e,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ReflectionReadonlyProperty.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse InvalidArgumentException;\nuse LogicException;\nuse ReflectionProperty;\n\nuse function assert;\nuse function func_get_args;\nuse function func_num_args;\nuse function is_object;\nuse function sprintf;\n\n/** @internal */\nfinal class ReflectionReadonlyProperty extends ReflectionProperty\n{\n    public function __construct(\n        private readonly ReflectionProperty $wrappedProperty,\n    ) {\n        if (! $wrappedProperty->isReadOnly()) {\n            throw new InvalidArgumentException('Given property is not readonly.');\n        }\n\n        parent::__construct($wrappedProperty->class, $wrappedProperty->name);\n    }\n\n    public function getValue(object|null $object = null): mixed\n    {\n        return $this->wrappedProperty->getValue(...func_get_args());\n    }\n\n    public function setValue(mixed $objectOrValue, mixed $value = null): void\n    {\n        if (func_num_args() < 2 || $objectOrValue === null || ! $this->isInitialized($objectOrValue)) {\n            $this->wrappedProperty->setValue(...func_get_args());\n\n            return;\n        }\n\n        assert(is_object($objectOrValue));\n\n        if (parent::getValue($objectOrValue) !== $value) {\n            throw new LogicException(sprintf('Attempting to change readonly property %s::$%s.', $this->class, $this->name));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/SequenceGenerator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class SequenceGenerator implements MappingAttribute\n{\n    public function __construct(\n        public readonly string|null $sequenceName = null,\n        public readonly int $allocationSize = 1,\n        public readonly int $initialValue = 1,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Table.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\nuse Doctrine\\Deprecations\\Deprecation;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nfinal class Table implements MappingAttribute\n{\n    /**\n     * @param array<Index>|null            $indexes\n     * @param array<UniqueConstraint>|null $uniqueConstraints\n     * @param array<string,mixed>          $options\n     */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly string|null $schema = null,\n        public readonly array|null $indexes = null,\n        public readonly array|null $uniqueConstraints = null,\n        public readonly array $options = [],\n    ) {\n        if ($this->indexes !== null) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/11357',\n                'Providing the property $indexes on %s does not have any effect and will be removed in Doctrine ORM 4.0. Please use the %s attribute instead.',\n                self::class,\n                Index::class,\n            );\n        }\n\n        if ($this->uniqueConstraints !== null) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/11357',\n                'Providing the property $uniqueConstraints on %s does not have any effect and will be removed in Doctrine ORM 4.0. Please use the %s attribute instead.',\n                self::class,\n                UniqueConstraint::class,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ToManyAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\ninterface ToManyAssociationMapping\n{\n    /** @phpstan-assert-if-true string $this->indexBy() */\n    public function isIndexed(): bool;\n\n    public function indexBy(): string;\n\n    /** @return array<string, 'asc'|'desc'> */\n    public function orderBy(): array;\n}\n"
  },
  {
    "path": "src/Mapping/ToManyAssociationMappingImplementation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse LogicException;\n\nuse function sprintf;\n\n/** @internal */\ntrait ToManyAssociationMappingImplementation\n{\n    /**\n     * Specification of a field on target-entity that is used to index the\n     * collection by. This field HAS to be either the primary key or a unique\n     * column. Otherwise the collection does not contain all the entities that\n     * are actually related.\n     */\n    public string|null $indexBy = null;\n\n    /**\n     * A map of field names (of the target entity) to sorting directions\n     *\n     * @var array<string, 'asc'|'desc'>\n     */\n    public array $orderBy = [];\n\n    /** @return array<string, 'asc'|'desc'> */\n    final public function orderBy(): array\n    {\n        return $this->orderBy;\n    }\n\n    /** @phpstan-assert-if-true !null $this->indexBy */\n    final public function isIndexed(): bool\n    {\n        return $this->indexBy !== null;\n    }\n\n    final public function indexBy(): string\n    {\n        if (! $this->isIndexed()) {\n            throw new LogicException(sprintf(\n                'This mapping is not indexed. Use %s::isIndexed() to check that before calling %s.',\n                self::class,\n                __METHOD__,\n            ));\n        }\n\n        return $this->indexBy;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        $serialized = parent::__sleep();\n\n        if ($this->indexBy !== null) {\n            $serialized[] = 'indexBy';\n        }\n\n        if ($this->orderBy !== []) {\n            $serialized[] = 'orderBy';\n        }\n\n        return $serialized;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ToManyInverseSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nabstract class ToManyInverseSideMapping extends InverseSideMapping implements ToManyAssociationMapping\n{\n    use ToManyAssociationMappingImplementation;\n}\n"
  },
  {
    "path": "src/Mapping/ToManyOwningSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nabstract class ToManyOwningSideMapping extends OwningSideMapping\n{\n    use ToManyAssociationMappingImplementation;\n}\n"
  },
  {
    "path": "src/Mapping/ToOneAssociationMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\ninterface ToOneAssociationMapping\n{\n}\n"
  },
  {
    "path": "src/Mapping/ToOneInverseSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nabstract class ToOneInverseSideMapping extends InverseSideMapping\n{\n    /**\n     * @param mixed[]      $mappingArray\n     * @param class-string $name\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     * } $mappingArray\n     */\n    public static function fromMappingArrayAndName(\n        array $mappingArray,\n        string $name,\n    ): static {\n        $mapping = static::fromMappingArray($mappingArray);\n\n        if (isset($mapping->id) && $mapping->id === true) {\n            throw MappingException::illegalInverseIdentifierAssociation($name, $mapping->fieldName);\n        }\n\n        if ($mapping->orphanRemoval) {\n            if (! $mapping->isCascadeRemove()) {\n                $mapping->cascade[] = 'remove';\n            }\n\n            $mapping->unique = null;\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Mapping/ToOneOwningSideMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse RuntimeException;\n\nuse function array_flip;\nuse function assert;\nuse function count;\nuse function trim;\n\nabstract class ToOneOwningSideMapping extends OwningSideMapping implements ToOneAssociationMapping\n{\n    /** @var array<string, string> */\n    public array $sourceToTargetKeyColumns = [];\n\n    /** @var array<string, string> */\n    public array $targetToSourceKeyColumns = [];\n\n    /** @var list<JoinColumnMapping> */\n    public array $joinColumns = [];\n\n    /** @var array<string, string> */\n    public array $joinColumnFieldNames = [];\n\n    /**\n     * @param array<string, mixed> $mappingArray\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     *     joinColumns?: mixed[]|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArray(array $mappingArray): static\n    {\n        $joinColumns = $mappingArray['joinColumns'] ?? [];\n        unset($mappingArray['joinColumns']);\n\n        $instance = parent::fromMappingArray($mappingArray);\n        assert($instance->isToOneOwningSide());\n\n        foreach ($joinColumns as $column) {\n            $instance->joinColumns[] = JoinColumnMapping::fromMappingArray($column);\n        }\n\n        if ($instance->orphanRemoval) {\n            if (! $instance->isCascadeRemove()) {\n                $instance->cascade[] = 'remove';\n            }\n\n            $instance->unique = null;\n        }\n\n        return $instance;\n    }\n\n    /**\n     * @param mixed[]      $mappingArray\n     * @param class-string $name\n     * @phpstan-param array{\n     *     fieldName: string,\n     *     sourceEntity: class-string,\n     *     targetEntity: class-string,\n     *     cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,\n     *     fetch?: ClassMetadata::FETCH_*|null,\n     *     inherited?: class-string|null,\n     *     declared?: class-string|null,\n     *     cache?: array<mixed>|null,\n     *     id?: bool|null,\n     *     isOnDeleteCascade?: bool|null,\n     *     originalClass?: class-string|null,\n     *     originalField?: string|null,\n     *     orphanRemoval?: bool,\n     *     unique?: bool|null,\n     *     joinTable?: mixed[]|null,\n     *     type?: int,\n     *     isOwningSide: bool,\n     *     joinColumns?: mixed[]|null,\n     * } $mappingArray\n     */\n    public static function fromMappingArrayAndName(\n        array $mappingArray,\n        NamingStrategy $namingStrategy,\n        string $name,\n        array|null $table,\n        bool $isInheritanceTypeSingleTable,\n    ): static {\n        if (isset($mappingArray['joinColumns'])) {\n            foreach ($mappingArray['joinColumns'] as $index => $joinColumn) {\n                if (empty($joinColumn['name'])) {\n                    $mappingArray['joinColumns'][$index]['name'] = $namingStrategy->joinColumnName($mappingArray['fieldName'], $name);\n                }\n\n                if (empty($joinColumn['referencedColumnName'])) {\n                    $mappingArray['joinColumns'][$index]['referencedColumnName'] = $namingStrategy->referenceColumnName();\n                }\n            }\n        }\n\n        $mapping = static::fromMappingArray($mappingArray);\n\n        assert($mapping->isToOneOwningSide());\n        if (empty($mapping->joinColumns)) {\n            // Apply default join column\n            $mapping->joinColumns = [\n                JoinColumnMapping::fromMappingArray([\n                    'name' => $namingStrategy->joinColumnName($mapping->fieldName, $name),\n                    'referencedColumnName' => $namingStrategy->referenceColumnName(),\n                ]),\n            ];\n        }\n\n        $uniqueConstraintColumns = [];\n\n        foreach ($mapping->joinColumns as $joinColumn) {\n            if ($mapping->id) {\n                if ($joinColumn->nullable !== null) {\n                    Deprecation::trigger(\n                        'doctrine/orm',\n                        'https://github.com/doctrine/orm/pull/12126',\n                        <<<'DEPRECATION'\n                        Specifying the \"nullable\" attribute for join columns in to-one associations (here, %s::$%s) that are part of the identifier is a no-op.\n                        The ORM will always set it to false.\n                        Doing so is deprecated and will be an error in 4.0.\n                        DEPRECATION,\n                        $mapping->sourceEntity,\n                        $mapping->fieldName,\n                    );\n                }\n\n                $joinColumn->nullable = false;\n            } elseif ($joinColumn->nullable === null) {\n                $joinColumn->nullable = true;\n            }\n\n            if ($mapping->isOneToOne() && ! $isInheritanceTypeSingleTable) {\n                if (count($mapping->joinColumns) === 1) {\n                    if (empty($mapping->id)) {\n                        $joinColumn->unique = true;\n                    }\n                } else {\n                    $uniqueConstraintColumns[] = $joinColumn->name;\n                }\n            }\n\n            if (empty($joinColumn->referencedColumnName)) {\n                $joinColumn->referencedColumnName = $namingStrategy->referenceColumnName();\n            }\n\n            if ($joinColumn->name[0] === '`') {\n                $joinColumn->name   = trim($joinColumn->name, '`');\n                $joinColumn->quoted = true;\n            }\n\n            if ($joinColumn->referencedColumnName[0] === '`') {\n                $joinColumn->referencedColumnName = trim($joinColumn->referencedColumnName, '`');\n                $joinColumn->quoted               = true;\n            }\n\n            $mapping->sourceToTargetKeyColumns[$joinColumn->name] = $joinColumn->referencedColumnName;\n            $mapping->joinColumnFieldNames[$joinColumn->name]     = $joinColumn->fieldName ?? $joinColumn->name;\n        }\n\n        if ($uniqueConstraintColumns) {\n            if (! $table) {\n                throw new RuntimeException('ClassMetadata::setTable() has to be called before defining a one to one relationship.');\n            }\n\n            $table['uniqueConstraints'][$mapping->fieldName . '_uniq'] = ['columns' => $uniqueConstraintColumns];\n        }\n\n        $mapping->targetToSourceKeyColumns = array_flip($mapping->sourceToTargetKeyColumns);\n\n        return $mapping;\n    }\n\n    public function offsetSet(mixed $offset, mixed $value): void\n    {\n        if ($offset === 'joinColumns') {\n            $joinColumns = [];\n            foreach ($value as $column) {\n                $joinColumns[] = JoinColumnMapping::fromMappingArray($column);\n            }\n\n            $this->joinColumns = $joinColumns;\n\n            return;\n        }\n\n        parent::offsetSet($offset, $value);\n    }\n\n    /** @return array<string, mixed> */\n    public function toArray(): array\n    {\n        $array = parent::toArray();\n\n        $joinColumns = [];\n        foreach ($array['joinColumns'] as $column) {\n            $columnArray = (array) $column;\n            if ($this->id) {\n                unset($columnArray['nullable']);\n            }\n\n            $joinColumns[] = $columnArray;\n        }\n\n        $array['joinColumns'] = $joinColumns;\n\n        return $array;\n    }\n\n    /** @return list<string> */\n    public function __sleep(): array\n    {\n        return [\n            ...parent::__sleep(),\n            'joinColumns',\n            'joinColumnFieldNames',\n            'sourceToTargetKeyColumns',\n            'targetToSourceKeyColumns',\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Mapping/TypedFieldMapper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse BackedEnum;\nuse ReflectionProperty;\n\ninterface TypedFieldMapper\n{\n    /**\n     * Validates & completes the given field mapping based on typed property.\n     *\n     * @param array{fieldName: string, enumType?: class-string<BackedEnum>, type?: string} $mapping The field mapping to validate & complete.\n     *\n     * @return array{fieldName: string, enumType?: class-string<BackedEnum>, type?: string} The updated mapping.\n     */\n    public function validateAndComplete(array $mapping, ReflectionProperty $field): array;\n}\n"
  },
  {
    "path": "src/Mapping/UnderscoreNamingStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse function preg_replace;\nuse function str_contains;\nuse function strrpos;\nuse function strtolower;\nuse function strtoupper;\nuse function substr;\n\nuse const CASE_LOWER;\nuse const CASE_UPPER;\n\n/**\n * Naming strategy implementing the underscore naming convention.\n * Converts 'MyEntity' to 'my_entity' or 'MY_ENTITY'.\n *\n * @link    www.doctrine-project.org\n */\nclass UnderscoreNamingStrategy implements NamingStrategy\n{\n    /**\n     * Underscore naming strategy construct.\n     *\n     * @param int $case CASE_LOWER | CASE_UPPER\n     */\n    public function __construct(private int $case = CASE_LOWER)\n    {\n    }\n\n    /** @return int CASE_LOWER | CASE_UPPER */\n    public function getCase(): int\n    {\n        return $this->case;\n    }\n\n    /**\n     * Sets string case CASE_LOWER | CASE_UPPER.\n     * Alphabetic characters converted to lowercase or uppercase.\n     */\n    public function setCase(int $case): void\n    {\n        $this->case = $case;\n    }\n\n    public function classToTableName(string $className): string\n    {\n        if (str_contains($className, '\\\\')) {\n            $className = substr($className, strrpos($className, '\\\\') + 1);\n        }\n\n        return $this->underscore($className);\n    }\n\n    public function propertyToColumnName(string $propertyName, string $className): string\n    {\n        return $this->underscore($propertyName);\n    }\n\n    public function embeddedFieldToColumnName(\n        string $propertyName,\n        string $embeddedColumnName,\n        string $className,\n        string $embeddedClassName,\n    ): string {\n        return $this->underscore($propertyName) . '_' . $embeddedColumnName;\n    }\n\n    public function referenceColumnName(): string\n    {\n        return $this->case === CASE_UPPER ?  'ID' : 'id';\n    }\n\n    public function joinColumnName(string $propertyName, string $className): string\n    {\n        return $this->underscore($propertyName) . '_' . $this->referenceColumnName();\n    }\n\n    public function joinTableName(\n        string $sourceEntity,\n        string $targetEntity,\n        string $propertyName,\n    ): string {\n        return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity);\n    }\n\n    public function joinKeyColumnName(\n        string $entityName,\n        string|null $referencedColumnName,\n    ): string {\n        return $this->classToTableName($entityName) . '_' .\n                ($referencedColumnName ?: $this->referenceColumnName());\n    }\n\n    private function underscore(string $string): string\n    {\n        $string = preg_replace('/(?<=[a-z0-9])([A-Z])/', '_$1', $string);\n\n        if ($this->case === CASE_UPPER) {\n            return strtoupper($string);\n        }\n\n        return strtolower($string);\n    }\n}\n"
  },
  {
    "path": "src/Mapping/UniqueConstraint.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]\nfinal class UniqueConstraint implements MappingAttribute\n{\n    /**\n     * @param array<string>|null       $columns\n     * @param array<string>|null       $fields\n     * @param array<string,mixed>|null $options\n     */\n    public function __construct(\n        public readonly string|null $name = null,\n        public readonly array|null $columns = null,\n        public readonly array|null $fields = null,\n        public readonly array|null $options = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Mapping/Version.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Mapping;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_PROPERTY)]\nfinal class Version implements MappingAttribute\n{\n}\n"
  },
  {
    "path": "src/NativeQuery.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\n\nuse function array_values;\nuse function is_int;\nuse function key;\nuse function ksort;\n\n/**\n * Represents a native SQL query.\n *\n * @final\n */\nclass NativeQuery extends AbstractQuery\n{\n    private string $sql;\n\n    /** @return $this */\n    public function setSQL(string $sql): self\n    {\n        $this->sql = $sql;\n\n        return $this;\n    }\n\n    public function getSQL(): string\n    {\n        return $this->sql;\n    }\n\n    protected function _doExecute(): Result|int\n    {\n        $parameters = [];\n        $types      = [];\n\n        foreach ($this->getParameters() as $parameter) {\n            $name = $parameter->getName();\n\n            if ($parameter->typeWasSpecified()) {\n                $parameters[$name] = $parameter->getValue();\n                $types[$name]      = $parameter->getType();\n\n                continue;\n            }\n\n            $value = $this->processParameterValue($parameter->getValue());\n            $type  = $parameter->getValue() === $value\n                ? $parameter->getType()\n                : ParameterTypeInferer::inferType($value);\n\n            $parameters[$name] = $value;\n            $types[$name]      = $type;\n        }\n\n        if ($parameters && is_int(key($parameters))) {\n            ksort($parameters);\n            ksort($types);\n\n            $parameters = array_values($parameters);\n            $types      = array_values($types);\n        }\n\n        return $this->em->getConnection()->executeQuery(\n            $this->sql,\n            $parameters,\n            $types,\n            $this->queryCacheProfile,\n        );\n    }\n}\n"
  },
  {
    "path": "src/NoResultException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\n/**\n * Exception thrown when an ORM query unexpectedly does not return any results.\n */\nclass NoResultException extends UnexpectedResultException\n{\n    public function __construct()\n    {\n        parent::__construct('No result was found for query although at least one row was expected.');\n    }\n}\n"
  },
  {
    "path": "src/NonUniqueResultException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\n/**\n * Exception thrown when an ORM query unexpectedly returns more than one result.\n */\nclass NonUniqueResultException extends UnexpectedResultException\n{\n    public const DEFAULT_MESSAGE = 'More than one result was found for query although one row or none was expected.';\n\n    public function __construct(string|null $message = null)\n    {\n        parent::__construct($message ?? self::DEFAULT_MESSAGE);\n    }\n}\n"
  },
  {
    "path": "src/ORMInvalidArgumentException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse InvalidArgumentException;\nuse Stringable;\n\nuse function array_map;\nuse function count;\nuse function get_debug_type;\nuse function gettype;\nuse function implode;\nuse function is_scalar;\nuse function reset;\nuse function spl_object_id;\nuse function sprintf;\n\n/**\n * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork\n */\nclass ORMInvalidArgumentException extends InvalidArgumentException\n{\n    public static function scheduleInsertForManagedEntity(object $entity): self\n    {\n        return new self('A managed+dirty entity ' . self::objToStr($entity) . ' can not be scheduled for insertion.');\n    }\n\n    public static function scheduleInsertForRemovedEntity(object $entity): self\n    {\n        return new self('Removed entity ' . self::objToStr($entity) . ' can not be scheduled for insertion.');\n    }\n\n    public static function scheduleInsertTwice(object $entity): self\n    {\n        return new self('Entity ' . self::objToStr($entity) . ' can not be scheduled for insertion twice.');\n    }\n\n    public static function entityWithoutIdentity(string $className, object $entity): self\n    {\n        return new self(\n            \"The given entity of type '\" . $className . \"' (\" . self::objToStr($entity) . ') has no identity/no ' .\n            'id values set. It cannot be added to the identity map.',\n        );\n    }\n\n    public static function readOnlyRequiresManagedEntity(object $entity): self\n    {\n        return new self('Only managed entities can be marked or checked as read only. But ' . self::objToStr($entity) . ' is not');\n    }\n\n    /** @param non-empty-list<array{AssociationMapping, object}> $newEntitiesWithAssociations */\n    public static function newEntitiesFoundThroughRelationships(array $newEntitiesWithAssociations): self\n    {\n        $errorMessages = array_map(\n            static function (array $newEntityWithAssociation): string {\n                [$associationMapping, $entity] = $newEntityWithAssociation;\n\n                return self::newEntityFoundThroughRelationshipMessage($associationMapping, $entity);\n            },\n            $newEntitiesWithAssociations,\n        );\n\n        if (count($errorMessages) === 1) {\n            return new self(reset($errorMessages));\n        }\n\n        return new self(\n            'Multiple non-persisted new entities were found through the given association graph:'\n            . \"\\n\\n * \"\n            . implode(\"\\n * \", $errorMessages),\n        );\n    }\n\n    public static function newEntityFoundThroughRelationship(AssociationMapping $associationMapping, object $entry): self\n    {\n        return new self(self::newEntityFoundThroughRelationshipMessage($associationMapping, $entry));\n    }\n\n    public static function detachedEntityFoundThroughRelationship(AssociationMapping $assoc, object $entry): self\n    {\n        return new self('A detached entity of type ' . $assoc->targetEntity . ' (' . self::objToStr($entry) . ') '\n            . \" was found through the relationship '\" . $assoc->sourceEntity . '#' . $assoc->fieldName . \"' \"\n            . 'during cascading a persist operation.');\n    }\n\n    public static function entityNotManaged(object $entity): self\n    {\n        return new self('Entity ' . self::objToStr($entity) . ' is not managed. An entity is managed if its fetched ' .\n            'from the database or registered as new through EntityManager#persist');\n    }\n\n    public static function entityHasNoIdentity(object $entity, string $operation): self\n    {\n        return new self('Entity has no identity, therefore ' . $operation . ' cannot be performed. ' . self::objToStr($entity));\n    }\n\n    public static function entityIsRemoved(object $entity, string $operation): self\n    {\n        return new self('Entity is removed, therefore ' . $operation . ' cannot be performed. ' . self::objToStr($entity));\n    }\n\n    public static function detachedEntityCannot(object $entity, string $operation): self\n    {\n        return new self('Detached entity ' . self::objToStr($entity) . ' cannot be ' . $operation);\n    }\n\n    public static function invalidObject(string $context, mixed $given, int $parameterIndex = 1): self\n    {\n        return new self($context . ' expects parameter ' . $parameterIndex .\n            ' to be an entity object, ' . gettype($given) . ' given.');\n    }\n\n    public static function invalidCompositeIdentifier(): self\n    {\n        return new self('Binding an entity with a composite primary key to a query is not supported. ' .\n            'You should split the parameter into the explicit fields and bind them separately.');\n    }\n\n    public static function invalidIdentifierBindingEntity(string $class): self\n    {\n        return new self(sprintf(\n            <<<'EXCEPTION'\nBinding entities to query parameters only allowed for entities that have an identifier.\nClass \"%s\" does not have an identifier.\nEXCEPTION\n            ,\n            $class,\n        ));\n    }\n\n    public static function invalidAssociation(ClassMetadata $targetClass, AssociationMapping $assoc, mixed $actualValue): self\n    {\n        $expectedType = $targetClass->getName();\n\n        return new self(sprintf(\n            'Expected value of type \"%s\" for association field \"%s#$%s\", got \"%s\" instead.',\n            $expectedType,\n            $assoc->sourceEntity,\n            $assoc->fieldName,\n            get_debug_type($actualValue),\n        ));\n    }\n\n    public static function invalidAutoGenerateMode(mixed $value): self\n    {\n        return new self(sprintf('Invalid auto generate mode \"%s\" given.', is_scalar($value) ? (string) $value : get_debug_type($value)));\n    }\n\n    public static function missingPrimaryKeyValue(string $className, string $idField): self\n    {\n        return new self(sprintf('Missing value for primary key %s on %s', $idField, $className));\n    }\n\n    public static function proxyDirectoryRequired(): self\n    {\n        return new self('You must configure a proxy directory. See docs for details');\n    }\n\n    public static function lazyGhostUnavailable(): self\n    {\n        return new self('Symfony LazyGhost is not available. Please install the \"symfony/var-exporter\" package version 6.4 or 7 to use this feature or enable PHP 8.4 native lazy objects.');\n    }\n\n    public static function proxyNamespaceRequired(): self\n    {\n        return new self('You must configure a proxy namespace');\n    }\n\n    public static function proxyDirectoryNotWritable(string $proxyDirectory): self\n    {\n        return new self(sprintf('Your proxy directory \"%s\" must be writable', $proxyDirectory));\n    }\n\n    /**\n     * Helper method to show an object as string.\n     */\n    private static function objToStr(object $obj): string\n    {\n        return $obj instanceof Stringable ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj);\n    }\n\n    private static function newEntityFoundThroughRelationshipMessage(AssociationMapping $associationMapping, object $entity): string\n    {\n        return 'A new entity was found through the relationship \\''\n            . $associationMapping->sourceEntity . '#' . $associationMapping->fieldName . '\\' that was not'\n            . ' configured to cascade persist operations for entity: ' . self::objToStr($entity) . '.'\n            . ' To solve this issue: Either explicitly call EntityManager#persist()'\n            . ' on this unknown entity or configure cascade persist'\n            . ' this association in the mapping for example #[ORM\\ManyToOne(..., cascade: [\\'persist\\'])].'\n            . ($entity instanceof Stringable\n                ? ''\n                : ' If you cannot find out which entity causes the problem implement \\''\n                . $associationMapping->targetEntity . '#__toString()\\' to get a clue.'\n            );\n    }\n}\n"
  },
  {
    "path": "src/ORMSetup.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\nuse Doctrine\\ORM\\Mapping\\Driver\\XmlDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ClassLocator;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Redis;\nuse RuntimeException;\nuse Symfony\\Component\\Cache\\Adapter\\ApcuAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\RedisAdapter;\n\nuse function apcu_enabled;\nuse function class_exists;\nuse function extension_loaded;\nuse function md5;\nuse function sys_get_temp_dir;\n\nuse const PHP_VERSION_ID;\n\nfinal class ORMSetup\n{\n    /**\n     * Creates a configuration with an attribute metadata driver.\n     *\n     * @param string[]|ClassLocator $paths\n     */\n    public static function createAttributeMetadataConfiguration(\n        array|ClassLocator $paths,\n        bool $isDevMode = false,\n        string|null $proxyDir = null,\n        CacheItemPoolInterface|null $cache = null,\n    ): Configuration {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                '%s is deprecated in favor of %s, and will be removed in 4.0.',\n                __METHOD__,\n                self::class . '::createAttributeMetadataConfig()',\n            );\n        }\n\n        $config = self::createConfiguration($isDevMode, $proxyDir, $cache);\n        $config->setMetadataDriverImpl(new AttributeDriver($paths));\n\n        return $config;\n    }\n\n    /**\n     * Creates a configuration with an attribute metadata driver.\n     *\n     * @param string[]|ClassLocator $paths\n     */\n    public static function createAttributeMetadataConfig(\n        array|ClassLocator $paths,\n        bool $isDevMode = false,\n        string|null $cacheNamespaceSeed = null,\n        CacheItemPoolInterface|null $cache = null,\n    ): Configuration {\n        $config = self::createConfig($isDevMode, $cacheNamespaceSeed, $cache);\n        $config->setMetadataDriverImpl(new AttributeDriver($paths));\n\n        return $config;\n    }\n\n    /**\n     * Creates a configuration with an XML metadata driver.\n     *\n     * @param string[] $paths\n     */\n    public static function createXMLMetadataConfiguration(\n        array $paths,\n        bool $isDevMode = false,\n        string|null $proxyDir = null,\n        CacheItemPoolInterface|null $cache = null,\n        bool $isXsdValidationEnabled = true,\n    ): Configuration {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                '%s is deprecated in favor of %s, and will be removed in 4.0.',\n                __METHOD__,\n                self::class . '::createXMLMetadataConfig()',\n            );\n        }\n\n        $config = self::createConfiguration($isDevMode, $proxyDir, $cache);\n        $config->setMetadataDriverImpl(new XmlDriver($paths, XmlDriver::DEFAULT_FILE_EXTENSION, $isXsdValidationEnabled));\n\n        return $config;\n    }\n\n    /**\n     * Creates a configuration with an XML metadata driver.\n     *\n     * @param string[] $paths\n     */\n    public static function createXMLMetadataConfig(\n        array $paths,\n        bool $isDevMode = false,\n        string|null $cacheNamespaceSeed = null,\n        CacheItemPoolInterface|null $cache = null,\n        bool $isXsdValidationEnabled = true,\n    ): Configuration {\n        $config = self::createConfig($isDevMode, $cacheNamespaceSeed, $cache);\n        $config->setMetadataDriverImpl(new XmlDriver(\n            $paths,\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            $isXsdValidationEnabled,\n        ));\n\n        return $config;\n    }\n\n    /**\n     * Creates a configuration without a metadata driver.\n     */\n    public static function createConfiguration(\n        bool $isDevMode = false,\n        string|null $proxyDir = null,\n        CacheItemPoolInterface|null $cache = null,\n    ): Configuration {\n        if (PHP_VERSION_ID >= 80400 && $proxyDir !== null) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                '%s is deprecated in favor of %s, and will be removed in 4.0.',\n                __METHOD__,\n                self::class . '::createConfig()',\n            );\n        }\n\n        $proxyDir = $proxyDir ?: sys_get_temp_dir();\n\n        $cache = self::createCacheInstance($isDevMode, $proxyDir, $cache);\n\n        $config = new Configuration();\n\n        $config->setMetadataCache($cache);\n        $config->setQueryCache($cache);\n        $config->setResultCache($cache);\n        $config->setProxyDir($proxyDir);\n        $config->setProxyNamespace('DoctrineProxies');\n        $config->setAutoGenerateProxyClasses($isDevMode);\n\n        return $config;\n    }\n\n    public static function createConfig(\n        bool $isDevMode = false,\n        string|null $cacheNamespaceSeed = null,\n        CacheItemPoolInterface|null $cache = null,\n    ): Configuration {\n        $cache  = self::createCacheInstance($isDevMode, $cacheNamespaceSeed, $cache);\n        $config = new Configuration();\n        $config->setMetadataCache($cache);\n        $config->setQueryCache($cache);\n        $config->setResultCache($cache);\n\n        return $config;\n    }\n\n    private static function createCacheInstance(\n        bool $isDevMode,\n        string|null $cacheNamespaceSeed,\n        CacheItemPoolInterface|null $cache,\n    ): CacheItemPoolInterface {\n        if ($cache !== null) {\n            return $cache;\n        }\n\n        if (! class_exists(ArrayAdapter::class)) {\n            throw new RuntimeException(\n                'The Doctrine setup tool cannot configure caches without symfony/cache.'\n                . ' Please add symfony/cache as explicit dependency or pass your own cache implementation.',\n            );\n        }\n\n        if ($isDevMode) {\n            return new ArrayAdapter();\n        }\n\n        $namespace = 'dc2_' . md5($cacheNamespaceSeed ?? 'default');\n\n        if (extension_loaded('apcu') && apcu_enabled()) {\n            return new ApcuAdapter($namespace);\n        }\n\n        if (MemcachedAdapter::isSupported()) {\n            return new MemcachedAdapter(MemcachedAdapter::createConnection('memcached://127.0.0.1'), $namespace);\n        }\n\n        if (extension_loaded('redis')) {\n            $redis = new Redis();\n            $redis->connect('127.0.0.1');\n\n            return new RedisAdapter($redis, $namespace);\n        }\n\n        return new ArrayAdapter();\n    }\n\n    private function __construct()\n    {\n    }\n}\n"
  },
  {
    "path": "src/OptimisticLockException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse DateTimeInterface;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Exception;\nuse Throwable;\n\n/**\n * An OptimisticLockException is thrown when a version check on an object\n * that uses optimistic locking through a version field fails.\n */\nclass OptimisticLockException extends Exception implements ORMException\n{\n    public function __construct(\n        string $msg,\n        private readonly object|string|null $entity,\n        Throwable|null $previous = null,\n    ) {\n        parent::__construct($msg, 0, $previous);\n    }\n\n    /**\n     * Gets the entity that caused the exception.\n     */\n    public function getEntity(): object|string|null\n    {\n        return $this->entity;\n    }\n\n    /** @param object|class-string $entity */\n    public static function lockFailed(object|string $entity): self\n    {\n        return new self('The optimistic lock on an entity failed.', $entity);\n    }\n\n    public static function lockFailedVersionMismatch(\n        object $entity,\n        int|string|DateTimeInterface $expectedLockVersion,\n        int|string|DateTimeInterface $actualLockVersion,\n    ): self {\n        $expectedLockVersion = $expectedLockVersion instanceof DateTimeInterface ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;\n        $actualLockVersion   = $actualLockVersion instanceof DateTimeInterface ? $actualLockVersion->getTimestamp() : $actualLockVersion;\n\n        return new self('The optimistic lock failed, version ' . $expectedLockVersion . ' was expected, but is actually ' . $actualLockVersion, $entity);\n    }\n\n    public static function notVersioned(string $entityName): self\n    {\n        return new self('Cannot obtain optimistic lock on unversioned entity ' . $entityName, null);\n    }\n}\n"
  },
  {
    "path": "src/PersistentCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\Common\\Collections\\AbstractLazyCollection;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ToManyAssociationMapping;\nuse RuntimeException;\nuse UnexpectedValueException;\n\nuse function array_combine;\nuse function array_diff_key;\nuse function array_map;\nuse function array_values;\nuse function array_walk;\nuse function assert;\nuse function is_object;\nuse function spl_object_id;\nuse function strtoupper;\n\n/**\n * A PersistentCollection represents a collection of elements that have persistent state.\n *\n * Collections of entities represent only the associations (links) to those entities.\n * That means, if the collection is part of a many-many mapping and you remove\n * entities from the collection, only the links in the relation table are removed (on flush).\n * Similarly, if you remove entities from a collection that is part of a one-many\n * mapping this will only result in the nulling out of the foreign keys on flush.\n *\n * @phpstan-template TKey of array-key\n * @phpstan-template T\n * @template-extends AbstractLazyCollection<TKey,T>\n * @template-implements Selectable<TKey,T>\n */\nfinal class PersistentCollection extends AbstractLazyCollection implements Selectable\n{\n    /**\n     * A snapshot of the collection at the moment it was fetched from the database.\n     * This is used to create a diff of the collection at commit time.\n     *\n     * @phpstan-var array<string|int, mixed>\n     */\n    private array $snapshot = [];\n\n    /**\n     * The entity that owns this collection.\n     */\n    private object|null $owner = null;\n\n    /**\n     * The association mapping the collection belongs to.\n     * This is currently either a OneToManyMapping or a ManyToManyMapping.\n     *\n     * @var (AssociationMapping&ToManyAssociationMapping)|null\n     */\n    private AssociationMapping|null $association = null;\n\n    /**\n     * The name of the field on the target entities that points to the owner\n     * of the collection. This is only set if the association is bi-directional.\n     */\n    private string|null $backRefFieldName = null;\n\n    /**\n     * Whether the collection is dirty and needs to be synchronized with the database\n     * when the UnitOfWork that manages its persistent state commits.\n     */\n    private bool $isDirty = false;\n\n    /**\n     * Creates a new persistent collection.\n     *\n     * @param EntityManagerInterface $em        The EntityManager the collection will be associated with.\n     * @param ClassMetadata          $typeClass The class descriptor of the entity type of this collection.\n     * @phpstan-param Collection<TKey, T>&Selectable<TKey, T> $collection The collection elements.\n     */\n    public function __construct(\n        private EntityManagerInterface|null $em,\n        private readonly ClassMetadata|null $typeClass,\n        Collection $collection,\n    ) {\n        $this->collection  = $collection;\n        $this->initialized = true;\n    }\n\n    /**\n     * INTERNAL:\n     * Sets the collection's owning entity together with the AssociationMapping that\n     * describes the association between the owner and the elements of the collection.\n     */\n    public function setOwner(object $entity, AssociationMapping&ToManyAssociationMapping $assoc): void\n    {\n        $this->owner            = $entity;\n        $this->association      = $assoc;\n        $this->backRefFieldName = $assoc->isOwningSide() ? $assoc->inversedBy : $assoc->mappedBy;\n    }\n\n    /**\n     * INTERNAL:\n     * Gets the collection owner.\n     */\n    public function getOwner(): object|null\n    {\n        return $this->owner;\n    }\n\n    public function getTypeClass(): ClassMetadata\n    {\n        assert($this->typeClass !== null);\n\n        return $this->typeClass;\n    }\n\n    private function getUnitOfWork(): UnitOfWork\n    {\n        assert($this->em !== null);\n\n        return $this->em->getUnitOfWork();\n    }\n\n    /**\n     * INTERNAL:\n     * Adds an element to a collection during hydration. This will automatically\n     * complete bidirectional associations in the case of a one-to-many association.\n     */\n    public function hydrateAdd(mixed $element): void\n    {\n        $this->unwrap()->add($element);\n\n        // If _backRefFieldName is set and its a one-to-many association,\n        // we need to set the back reference.\n        if ($this->backRefFieldName && $this->getMapping()->isOneToMany()) {\n            assert($this->typeClass !== null);\n            // Set back reference to owner\n            $this->typeClass->propertyAccessors[$this->backRefFieldName]->setValue(\n                $element,\n                $this->owner,\n            );\n\n            $this->getUnitOfWork()->setOriginalEntityProperty(\n                spl_object_id($element),\n                $this->backRefFieldName,\n                $this->owner,\n            );\n        }\n    }\n\n    /**\n     * INTERNAL:\n     * Sets a keyed element in the collection during hydration.\n     */\n    public function hydrateSet(mixed $key, mixed $element): void\n    {\n        $this->unwrap()->set($key, $element);\n\n        // If _backRefFieldName is set, then the association is bidirectional\n        // and we need to set the back reference.\n        if ($this->backRefFieldName && $this->getMapping()->isOneToMany()) {\n            assert($this->typeClass !== null);\n            // Set back reference to owner\n            $this->typeClass->propertyAccessors[$this->backRefFieldName]->setValue(\n                $element,\n                $this->owner,\n            );\n        }\n    }\n\n    /**\n     * Initializes the collection by loading its contents from the database\n     * if the collection is not yet initialized.\n     */\n    public function initialize(): void\n    {\n        if ($this->initialized || ! $this->association) {\n            return;\n        }\n\n        $this->doInitialize();\n\n        $this->initialized = true;\n    }\n\n    /**\n     * INTERNAL:\n     * Tells this collection to take a snapshot of its current state.\n     */\n    public function takeSnapshot(): void\n    {\n        $this->snapshot = $this->unwrap()->toArray();\n        $this->isDirty  = false;\n    }\n\n    /**\n     * INTERNAL:\n     * Returns the last snapshot of the elements in the collection.\n     *\n     * @phpstan-return array<string|int, mixed> The last snapshot of the elements.\n     */\n    public function getSnapshot(): array\n    {\n        return $this->snapshot;\n    }\n\n    /**\n     * INTERNAL:\n     * getDeleteDiff\n     *\n     * @return mixed[]\n     */\n    public function getDeleteDiff(): array\n    {\n        $collectionItems = $this->unwrap()->toArray();\n\n        return array_values(array_diff_key(\n            array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot),\n            array_combine(array_map('spl_object_id', $collectionItems), $collectionItems),\n        ));\n    }\n\n    /**\n     * INTERNAL:\n     * getInsertDiff\n     *\n     * @return mixed[]\n     */\n    public function getInsertDiff(): array\n    {\n        $collectionItems = $this->unwrap()->toArray();\n\n        return array_values(array_diff_key(\n            array_combine(array_map('spl_object_id', $collectionItems), $collectionItems),\n            array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot),\n        ));\n    }\n\n    /** INTERNAL: Gets the association mapping of the collection. */\n    public function getMapping(): AssociationMapping&ToManyAssociationMapping\n    {\n        if ($this->association === null) {\n            throw new UnexpectedValueException('The underlying association mapping is null although it should not be');\n        }\n\n        return $this->association;\n    }\n\n    /**\n     * Marks this collection as changed/dirty.\n     */\n    private function changed(): void\n    {\n        if ($this->isDirty) {\n            return;\n        }\n\n        $this->isDirty = true;\n    }\n\n    /**\n     * Gets a boolean flag indicating whether this collection is dirty which means\n     * its state needs to be synchronized with the database.\n     */\n    public function isDirty(): bool\n    {\n        return $this->isDirty;\n    }\n\n    /**\n     * Sets a boolean flag, indicating whether this collection is dirty.\n     */\n    public function setDirty(bool $dirty): void\n    {\n        $this->isDirty = $dirty;\n    }\n\n    /**\n     * Sets the initialized flag of the collection, forcing it into that state.\n     */\n    public function setInitialized(bool $bool): void\n    {\n        $this->initialized = $bool;\n    }\n\n    public function remove(string|int $key): mixed\n    {\n        // TODO: If the keys are persistent as well (not yet implemented)\n        //       and the collection is not initialized and orphanRemoval is\n        //       not used we can issue a straight SQL delete/update on the\n        //       association (table). Without initializing the collection.\n        $removed = parent::remove($key);\n\n        if (! $removed) {\n            return $removed;\n        }\n\n        $this->changed();\n\n        if (\n            $this->association !== null &&\n            $this->association->isToMany() &&\n            $this->owner &&\n            $this->getMapping()->orphanRemoval\n        ) {\n            $this->getUnitOfWork()->scheduleOrphanRemoval($removed);\n        }\n\n        return $removed;\n    }\n\n    public function removeElement(mixed $element): bool\n    {\n        $removed = parent::removeElement($element);\n\n        if (! $removed) {\n            return $removed;\n        }\n\n        $this->changed();\n\n        if (\n            $this->association !== null &&\n            $this->association->isToMany() &&\n            $this->owner &&\n            $this->getMapping()->orphanRemoval\n        ) {\n            $this->getUnitOfWork()->scheduleOrphanRemoval($element);\n        }\n\n        return $removed;\n    }\n\n    public function containsKey(mixed $key): bool\n    {\n        if (\n            ! $this->initialized && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY\n            && isset($this->getMapping()->indexBy)\n        ) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());\n\n            return $this->unwrap()->containsKey($key) || $persister->containsKey($this, $key);\n        }\n\n        return parent::containsKey($key);\n    }\n\n    public function contains(mixed $element): bool\n    {\n        if (! $this->initialized && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());\n\n            return $this->unwrap()->contains($element) || $persister->contains($this, $element);\n        }\n\n        return parent::contains($element);\n    }\n\n    public function get(string|int $key): mixed\n    {\n        if (\n            ! $this->initialized\n            && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY\n            && isset($this->getMapping()->indexBy)\n        ) {\n            assert($this->em !== null);\n            assert($this->typeClass !== null);\n            if (! $this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->getMapping()->indexBy)) {\n                return $this->em->find($this->typeClass->name, $key);\n            }\n\n            return $this->getUnitOfWork()->getCollectionPersister($this->getMapping())->get($this, $key);\n        }\n\n        return parent::get($key);\n    }\n\n    public function count(): int\n    {\n        if (! $this->initialized && $this->association !== null && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($this->association);\n\n            return $persister->count($this) + ($this->isDirty ? $this->unwrap()->count() : 0);\n        }\n\n        return parent::count();\n    }\n\n    public function set(string|int $key, mixed $value): void\n    {\n        parent::set($key, $value);\n\n        $this->changed();\n\n        if (is_object($value) && $this->em) {\n            $this->getUnitOfWork()->cancelOrphanRemoval($value);\n        }\n    }\n\n    public function add(mixed $value): bool\n    {\n        $this->unwrap()->add($value);\n\n        $this->changed();\n\n        if (is_object($value) && $this->em) {\n            $this->getUnitOfWork()->cancelOrphanRemoval($value);\n        }\n\n        return true;\n    }\n\n    public function offsetExists(mixed $offset): bool\n    {\n        return $this->containsKey($offset);\n    }\n\n    public function offsetGet(mixed $offset): mixed\n    {\n        return $this->get($offset);\n    }\n\n    public function offsetSet(mixed $offset, mixed $value): void\n    {\n        if (! isset($offset)) {\n            $this->add($value);\n\n            return;\n        }\n\n        $this->set($offset, $value);\n    }\n\n    public function offsetUnset(mixed $offset): void\n    {\n        $this->remove($offset);\n    }\n\n    public function isEmpty(): bool\n    {\n        return $this->unwrap()->isEmpty() && $this->count() === 0;\n    }\n\n    public function clear(): void\n    {\n        if ($this->initialized && $this->isEmpty()) {\n            $this->unwrap()->clear();\n\n            return;\n        }\n\n        $uow         = $this->getUnitOfWork();\n        $association = $this->getMapping();\n\n        if (\n            $association->isToMany() &&\n            $association->orphanRemoval &&\n            $this->owner\n        ) {\n            // we need to initialize here, as orphan removal acts like implicit cascadeRemove,\n            // hence for event listeners we need the objects in memory.\n            $this->initialize();\n\n            foreach ($this->unwrap() as $element) {\n                $uow->scheduleOrphanRemoval($element);\n            }\n        }\n\n        $this->unwrap()->clear();\n\n        $this->initialized = true; // direct call, {@link initialize()} is too expensive\n\n        if ($association->isOwningSide() && $this->owner) {\n            $this->changed();\n\n            $uow->scheduleCollectionDeletion($this);\n\n            $this->takeSnapshot();\n        }\n    }\n\n    /**\n     * Called by PHP when this collection is serialized. Ensures that only the\n     * elements are properly serialized.\n     *\n     * Internal note: Tried to implement Serializable first but that did not work well\n     *                with circular references. This solution seems simpler and works well.\n     *\n     * @return string[]\n     * @phpstan-return array{0: string, 1: string}\n     */\n    public function __sleep(): array\n    {\n        return ['collection', 'initialized'];\n    }\n\n    public function __wakeup(): void\n    {\n        $this->em = null;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function first()\n    {\n        if (! $this->initialized && ! $this->isDirty && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());\n\n            return array_values($persister->slice($this, 0, 1))[0] ?? false;\n        }\n\n        return parent::first();\n    }\n\n    /**\n     * Extracts a slice of $length elements starting at position $offset from the Collection.\n     *\n     * If $length is null it returns all elements from $offset to the end of the Collection.\n     * Keys have to be preserved by this method. Calling this method will only return the\n     * selected slice and NOT change the elements contained in the collection slice is called on.\n     *\n     * @return mixed[]\n     * @phpstan-return array<TKey,T>\n     */\n    public function slice(int $offset, int|null $length = null): array\n    {\n        if (! $this->initialized && ! $this->isDirty && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());\n\n            return $persister->slice($this, $offset, $length);\n        }\n\n        return parent::slice($offset, $length);\n    }\n\n    /**\n     * Cleans up internal state of cloned persistent collection.\n     *\n     * The following problems have to be prevented:\n     * 1. Added entities are added to old PC\n     * 2. New collection is not dirty, if reused on other entity nothing\n     * changes.\n     * 3. Snapshot leads to invalid diffs being generated.\n     * 4. Lazy loading grabs entities from old owner object.\n     * 5. New collection is connected to old owner and leads to duplicate keys.\n     */\n    public function __clone()\n    {\n        if (is_object($this->collection)) {\n            $this->collection = clone $this->collection;\n        }\n\n        $this->initialize();\n\n        $this->owner    = null;\n        $this->snapshot = [];\n\n        $this->changed();\n    }\n\n    /**\n     * Selects all elements from a selectable that match the expression and\n     * return a new collection containing these elements.\n     *\n     * @phpstan-return Collection<TKey, T>\n     *\n     * @throws RuntimeException\n     */\n    public function matching(Criteria $criteria): Collection\n    {\n        if ($this->isDirty) {\n            $this->initialize();\n        }\n\n        if ($this->initialized) {\n            return $this->unwrap()->matching($criteria);\n        }\n\n        $association = $this->getMapping();\n        if ($association->isManyToMany()) {\n            $persister = $this->getUnitOfWork()->getCollectionPersister($association);\n\n            return new ArrayCollection($persister->loadCriteria($this, $criteria));\n        }\n\n        $builder         = Criteria::expr();\n        $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner);\n        $expression      = $criteria->getWhereExpression();\n        $expression      = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression;\n\n        $criteria = clone $criteria;\n        $criteria->where($expression);\n        $criteria->orderBy(\n            $criteria->orderings() ?: array_map(\n                static fn (string $order): Order => Order::from(strtoupper($order)),\n                $association->orderBy(),\n            ),\n        );\n\n        $persister = $this->getUnitOfWork()->getEntityPersister($association->targetEntity);\n\n        return $association->fetch === ClassMetadata::FETCH_EXTRA_LAZY\n            ? new LazyCriteriaCollection($persister, $criteria)\n            : new ArrayCollection($persister->loadCriteria($criteria));\n    }\n\n    /**\n     * Retrieves the wrapped Collection instance.\n     *\n     * @return Collection<TKey, T>&Selectable<TKey, T>\n     */\n    public function unwrap(): Selectable&Collection\n    {\n        assert($this->collection instanceof Collection);\n        assert($this->collection instanceof Selectable);\n\n        return $this->collection;\n    }\n\n    protected function doInitialize(): void\n    {\n        // Has NEW objects added through add(). Remember them.\n        $newlyAddedDirtyObjects = [];\n\n        if ($this->isDirty) {\n            $newlyAddedDirtyObjects = $this->unwrap()->toArray();\n        }\n\n        $this->unwrap()->clear();\n        $this->getUnitOfWork()->loadCollection($this);\n        $this->takeSnapshot();\n\n        if ($newlyAddedDirtyObjects) {\n            $this->restoreNewObjectsInDirtyCollection($newlyAddedDirtyObjects);\n        }\n    }\n\n    /**\n     * @param object[] $newObjects\n     *\n     * Note: the only reason why this entire looping/complexity is performed via `spl_object_id`\n     *       is because we want to prevent using `array_udiff()`, which is likely to cause very\n     *       high overhead (complexity of O(n^2)). `array_diff_key()` performs the operation in\n     *       core, which is faster than using a callback for comparisons\n     */\n    private function restoreNewObjectsInDirtyCollection(array $newObjects): void\n    {\n        $loadedObjects               = $this->unwrap()->toArray();\n        $newObjectsByOid             = array_combine(array_map('spl_object_id', $newObjects), $newObjects);\n        $loadedObjectsByOid          = array_combine(array_map('spl_object_id', $loadedObjects), $loadedObjects);\n        $newObjectsThatWereNotLoaded = array_diff_key($newObjectsByOid, $loadedObjectsByOid);\n\n        if ($newObjectsThatWereNotLoaded) {\n            // Reattach NEW objects added through add(), if any.\n            array_walk($newObjectsThatWereNotLoaded, [$this->unwrap(), 'add']);\n\n            $this->isDirty = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Collection/AbstractCollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Collection;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\UnitOfWork;\n\n/**\n * Base class for all collection persisters.\n */\nabstract class AbstractCollectionPersister implements CollectionPersister\n{\n    protected Connection $conn;\n    protected UnitOfWork $uow;\n    protected AbstractPlatform $platform;\n    protected QuoteStrategy $quoteStrategy;\n\n    /**\n     * Initializes a new instance of a class derived from AbstractCollectionPersister.\n     */\n    public function __construct(\n        protected EntityManagerInterface $em,\n    ) {\n        $this->uow           = $em->getUnitOfWork();\n        $this->conn          = $em->getConnection();\n        $this->platform      = $this->conn->getDatabasePlatform();\n        $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();\n    }\n\n    /**\n     * Check if entity is in a valid state for operations.\n     */\n    protected function isValidEntityState(object $entity): bool\n    {\n        $entityState = $this->uow->getEntityState($entity, UnitOfWork::STATE_NEW);\n\n        if ($entityState === UnitOfWork::STATE_NEW) {\n            return false;\n        }\n\n        // If Entity is scheduled for inclusion, it is not in this collection.\n        // We can assure that because it would have return true before on array check\n        return ! ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity));\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Collection/CollectionPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Collection;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\PersistentCollection;\n\n/**\n * Define the behavior that should be implemented by all collection persisters.\n */\ninterface CollectionPersister\n{\n    /**\n     * Deletes the persistent state represented by the given collection.\n     */\n    public function delete(PersistentCollection $collection): void;\n\n    /**\n     * Updates the given collection, synchronizing its state with the database\n     * by inserting, updating and deleting individual elements.\n     */\n    public function update(PersistentCollection $collection): void;\n\n    /**\n     * Counts the size of this persistent collection.\n     */\n    public function count(PersistentCollection $collection): int;\n\n    /**\n     * Slices elements.\n     *\n     * @return mixed[]\n     */\n    public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array;\n\n    /**\n     * Checks for existence of an element.\n     */\n    public function contains(PersistentCollection $collection, object $element): bool;\n\n    /**\n     * Checks for existence of a key.\n     */\n    public function containsKey(PersistentCollection $collection, mixed $key): bool;\n\n    /**\n     * Gets an element by key.\n     */\n    public function get(PersistentCollection $collection, mixed $index): mixed;\n\n    /**\n     * Loads association entities matching the given Criteria object.\n     *\n     * @return mixed[]\n     */\n    public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array;\n}\n"
  },
  {
    "path": "src/Persisters/Collection/ManyToManyPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Collection;\n\nuse BadMethodCallException;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\DBAL\\Exception as DBALException;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\InverseSideMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToManyAssociationMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\SqlValueVisitor;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\n\nuse function array_fill;\nuse function array_merge;\nuse function array_pop;\nuse function assert;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function reset;\nuse function sprintf;\n\n/**\n * Persister for many-to-many collections.\n */\nclass ManyToManyPersister extends AbstractCollectionPersister\n{\n    public function delete(PersistentCollection $collection): void\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isOwningSide()) {\n            return; // ignore inverse side\n        }\n\n        assert($mapping->isManyToManyOwningSide());\n\n        $types = [];\n        $class = $this->em->getClassMetadata($mapping->sourceEntity);\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em);\n        }\n\n        $this->conn->executeStatement($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types);\n    }\n\n    public function update(PersistentCollection $collection): void\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isOwningSide()) {\n            return; // ignore inverse side\n        }\n\n        [$deleteSql, $deleteTypes] = $this->getDeleteRowSQL($collection);\n        [$insertSql, $insertTypes] = $this->getInsertRowSQL($collection);\n\n        foreach ($collection->getDeleteDiff() as $element) {\n            $this->conn->executeStatement(\n                $deleteSql,\n                $this->getDeleteRowSQLParameters($collection, $element),\n                $deleteTypes,\n            );\n        }\n\n        foreach ($collection->getInsertDiff() as $element) {\n            $this->conn->executeStatement(\n                $insertSql,\n                $this->getInsertRowSQLParameters($collection, $element),\n                $insertTypes,\n            );\n        }\n    }\n\n    public function get(PersistentCollection $collection, mixed $index): object|null\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isIndexed()) {\n            throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');\n        }\n\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n        $mappedKey = $mapping->isOwningSide()\n            ? $mapping->inversedBy\n            : $mapping->mappedBy;\n\n        assert($mappedKey !== null);\n\n        return $persister->load(\n            [$mappedKey => $collection->getOwner(), $mapping->indexBy() => $index],\n            null,\n            $mapping,\n            [],\n            LockMode::NONE,\n            1,\n        );\n    }\n\n    public function count(PersistentCollection $collection): int\n    {\n        $conditions  = [];\n        $params      = [];\n        $types       = [];\n        $mapping     = $this->getMapping($collection);\n        $id          = $this->uow->getEntityIdentifier($collection->getOwner());\n        $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n        $association = $this->em->getMetadataFactory()->getOwningSide($mapping);\n\n        $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform);\n        $joinColumns   = ! $mapping->isOwningSide()\n            ? $association->joinTable->inverseJoinColumns\n            : $association->joinTable->joinColumns;\n\n        foreach ($joinColumns as $joinColumn) {\n            $columnName     = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform);\n            $referencedName = $joinColumn->referencedColumnName;\n            $conditions[]   = 't.' . $columnName . ' = ?';\n            $params[]       = $id[$sourceClass->getFieldForColumn($referencedName)];\n            $types[]        = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);\n        }\n\n        [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($mapping);\n\n        if ($filterSql) {\n            $conditions[] = $filterSql;\n        }\n\n        // If there is a provided criteria, make part of conditions\n        // @todo Fix this. Current SQL returns something like:\n        /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) {\n            // A join is needed on the target entity\n            $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);\n            $targetJoinSql   = ' JOIN ' . $targetTableName . ' te'\n                . ' ON' . implode(' AND ', $this->getOnConditionSQL($association));\n\n            // And criteria conditions needs to be added\n            $persister    = $this->uow->getEntityPersister($targetClass->name);\n            $visitor      = new SqlExpressionVisitor($persister, $targetClass);\n            $conditions[] = $visitor->dispatch($expression);\n\n            $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL;\n        }*/\n\n        $sql = 'SELECT COUNT(*)'\n            . ' FROM ' . $joinTableName . ' t'\n            . $joinTargetEntitySQL\n            . ' WHERE ' . implode(' AND ', $conditions);\n\n        return (int) $this->conn->fetchOne($sql, $params, $types);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array\n    {\n        $mapping   = $this->getMapping($collection);\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);\n    }\n\n    public function containsKey(PersistentCollection $collection, mixed $key): bool\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isIndexed()) {\n            throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');\n        }\n\n        [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictionsWithKey(\n            $collection,\n            (string) $key,\n            true,\n        );\n\n        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);\n\n        return (bool) $this->conn->fetchOne($sql, $params, $types);\n    }\n\n    public function contains(PersistentCollection $collection, object $element): bool\n    {\n        if (! $this->isValidEntityState($element)) {\n            return false;\n        }\n\n        [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictions(\n            $collection,\n            $element,\n            true,\n        );\n\n        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);\n\n        return (bool) $this->conn->fetchOne($sql, $params, $types);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array\n    {\n        $mapping       = $this->getMapping($collection);\n        $owner         = $collection->getOwner();\n        $ownerMetadata = $this->em->getClassMetadata($owner::class);\n        $id            = $this->uow->getEntityIdentifier($owner);\n        $targetClass   = $this->em->getClassMetadata($mapping->targetEntity);\n        $onConditions  = $this->getOnConditionSQL($mapping);\n        $whereClauses  = $params = [];\n        $paramTypes    = [];\n\n        if (! $mapping->isOwningSide()) {\n            assert($mapping instanceof InverseSideMapping);\n            $associationSourceClass = $targetClass;\n            $sourceRelationMode     = 'relationToTargetKeyColumns';\n        } else {\n            $associationSourceClass = $ownerMetadata;\n            $sourceRelationMode     = 'relationToSourceKeyColumns';\n        }\n\n        $mapping = $this->em->getMetadataFactory()->getOwningSide($mapping);\n\n        foreach ($mapping->$sourceRelationMode as $key => $value) {\n            $whereClauses[] = sprintf('t.%s = ?', $key);\n            $params[]       = $ownerMetadata->containsForeignIdentifier\n                ? $id[$ownerMetadata->getFieldForColumn($value)]\n                : $id[$ownerMetadata->fieldNames[$value]];\n            $paramTypes[]   = PersisterHelper::getTypeOfColumn($value, $ownerMetadata, $this->em);\n        }\n\n        $parameters = $this->expandCriteriaParameters($criteria);\n\n        foreach ($parameters as $parameter) {\n            [$name, $value, $operator] = $parameter;\n\n            $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);\n\n            if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {\n                $whereClauses[] = sprintf('te.%s %s NULL', $field, $operator === Comparison::EQ ? 'IS' : 'IS NOT');\n            } elseif ($operator === Comparison::IN || $operator === Comparison::NIN) {\n                $whereClauses[] = sprintf('te.%s %s (%s)', $field, $operator === Comparison::IN ? 'IN' : 'NOT IN', implode(', ', array_fill(0, count($value), '?')));\n                foreach ($value as $item) {\n                    $params     = array_merge($params, PersisterHelper::convertToParameterValue($item, $this->em));\n                    $paramTypes = array_merge($paramTypes, PersisterHelper::inferParameterTypes($name, $item, $targetClass, $this->em));\n                }\n            } else {\n                $whereClauses[] = sprintf('te.%s %s ?', $field, $operator);\n\n                $params     = [...$params, ...PersisterHelper::convertToParameterValue($value, $this->em)];\n                $paramTypes = [...$paramTypes, ...PersisterHelper::inferParameterTypes($name, $value, $targetClass, $this->em)];\n            }\n        }\n\n        $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);\n        $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform);\n\n        $rsm = new Query\\ResultSetMappingBuilder($this->em);\n        $rsm->addRootEntityFromClassMetadata($targetClass->name, 'te');\n\n        $sql = 'SELECT ' . $rsm->generateSelectClause()\n            . ' FROM ' . $tableName . ' te'\n            . ' JOIN ' . $joinTable . ' t ON'\n            . implode(' AND ', $onConditions)\n            . ' WHERE ' . implode(' AND ', $whereClauses);\n\n        $sql .= $this->getOrderingSql($criteria, $targetClass);\n\n        $sql .= $this->getLimitSql($criteria);\n\n        $stmt = $this->conn->executeQuery($sql, $params, $paramTypes);\n\n        return $this\n            ->em\n            ->newHydrator(Query::HYDRATE_OBJECT)\n            ->hydrateAll($stmt, $rsm);\n    }\n\n    /**\n     * Generates the filter SQL for a given mapping.\n     *\n     * This method is not used for actually grabbing the related entities\n     * but when the extra-lazy collection methods are called on a filtered\n     * association. This is why besides the many to many table we also\n     * have to join in the actual entities table leading to additional\n     * JOIN.\n     *\n     * @param AssociationMapping $mapping Array containing mapping information.\n     *\n     * @return string[] ordered tuple:\n     *                   - JOIN condition to add to the SQL\n     *                   - WHERE condition to add to the SQL\n     * @phpstan-return array{0: string, 1: string}\n     */\n    public function getFilterSql(AssociationMapping $mapping): array\n    {\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n        $rootClass   = $this->em->getClassMetadata($targetClass->rootEntityName);\n        $filterSql   = $this->generateFilterConditionSQL($rootClass, 'te');\n\n        if ($filterSql === '') {\n            return ['', ''];\n        }\n\n        // A join is needed if there is filtering on the target entity\n        $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform);\n        $joinSql   = ' JOIN ' . $tableName . ' te'\n            . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));\n\n        return [$joinSql, $filterSql];\n    }\n\n    /**\n     * Generates the filter SQL for a given entity and table alias.\n     *\n     * @param ClassMetadata $targetEntity     Metadata of the target entity.\n     * @param string        $targetTableAlias The table alias of the joined/selected table.\n     *\n     * @return string The SQL query part to add to a query.\n     */\n    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        $filterClauses = [];\n\n        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {\n            $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);\n            if ($filterExpr) {\n                $filterClauses[] = '(' . $filterExpr . ')';\n            }\n        }\n\n        return $filterClauses\n            ? '(' . implode(' AND ', $filterClauses) . ')'\n            : '';\n    }\n\n    /**\n     * Generate ON condition\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    protected function getOnConditionSQL(AssociationMapping $mapping): array\n    {\n        $association = $this->em->getMetadataFactory()->getOwningSide($mapping);\n        $joinColumns = $mapping->isOwningSide()\n            ? $association->joinTable->inverseJoinColumns\n            : $association->joinTable->joinColumns;\n\n        $conditions = [];\n\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n        foreach ($joinColumns as $joinColumn) {\n            $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n            $refColumnName  = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);\n\n            $conditions[] = ' t.' . $joinColumnName . ' = te.' . $refColumnName;\n        }\n\n        return $conditions;\n    }\n\n    protected function getDeleteSQL(PersistentCollection $collection): string\n    {\n        $columns = [];\n        $mapping = $this->getMapping($collection);\n        assert($mapping->isManyToManyOwningSide());\n        $class     = $this->em->getClassMetadata($collection->getOwner()::class);\n        $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n        }\n\n        return 'DELETE FROM ' . $joinTable\n            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';\n    }\n\n    /**\n     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql.\n     *\n     * @return list<mixed>\n     */\n    protected function getDeleteSQLParameters(PersistentCollection $collection): array\n    {\n        $mapping = $this->getMapping($collection);\n        assert($mapping->isManyToManyOwningSide());\n        $identifier = $this->uow->getEntityIdentifier($collection->getOwner());\n\n        // Optimization for single column identifier\n        if (count($mapping->relationToSourceKeyColumns) === 1) {\n            return [reset($identifier)];\n        }\n\n        // Composite identifier\n        $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n        $params      = [];\n\n        foreach ($mapping->relationToSourceKeyColumns as $columnName => $refColumnName) {\n            $params[] = isset($sourceClass->fieldNames[$refColumnName])\n                ? $identifier[$sourceClass->fieldNames[$refColumnName]]\n                : $identifier[$sourceClass->getFieldForColumn($refColumnName)];\n        }\n\n        return $params;\n    }\n\n    /**\n     * Gets the SQL statement used for deleting a row from the collection.\n     *\n     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array\n     *                             of types for bound parameters\n     * @phpstan-return array{0: string, 1: list<string>}\n     */\n    protected function getDeleteRowSQL(PersistentCollection $collection): array\n    {\n        $mapping = $this->getMapping($collection);\n        assert($mapping->isManyToManyOwningSide());\n        $class       = $this->em->getClassMetadata($mapping->sourceEntity);\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n        $columns     = [];\n        $types       = [];\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em);\n        }\n\n        foreach ($mapping->joinTable->inverseJoinColumns as $joinColumn) {\n            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n        }\n\n        return [\n            'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)\n            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',\n            $types,\n        ];\n    }\n\n    /**\n     * Gets the SQL parameters for the corresponding SQL statement to delete the given\n     * element from the given collection.\n     *\n     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.\n     *\n     * @return mixed[]\n     * @phpstan-return list<mixed>\n     */\n    protected function getDeleteRowSQLParameters(PersistentCollection $collection, object $element): array\n    {\n        return $this->collectJoinTableColumnParameters($collection, $element);\n    }\n\n    /**\n     * Gets the SQL statement used for inserting a row in the collection.\n     *\n     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array\n     *                             of types for bound parameters\n     * @phpstan-return array{0: string, 1: list<string>}\n     */\n    protected function getInsertRowSQL(PersistentCollection $collection): array\n    {\n        $columns = [];\n        $types   = [];\n        $mapping = $this->getMapping($collection);\n        assert($mapping->isManyToManyOwningSide());\n        $class       = $this->em->getClassMetadata($mapping->sourceEntity);\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em);\n        }\n\n        foreach ($mapping->joinTable->inverseJoinColumns as $joinColumn) {\n            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n        }\n\n        return [\n            'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)\n            . ' (' . implode(', ', $columns) . ')'\n            . ' VALUES'\n            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',\n            $types,\n        ];\n    }\n\n    /**\n     * Gets the SQL parameters for the corresponding SQL statement to insert the given\n     * element of the given collection into the database.\n     *\n     * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.\n     *\n     * @return mixed[]\n     * @phpstan-return list<mixed>\n     */\n    protected function getInsertRowSQLParameters(PersistentCollection $collection, object $element): array\n    {\n        return $this->collectJoinTableColumnParameters($collection, $element);\n    }\n\n    /**\n     * Collects the parameters for inserting/deleting on the join table in the order\n     * of the join table columns as specified in ManyToManyMapping#joinTableColumns.\n     *\n     * @return mixed[]\n     * @phpstan-return list<mixed>\n     */\n    private function collectJoinTableColumnParameters(\n        PersistentCollection $collection,\n        object $element,\n    ): array {\n        $params  = [];\n        $mapping = $this->getMapping($collection);\n        assert($mapping->isManyToManyOwningSide());\n        $isComposite = count($mapping->joinTableColumns) > 2;\n\n        $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());\n        $identifier2 = $this->uow->getEntityIdentifier($element);\n\n        $class1 = $class2 = null;\n        if ($isComposite) {\n            $class1 = $this->em->getClassMetadata($collection->getOwner()::class);\n            $class2 = $collection->getTypeClass();\n        }\n\n        foreach ($mapping->joinTableColumns as $joinTableColumn) {\n            $isRelationToSource = isset($mapping->relationToSourceKeyColumns[$joinTableColumn]);\n\n            if (! $isComposite) {\n                $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);\n\n                continue;\n            }\n\n            if ($isRelationToSource) {\n                $params[] = $identifier1[$class1->getFieldForColumn($mapping->relationToSourceKeyColumns[$joinTableColumn])];\n\n                continue;\n            }\n\n            $params[] = $identifier2[$class2->getFieldForColumn($mapping->relationToTargetKeyColumns[$joinTableColumn])];\n        }\n\n        return $params;\n    }\n\n    /**\n     * @param bool $addFilters Whether the filter SQL should be included or not.\n     *\n     * @return mixed[] ordered vector:\n     *                - quoted join table name\n     *                - where clauses to be added for filtering\n     *                - parameters to be bound for filtering\n     *                - types of the parameters to be bound for filtering\n     * @phpstan-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}\n     */\n    private function getJoinTableRestrictionsWithKey(\n        PersistentCollection $collection,\n        string $key,\n        bool $addFilters,\n    ): array {\n        $filterMapping = $this->getMapping($collection);\n        $mapping       = $filterMapping;\n        $indexBy       = $mapping->indexBy();\n        $id            = $this->uow->getEntityIdentifier($collection->getOwner());\n        $sourceClass   = $this->em->getClassMetadata($mapping->sourceEntity);\n        $targetClass   = $this->em->getClassMetadata($mapping->targetEntity);\n\n        if (! $mapping->isOwningSide()) {\n            assert($mapping instanceof InverseSideMapping);\n            $associationSourceClass = $this->em->getClassMetadata($mapping->targetEntity);\n            $mapping                = $associationSourceClass->associationMappings[$mapping->mappedBy];\n            assert($mapping->isManyToManyOwningSide());\n            $joinColumns        = $mapping->joinTable->joinColumns;\n            $sourceRelationMode = 'relationToTargetKeyColumns';\n            $targetRelationMode = 'relationToSourceKeyColumns';\n        } else {\n            assert($mapping->isManyToManyOwningSide());\n            $associationSourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n            $joinColumns            = $mapping->joinTable->inverseJoinColumns;\n            $sourceRelationMode     = 'relationToSourceKeyColumns';\n            $targetRelationMode     = 'relationToTargetKeyColumns';\n        }\n\n        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform) . ' t';\n        $whereClauses    = [];\n        $params          = [];\n        $types           = [];\n\n        $joinNeeded = ! in_array($indexBy, $targetClass->identifier, true);\n\n        if ($joinNeeded) { // extra join needed if indexBy is not a @id\n            $joinConditions = [];\n\n            foreach ($joinColumns as $joinTableColumn) {\n                $joinConditions[] = 't.' . $joinTableColumn->name . ' = tr.' . $joinTableColumn->referencedColumnName;\n            }\n\n            $tableName        = $this->quoteStrategy->getTableName($targetClass, $this->platform);\n            $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);\n            $columnName       = $targetClass->getColumnName($indexBy);\n\n            $whereClauses[] = 'tr.' . $columnName . ' = ?';\n            $params[]       = $key;\n            $types[]        = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em);\n        }\n\n        foreach ($mapping->joinTableColumns as $joinTableColumn) {\n            if (isset($mapping->{$sourceRelationMode}[$joinTableColumn])) {\n                $column         = $mapping->{$sourceRelationMode}[$joinTableColumn];\n                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';\n                $params[]       = $sourceClass->containsForeignIdentifier\n                    ? $id[$sourceClass->getFieldForColumn($column)]\n                    : $id[$sourceClass->fieldNames[$column]];\n                $types[]        = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);\n            } elseif (! $joinNeeded) {\n                $column = $mapping->{$targetRelationMode}[$joinTableColumn];\n\n                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';\n                $params[]       = $key;\n                $types[]        = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em);\n            }\n        }\n\n        if ($addFilters) {\n            [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($filterMapping);\n\n            if ($filterSql) {\n                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;\n                $whereClauses[]   = $filterSql;\n            }\n        }\n\n        return [$quotedJoinTable, $whereClauses, $params, $types];\n    }\n\n    /**\n     * @param bool $addFilters Whether the filter SQL should be included or not.\n     *\n     * @return mixed[] ordered vector:\n     *                - quoted join table name\n     *                - where clauses to be added for filtering\n     *                - parameters to be bound for filtering\n     *                - types of the parameters to be bound for filtering\n     * @phpstan-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}\n     */\n    private function getJoinTableRestrictions(\n        PersistentCollection $collection,\n        object $element,\n        bool $addFilters,\n    ): array {\n        $filterMapping = $this->getMapping($collection);\n        $mapping       = $filterMapping;\n\n        if (! $mapping->isOwningSide()) {\n            $sourceClass = $this->em->getClassMetadata($mapping->targetEntity);\n            $targetClass = $this->em->getClassMetadata($mapping->sourceEntity);\n            $sourceId    = $this->uow->getEntityIdentifier($element);\n            $targetId    = $this->uow->getEntityIdentifier($collection->getOwner());\n        } else {\n            $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n            $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n            $sourceId    = $this->uow->getEntityIdentifier($collection->getOwner());\n            $targetId    = $this->uow->getEntityIdentifier($element);\n        }\n\n        $mapping = $this->em->getMetadataFactory()->getOwningSide($mapping);\n\n        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);\n        $whereClauses    = [];\n        $params          = [];\n        $types           = [];\n\n        foreach ($mapping->joinTableColumns as $joinTableColumn) {\n            $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';\n\n            if (isset($mapping->relationToTargetKeyColumns[$joinTableColumn])) {\n                $targetColumn = $mapping->relationToTargetKeyColumns[$joinTableColumn];\n                $params[]     = $targetId[$targetClass->getFieldForColumn($targetColumn)];\n                $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);\n\n                continue;\n            }\n\n            // relationToSourceKeyColumns\n            $targetColumn = $mapping->relationToSourceKeyColumns[$joinTableColumn];\n            $params[]     = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];\n            $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);\n        }\n\n        if ($addFilters) {\n            $quotedJoinTable .= ' t';\n\n            [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($filterMapping);\n\n            if ($filterSql) {\n                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;\n                $whereClauses[]   = $filterSql;\n            }\n        }\n\n        return [$quotedJoinTable, $whereClauses, $params, $types];\n    }\n\n    /**\n     * Expands Criteria Parameters by walking the expressions and grabbing all\n     * parameters and types from it.\n     *\n     * @return mixed[][]\n     */\n    private function expandCriteriaParameters(Criteria $criteria): array\n    {\n        $expression = $criteria->getWhereExpression();\n\n        if ($expression === null) {\n            return [];\n        }\n\n        $valueVisitor = new SqlValueVisitor();\n\n        $valueVisitor->dispatch($expression);\n\n        [, $types] = $valueVisitor->getParamsAndTypes();\n\n        return $types;\n    }\n\n    private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string\n    {\n        $orderings = $criteria->orderings();\n        if ($orderings) {\n            $orderBy = [];\n            foreach ($orderings as $name => $direction) {\n                $field     = $this->quoteStrategy->getColumnName(\n                    $name,\n                    $targetClass,\n                    $this->platform,\n                );\n                $orderBy[] = $field . ' ' . $direction->value;\n            }\n\n            return ' ORDER BY ' . implode(', ', $orderBy);\n        }\n\n        return '';\n    }\n\n    /** @throws DBALException */\n    private function getLimitSql(Criteria $criteria): string\n    {\n        $limit  = $criteria->getMaxResults();\n        $offset = $criteria->getFirstResult();\n\n        return $this->platform->modifyLimitQuery('', $limit, $offset ?? 0);\n    }\n\n    private function getMapping(PersistentCollection $collection): AssociationMapping&ManyToManyAssociationMapping\n    {\n        $mapping = $collection->getMapping();\n\n        assert($mapping instanceof ManyToManyAssociationMapping);\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Collection/OneToManyPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Collection;\n\nuse BadMethodCallException;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\Exception as DBALException;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityNotFoundException;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\n\nuse function array_fill;\nuse function array_keys;\nuse function array_reverse;\nuse function array_values;\nuse function assert;\nuse function count;\nuse function implode;\nuse function is_int;\nuse function is_string;\n\n/**\n * Persister for one-to-many collections.\n */\nclass OneToManyPersister extends AbstractCollectionPersister\n{\n    public function delete(PersistentCollection $collection): void\n    {\n        // The only valid case here is when you have weak entities. In this\n        // scenario, you have @OneToMany with orphanRemoval=true, and replacing\n        // the entire collection with a new would trigger this operation.\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->orphanRemoval) {\n            // Handling non-orphan removal should never happen, as @OneToMany\n            // can only be inverse side. For owning side one to many, it is\n            // required to have a join table, which would classify as a ManyToManyPersister.\n            return;\n        }\n\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n\n        $targetClass->isInheritanceTypeJoined()\n            ? $this->deleteJoinedEntityCollection($collection)\n            : $this->deleteEntityCollection($collection);\n    }\n\n    public function update(PersistentCollection $collection): void\n    {\n        // This can never happen. One to many can only be inverse side.\n        // For owning side one to many, it is required to have a join table,\n        // then classifying it as a ManyToManyPersister.\n        return;\n    }\n\n    public function get(PersistentCollection $collection, mixed $index): object|null\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isIndexed()) {\n            throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');\n        }\n\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        return $persister->load(\n            [\n                $mapping->mappedBy  => $collection->getOwner(),\n                $mapping->indexBy() => $index,\n            ],\n            null,\n            $mapping,\n            [],\n            null,\n            1,\n        );\n    }\n\n    public function count(PersistentCollection $collection): int\n    {\n        $mapping   = $this->getMapping($collection);\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        // only works with single id identifier entities. Will throw an\n        // exception in Entity Persisters if that is not the case for the\n        // 'mappedBy' field.\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));\n\n        return $persister->count($criteria);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array\n    {\n        $mapping   = $this->getMapping($collection);\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        return $persister->getOneToManyCollection($mapping, $collection->getOwner(), $offset, $length);\n    }\n\n    public function containsKey(PersistentCollection $collection, mixed $key): bool\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isIndexed()) {\n            throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');\n        }\n\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        // only works with single id identifier entities. Will throw an\n        // exception in Entity Persisters if that is not the case for the\n        // 'mappedBy' field.\n        $criteria = Criteria::create(true);\n\n        $criteria->andWhere(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));\n        $criteria->andWhere(Criteria::expr()->eq($mapping->indexBy(), $key));\n\n        return (bool) $persister->count($criteria);\n    }\n\n    public function contains(PersistentCollection $collection, object $element): bool\n    {\n        if (! $this->isValidEntityState($element)) {\n            return false;\n        }\n\n        $mapping   = $this->getMapping($collection);\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n\n        // only works with single id identifier entities. Will throw an\n        // exception in Entity Persisters if that is not the case for the\n        // 'mappedBy' field.\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));\n\n        return $persister->exists($element, $criteria);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array\n    {\n        throw new BadMethodCallException('Filtering a collection by Criteria is not supported by this CollectionPersister.');\n    }\n\n    /**\n     * @throws DBALException\n     * @throws EntityNotFoundException\n     * @throws MappingException\n     */\n    private function deleteEntityCollection(PersistentCollection $collection): int\n    {\n        $mapping     = $this->getMapping($collection);\n        $identifier  = $this->uow->getEntityIdentifier($collection->getOwner());\n        $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n        $columns     = [];\n        $parameters  = [];\n        $types       = [];\n\n        foreach ($this->em->getMetadataFactory()->getOwningSide($mapping)->joinColumns as $joinColumn) {\n            $columns[]    = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n            $parameters[] = $identifier[$sourceClass->getFieldForColumn($joinColumn->referencedColumnName)];\n            $types[]      = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $sourceClass, $this->em);\n        }\n\n        $statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform)\n            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';\n\n        if ($targetClass->isInheritanceTypeSingleTable()) {\n            $discriminatorColumn = $targetClass->getDiscriminatorColumn();\n            $discriminatorValues = $targetClass->discriminatorValue ? [$targetClass->discriminatorValue] : array_keys($targetClass->discriminatorMap);\n            $statement          .= ' AND ' . $discriminatorColumn->name . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')';\n            foreach ($discriminatorValues as $discriminatorValue) {\n                $parameters[] = $discriminatorValue;\n                $types[]      = $discriminatorColumn->type;\n            }\n        }\n\n        $numAffected = $this->conn->executeStatement($statement, $parameters, $types);\n\n        assert(is_int($numAffected));\n\n        return $numAffected;\n    }\n\n    /**\n     * Delete Class Table Inheritance entities.\n     * A temporary table is needed to keep IDs to be deleted in both parent and child class' tables.\n     *\n     * Thanks Steve Ebersole (Hibernate) for idea on how to tackle reliably this scenario, we owe him a beer! =)\n     *\n     * @throws DBALException\n     */\n    private function deleteJoinedEntityCollection(PersistentCollection $collection): int\n    {\n        $mapping     = $this->getMapping($collection);\n        $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);\n        $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n        $rootClass   = $this->em->getClassMetadata($targetClass->rootEntityName);\n\n        // 1) Build temporary table DDL\n        $tempTable         = $this->platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());\n        $idColumnNames     = $rootClass->getIdentifierColumnNames();\n        $idColumnList      = implode(', ', $idColumnNames);\n        $columnDefinitions = [];\n\n        foreach ($idColumnNames as $idColumnName) {\n            $columnDefinitions[$idColumnName] = [\n                'name'    => $idColumnName,\n                'notnull' => true,\n                'type'    => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em)),\n            ];\n        }\n\n        $statement = $this->platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable\n            . ' (' . $this->platform->getColumnDeclarationListSQL($columnDefinitions) . ')';\n\n        $this->conn->executeStatement($statement);\n\n        // 2) Build insert table records into temporary table\n        $query = $this->em->createQuery(\n            ' SELECT t0.' . implode(', t0.', $rootClass->getIdentifierFieldNames())\n            . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping->mappedBy . ' = :owner',\n        )->setParameter('owner', $collection->getOwner());\n\n        $sql = $query->getSQL();\n        assert(is_string($sql));\n        $statement  = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ') ' . $sql;\n        $parameters = array_values($sourceClass->getIdentifierValues($collection->getOwner()));\n        $numDeleted = $this->conn->executeStatement($statement, $parameters);\n\n        // 3) Delete records on each table in the hierarchy\n        $classNames = [...$targetClass->parentClasses, ...[$targetClass->name], ...$targetClass->subClasses];\n\n        foreach (array_reverse($classNames) as $className) {\n            $tableName = $this->quoteStrategy->getTableName($this->em->getClassMetadata($className), $this->platform);\n            $statement = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ')'\n                . ' IN (SELECT ' . $idColumnList . ' FROM ' . $tempTable . ')';\n\n            $this->conn->executeStatement($statement);\n        }\n\n        // 4) Drop temporary table\n        $statement = $this->platform->getDropTemporaryTableSQL($tempTable);\n\n        $this->conn->executeStatement($statement);\n\n        assert(is_int($numDeleted));\n\n        return $numDeleted;\n    }\n\n    private function getMapping(PersistentCollection $collection): OneToManyAssociationMapping\n    {\n        $mapping = $collection->getMapping();\n\n        assert($mapping->isOneToMany());\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Entity/AbstractEntityInheritancePersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\nuse function sprintf;\n\n/**\n * Base class for entity persisters that implement a certain inheritance mapping strategy.\n * All these persisters are assumed to use a discriminator column to discriminate entity\n * types in the hierarchy.\n */\nabstract class AbstractEntityInheritancePersister extends BasicEntityPersister\n{\n    /**\n     * {@inheritDoc}\n     */\n    protected function prepareInsertData(object $entity): array\n    {\n        $data = parent::prepareInsertData($entity);\n\n        // Populate the discriminator column\n        $discColumn                                                        = $this->class->getDiscriminatorColumn();\n        $this->columnTypes[$discColumn->name]                              = $discColumn->type;\n        $data[$this->getDiscriminatorColumnTableName()][$discColumn->name] = $this->class->discriminatorValue;\n\n        return $data;\n    }\n\n    /**\n     * Gets the name of the table that contains the discriminator column.\n     */\n    abstract protected function getDiscriminatorColumnTableName(): string;\n\n    protected function getSelectColumnSQL(string $field, ClassMetadata $class, string $alias = 'r'): string\n    {\n        $tableAlias   = $alias === 'r' ? '' : $alias;\n        $fieldMapping = $class->fieldMappings[$field];\n        $columnAlias  = $this->getSQLColumnAlias($fieldMapping->columnName);\n        $sql          = sprintf(\n            '%s.%s',\n            $this->getSQLTableAlias($class->name, $tableAlias),\n            $this->quoteStrategy->getColumnName($field, $class, $this->platform),\n        );\n\n        $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field, $class->name);\n\n        $type = Type::getType($fieldMapping->type);\n        $sql  = $type->convertToPHPValueSQL($sql, $this->platform);\n\n        return $sql . ' AS ' . $columnAlias;\n    }\n\n    protected function getSelectJoinColumnSQL(string $tableAlias, string $joinColumnName, string $quotedColumnName, string $type): string\n    {\n        $columnAlias = $this->getSQLColumnAlias($joinColumnName);\n\n        $this->currentPersisterContext->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type);\n\n        return $tableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Entity/BasicEntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse BackedEnum;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToManyAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Exception\\CantUseInOperatorOnCompositeKeys;\nuse Doctrine\\ORM\\Persisters\\Exception\\InvalidOrientation;\nuse Doctrine\\ORM\\Persisters\\Exception\\UnrecognizedField;\nuse Doctrine\\ORM\\Persisters\\SqlExpressionVisitor;\nuse Doctrine\\ORM\\Persisters\\SqlValueVisitor;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Repository\\Exception\\InvalidFindByCall;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\nuse Doctrine\\ORM\\Utility\\LockSqlHelper;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse LengthException;\n\nuse function array_combine;\nuse function array_diff_key;\nuse function array_fill;\nuse function array_flip;\nuse function array_keys;\nuse function array_map;\nuse function array_unique;\nuse function array_values;\nuse function assert;\nuse function count;\nuse function implode;\nuse function is_array;\nuse function reset;\nuse function spl_object_id;\nuse function sprintf;\nuse function str_contains;\nuse function strtoupper;\nuse function trim;\n\n/**\n * A BasicEntityPersister maps an entity to a single table in a relational database.\n *\n * A persister is always responsible for a single entity type.\n *\n * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent\n * state of entities onto a relational database when the UnitOfWork is committed,\n * as well as for basic querying of entities and their associations (not DQL).\n *\n * The persisting operations that are invoked during a commit of a UnitOfWork to\n * persist the persistent entity state are:\n *\n *   - {@link addInsert} : To schedule an entity for insertion.\n *   - {@link executeInserts} : To execute all scheduled insertions.\n *   - {@link update} : To update the persistent state of an entity.\n *   - {@link delete} : To delete the persistent state of an entity.\n *\n * As can be seen from the above list, insertions are batched and executed all at once\n * for increased efficiency.\n *\n * The querying operations invoked during a UnitOfWork, either through direct find\n * requests or lazy-loading, are the following:\n *\n *   - {@link load} : Loads (the state of) a single, managed entity.\n *   - {@link loadAll} : Loads multiple, managed entities.\n *   - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading).\n *   - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading).\n *   - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading).\n *\n * The BasicEntityPersister implementation provides the default behavior for\n * persisting and querying entities that are mapped to a single database table.\n *\n * Subclasses can be created to provide custom persisting and querying strategies,\n * i.e. spanning multiple tables.\n */\nclass BasicEntityPersister implements EntityPersister\n{\n    use LockSqlHelper;\n\n    /** @var array<string,string> */\n    private static array $comparisonMap = [\n        Comparison::EQ          => '= %s',\n        Comparison::NEQ         => '!= %s',\n        Comparison::GT          => '> %s',\n        Comparison::GTE         => '>= %s',\n        Comparison::LT          => '< %s',\n        Comparison::LTE         => '<= %s',\n        Comparison::IN          => 'IN (%s)',\n        Comparison::NIN         => 'NOT IN (%s)',\n        Comparison::CONTAINS    => 'LIKE %s',\n        Comparison::STARTS_WITH => 'LIKE %s',\n        Comparison::ENDS_WITH   => 'LIKE %s',\n    ];\n\n    /**\n     * The underlying DBAL Connection of the used EntityManager.\n     */\n    protected Connection $conn;\n\n    /**\n     * The database platform.\n     */\n    protected AbstractPlatform $platform;\n\n    /**\n     * Queued inserts.\n     *\n     * @phpstan-var array<int, object>\n     */\n    protected array $queuedInserts = [];\n\n    /**\n     * The map of column names to DBAL mapping types of all prepared columns used\n     * when INSERTing or UPDATEing an entity.\n     *\n     * @see prepareInsertData($entity)\n     * @see prepareUpdateData($entity)\n     *\n     * @var mixed[]\n     */\n    protected array $columnTypes = [];\n\n    /**\n     * The map of quoted column names.\n     *\n     * @see prepareInsertData($entity)\n     * @see prepareUpdateData($entity)\n     *\n     * @var mixed[]\n     */\n    protected array $quotedColumns = [];\n\n    /**\n     * The quote strategy.\n     */\n    protected QuoteStrategy $quoteStrategy;\n\n    /**\n     * The IdentifierFlattener used for manipulating identifiers\n     */\n    protected readonly IdentifierFlattener $identifierFlattener;\n\n    protected CachedPersisterContext $currentPersisterContext;\n    private readonly CachedPersisterContext $limitsHandlingContext;\n    private readonly CachedPersisterContext $noLimitsContext;\n\n    private string|null $filterHash = null;\n\n    /**\n     * Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager\n     * and persists instances of the class described by the given ClassMetadata descriptor.\n     *\n     * @param ClassMetadata $class Metadata object that describes the mapping of the mapped entity class.\n     */\n    public function __construct(\n        protected EntityManagerInterface $em,\n        protected ClassMetadata $class,\n    ) {\n        $this->conn                  = $em->getConnection();\n        $this->platform              = $this->conn->getDatabasePlatform();\n        $this->quoteStrategy         = $em->getConfiguration()->getQuoteStrategy();\n        $this->identifierFlattener   = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());\n        $this->noLimitsContext       = $this->currentPersisterContext = new CachedPersisterContext(\n            $class,\n            new Query\\ResultSetMapping(),\n            false,\n        );\n        $this->limitsHandlingContext = new CachedPersisterContext(\n            $class,\n            new Query\\ResultSetMapping(),\n            true,\n        );\n    }\n\n    final protected function isFilterHashUpToDate(): bool\n    {\n        return $this->filterHash === $this->em->getFilters()->getHash();\n    }\n\n    final protected function updateFilterHash(): void\n    {\n        $this->filterHash = $this->em->getFilters()->getHash();\n    }\n\n    public function getClassMetadata(): ClassMetadata\n    {\n        return $this->class;\n    }\n\n    public function getResultSetMapping(): ResultSetMapping\n    {\n        return $this->currentPersisterContext->rsm;\n    }\n\n    public function addInsert(object $entity): void\n    {\n        $this->queuedInserts[spl_object_id($entity)] = $entity;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getInserts(): array\n    {\n        return $this->queuedInserts;\n    }\n\n    public function executeInserts(): void\n    {\n        if (! $this->queuedInserts) {\n            return;\n        }\n\n        $uow            = $this->em->getUnitOfWork();\n        $idGenerator    = $this->class->idGenerator;\n        $isPostInsertId = $idGenerator->isPostInsertGenerator();\n\n        $stmt      = $this->conn->prepare($this->getInsertSQL());\n        $tableName = $this->class->getTableName();\n\n        foreach ($this->queuedInserts as $key => $entity) {\n            $insertData = $this->prepareInsertData($entity);\n\n            if (isset($insertData[$tableName])) {\n                $paramIndex = 1;\n\n                foreach ($insertData[$tableName] as $column => $value) {\n                    $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]);\n                }\n            }\n\n            $stmt->executeStatement();\n\n            if ($isPostInsertId) {\n                $generatedId = $idGenerator->generateId($this->em, $entity);\n                $id          = [$this->class->identifier[0] => $generatedId];\n\n                $uow->assignPostInsertId($entity, $generatedId);\n            } else {\n                $id = $this->class->getIdentifierValues($entity);\n            }\n\n            if ($this->class->requiresFetchAfterChange) {\n                $this->assignDefaultVersionAndUpsertableValues($entity, $id);\n            }\n\n            // Unset this queued insert, so that the prepareUpdateData() method (called via prepareInsertData() method)\n            // knows right away (for the next entity already) that the current entity has been written to the database\n            // and no extra updates need to be scheduled to refer to it.\n            //\n            // In \\Doctrine\\ORM\\UnitOfWork::executeInserts(), the UoW already removed entities\n            // from its own list (\\Doctrine\\ORM\\UnitOfWork::$entityInsertions) right after they\n            // were given to our addInsert() method.\n            unset($this->queuedInserts[$key]);\n        }\n    }\n\n    /**\n     * Retrieves the default version value which was created\n     * by the preceding INSERT statement and assigns it back in to the\n     * entities version field if the given entity is versioned.\n     * Also retrieves values of columns marked as 'non insertable' and / or\n     * 'not updatable' and assigns them back to the entities corresponding fields.\n     *\n     * @param mixed[] $id\n     */\n    protected function assignDefaultVersionAndUpsertableValues(object $entity, array $id): void\n    {\n        $values = $this->fetchVersionAndNotUpsertableValues($this->class, $id);\n\n        foreach ($values as $field => $value) {\n            $value = Type::getType($this->class->fieldMappings[$field]->type)->convertToPHPValue($value, $this->platform);\n\n            $this->class->setFieldValue($entity, $field, $value);\n        }\n    }\n\n    /**\n     * Fetches the current version value of a versioned entity and / or the values of fields\n     * marked as 'not insertable' and / or 'not updatable'.\n     *\n     * @param mixed[] $id\n     */\n    protected function fetchVersionAndNotUpsertableValues(ClassMetadata $versionedClass, array $id): mixed\n    {\n        $columnNames = [];\n        foreach ($this->class->fieldMappings as $key => $column) {\n            if (isset($column->generated) || ($this->class->isVersioned && $key === $versionedClass->versionField)) {\n                $columnNames[$key] = $this->quoteStrategy->getColumnName($key, $versionedClass, $this->platform);\n            }\n        }\n\n        $tableName  = $this->quoteStrategy->getTableName($versionedClass, $this->platform);\n        $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform);\n\n        // FIXME: Order with composite keys might not be correct\n        $sql = 'SELECT ' . implode(', ', $columnNames)\n            . ' FROM ' . $tableName\n            . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';\n\n        $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id);\n\n        $values = $this->conn->fetchNumeric(\n            $sql,\n            array_values($flatId),\n            $this->extractIdentifierTypes($id, $versionedClass),\n        );\n\n        if ($values === false) {\n            throw new LengthException('Unexpected empty result for database query.');\n        }\n\n        $values = array_combine(array_keys($columnNames), $values);\n\n        if (! $values) {\n            throw new LengthException('Unexpected number of database columns.');\n        }\n\n        return $values;\n    }\n\n    /**\n     * @param mixed[] $id\n     *\n     * @return list<ParameterType|int|string>\n     * @phpstan-return list<ParameterType::*|ArrayParameterType::*|string>\n     */\n    final protected function extractIdentifierTypes(array $id, ClassMetadata $versionedClass): array\n    {\n        $types = [];\n\n        foreach ($id as $field => $value) {\n            $types = [...$types, ...PersisterHelper::inferParameterTypes($field, $value, $versionedClass, $this->em)];\n        }\n\n        return $types;\n    }\n\n    public function update(object $entity): void\n    {\n        $tableName  = $this->class->getTableName();\n        $updateData = $this->prepareUpdateData($entity);\n\n        if (! isset($updateData[$tableName])) {\n            return;\n        }\n\n        $data = $updateData[$tableName];\n\n        if (! $data) {\n            return;\n        }\n\n        $isVersioned     = $this->class->isVersioned;\n        $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform);\n\n        $this->updateTable($entity, $quotedTableName, $data, $isVersioned);\n\n        if ($this->class->requiresFetchAfterChange) {\n            $id = $this->class->getIdentifierValues($entity);\n\n            $this->assignDefaultVersionAndUpsertableValues($entity, $id);\n        }\n    }\n\n    /**\n     * Performs an UPDATE statement for an entity on a specific table.\n     * The UPDATE can optionally be versioned, which requires the entity to have a version field.\n     *\n     * @param object  $entity          The entity object being updated.\n     * @param string  $quotedTableName The quoted name of the table to apply the UPDATE on.\n     * @param mixed[] $updateData      The map of columns to update (column => value).\n     * @param bool    $versioned       Whether the UPDATE should be versioned.\n     *\n     * @throws UnrecognizedField\n     * @throws OptimisticLockException\n     */\n    final protected function updateTable(\n        object $entity,\n        string $quotedTableName,\n        array $updateData,\n        bool $versioned = false,\n    ): void {\n        $set    = [];\n        $types  = [];\n        $params = [];\n\n        foreach ($updateData as $columnName => $value) {\n            $placeholder = '?';\n            $column      = $columnName;\n\n            switch (true) {\n                case isset($this->class->fieldNames[$columnName]):\n                    $fieldName = $this->class->fieldNames[$columnName];\n                    $column    = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform);\n\n                    if (isset($this->class->fieldMappings[$fieldName])) {\n                        $type        = Type::getType($this->columnTypes[$columnName]);\n                        $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);\n                    }\n\n                    break;\n\n                case isset($this->quotedColumns[$columnName]):\n                    $column = $this->quotedColumns[$columnName];\n\n                    break;\n            }\n\n            $params[] = $value;\n            $set[]    = $column . ' = ' . $placeholder;\n            $types[]  = $this->columnTypes[$columnName];\n        }\n\n        $where      = [];\n        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);\n\n        foreach ($this->class->identifier as $idField) {\n            if (! isset($this->class->associationMappings[$idField])) {\n                $params[] = $identifier[$idField];\n                $types[]  = $this->class->fieldMappings[$idField]->type;\n                $where[]  = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform);\n\n                continue;\n            }\n\n            assert($this->class->associationMappings[$idField]->isToOneOwningSide());\n\n            $params[] = $identifier[$idField];\n            $where[]  = $this->quoteStrategy->getJoinColumnName(\n                $this->class->associationMappings[$idField]->joinColumns[0],\n                $this->class,\n                $this->platform,\n            );\n\n            $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]->targetEntity);\n            $targetType    = PersisterHelper::getTypeOfField($targetMapping->identifier[0], $targetMapping, $this->em);\n\n            if ($targetType === []) {\n                throw UnrecognizedField::byFullyQualifiedName($this->class->name, $targetMapping->identifier[0]);\n            }\n\n            $types[] = reset($targetType);\n        }\n\n        if ($versioned) {\n            $versionField = $this->class->versionField;\n            assert($versionField !== null);\n            $versionFieldType = $this->class->fieldMappings[$versionField]->type;\n            $versionColumn    = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform);\n\n            $where[]  = $versionColumn;\n            $types[]  = $this->class->fieldMappings[$versionField]->type;\n            $params[] = $this->class->propertyAccessors[$versionField]->getValue($entity);\n\n            switch ($versionFieldType) {\n                case Types::SMALLINT:\n                case Types::INTEGER:\n                case Types::BIGINT:\n                    $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';\n                    break;\n\n                case Types::DATETIME_MUTABLE:\n                    $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';\n                    break;\n            }\n        }\n\n        $sql = 'UPDATE ' . $quotedTableName\n             . ' SET ' . implode(', ', $set)\n             . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';\n\n        $result = $this->conn->executeStatement($sql, $params, $types);\n\n        if ($versioned && ! $result) {\n            throw OptimisticLockException::lockFailed($entity);\n        }\n    }\n\n    /**\n     * @param array<mixed> $identifier\n     * @param string[]     $types\n     *\n     * @todo Add check for platform if it supports foreign keys/cascading.\n     */\n    protected function deleteJoinTableRecords(array $identifier, array $types): void\n    {\n        foreach ($this->class->associationMappings as $mapping) {\n            if (! $mapping->isManyToMany() || $mapping->isOnDeleteCascade) {\n                continue;\n            }\n\n            // @Todo this only covers scenarios with no inheritance or of the same level. Is there something\n            // like self-referential relationship between different levels of an inheritance hierarchy? I hope not!\n            $selfReferential = ($mapping->targetEntity === $mapping->sourceEntity);\n            $class           = $this->class;\n            $association     = $mapping;\n            $otherColumns    = [];\n            $otherKeys       = [];\n            $keys            = [];\n\n            if (! $mapping->isOwningSide()) {\n                $class = $this->em->getClassMetadata($mapping->targetEntity);\n            }\n\n            $association = $this->em->getMetadataFactory()->getOwningSide($association);\n            $joinColumns = $mapping->isOwningSide()\n                ? $association->joinTable->joinColumns\n                : $association->joinTable->inverseJoinColumns;\n\n            if ($selfReferential) {\n                $otherColumns = ! $mapping->isOwningSide()\n                    ? $association->joinTable->joinColumns\n                    : $association->joinTable->inverseJoinColumns;\n            }\n\n            foreach ($joinColumns as $joinColumn) {\n                $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n            }\n\n            foreach ($otherColumns as $joinColumn) {\n                $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n            }\n\n            $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform);\n\n            $this->conn->delete($joinTableName, array_combine($keys, $identifier), $types);\n\n            if ($selfReferential) {\n                $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier), $types);\n            }\n        }\n    }\n\n    public function delete(object $entity): bool\n    {\n        $class      = $this->class;\n        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);\n        $tableName  = $this->quoteStrategy->getTableName($class, $this->platform);\n        $idColumns  = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform);\n        $id         = array_combine($idColumns, $identifier);\n        $types      = $this->getClassIdentifiersTypes($class);\n\n        $this->deleteJoinTableRecords($identifier, $types);\n\n        return (bool) $this->conn->delete($tableName, $id, $types);\n    }\n\n    /**\n     * Prepares the changeset of an entity for database insertion (UPDATE).\n     *\n     * The changeset is obtained from the currently running UnitOfWork.\n     *\n     * During this preparation the array that is passed as the second parameter is filled with\n     * <columnName> => <value> pairs, grouped by table name.\n     *\n     * Example:\n     * <code>\n     * array(\n     *    'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...),\n     *    'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...),\n     *    ...\n     * )\n     * </code>\n     *\n     * @param object $entity   The entity for which to prepare the data.\n     * @param bool   $isInsert Whether the data to be prepared refers to an insert statement.\n     *\n     * @return mixed[][] The prepared data.\n     * @phpstan-return array<string, array<array-key, mixed|null>>\n     */\n    protected function prepareUpdateData(object $entity, bool $isInsert = false): array\n    {\n        $versionField = null;\n        $result       = [];\n        $uow          = $this->em->getUnitOfWork();\n\n        $versioned = $this->class->isVersioned;\n        if ($versioned !== false) {\n            $versionField = $this->class->versionField;\n        }\n\n        foreach ($uow->getEntityChangeSet($entity) as $field => $change) {\n            if (isset($versionField) && $versionField === $field) {\n                continue;\n            }\n\n            if (isset($this->class->embeddedClasses[$field])) {\n                continue;\n            }\n\n            $newVal = $change[1];\n\n            if (! isset($this->class->associationMappings[$field])) {\n                $fieldMapping = $this->class->fieldMappings[$field];\n                $columnName   = $fieldMapping->columnName;\n\n                if (! $isInsert && isset($fieldMapping->notUpdatable)) {\n                    continue;\n                }\n\n                if ($isInsert && isset($fieldMapping->notInsertable)) {\n                    continue;\n                }\n\n                $this->columnTypes[$columnName] = $fieldMapping->type;\n\n                $result[$this->getOwningTable($field)][$columnName] = $newVal;\n\n                continue;\n            }\n\n            $assoc = $this->class->associationMappings[$field];\n\n            // Only owning side of x-1 associations can have a FK column.\n            if (! $assoc->isToOneOwningSide()) {\n                continue;\n            }\n\n            if ($newVal !== null) {\n                $oid = spl_object_id($newVal);\n\n                // If the associated entity $newVal is not yet persisted and/or does not yet have\n                // an ID assigned, we must set $newVal = null. This will insert a null value and\n                // schedule an extra update on the UnitOfWork.\n                //\n                // This gives us extra time to a) possibly obtain a database-generated identifier\n                // value for $newVal, and b) insert $newVal into the database before the foreign\n                // key reference is being made.\n                //\n                // When looking at $this->queuedInserts and $uow->isScheduledForInsert, be aware\n                // of the implementation details that our own executeInserts() method will remove\n                // entities from the former as soon as the insert statement has been executed and\n                // a post-insert ID has been assigned (if necessary), and that the UnitOfWork has\n                // already removed entities from its own list at the time they were passed to our\n                // addInsert() method.\n                //\n                // Then, there is one extra exception we can make: An entity that references back to itself\n                // _and_ uses an application-provided ID (the \"NONE\" generator strategy) also does not\n                // need the extra update, although it is still in the list of insertions itself.\n                // This looks like a minor optimization at first, but is the capstone for being able to\n                // use non-NULLable, self-referencing associations in applications that provide IDs (like UUIDs).\n                if (\n                    (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal))\n                    && ! ($newVal === $entity && $this->class->isIdentifierNatural())\n                ) {\n                    $uow->scheduleExtraUpdate($entity, [$field => [null, $newVal]]);\n\n                    $newVal = null;\n                }\n            }\n\n            $newValId = null;\n\n            if ($newVal !== null) {\n                $newValId = $uow->getEntityIdentifier($newVal);\n            }\n\n            $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n            $owningTable = $this->getOwningTable($field);\n\n            foreach ($assoc->joinColumns as $joinColumn) {\n                $sourceColumn = $joinColumn->name;\n                $targetColumn = $joinColumn->referencedColumnName;\n                $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n\n                $this->quotedColumns[$sourceColumn] = $quotedColumn;\n                $this->columnTypes[$sourceColumn]   = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);\n\n                $newValue                            = $newValId ? $newValId[$targetClass->getFieldForColumn($targetColumn)] : null;\n                $result[$owningTable][$sourceColumn] = $newValue instanceof BackedEnum ? $newValue->value : $newValue;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Prepares the data changeset of a managed entity for database insertion (initial INSERT).\n     * The changeset of the entity is obtained from the currently running UnitOfWork.\n     *\n     * The default insert data preparation is the same as for updates.\n     *\n     * @see prepareUpdateData\n     *\n     * @param object $entity The entity for which to prepare the data.\n     *\n     * @return mixed[][] The prepared data for the tables to update.\n     * @phpstan-return array<string, mixed[]>\n     */\n    protected function prepareInsertData(object $entity): array\n    {\n        return $this->prepareUpdateData($entity, true);\n    }\n\n    public function getOwningTable(string $fieldName): string\n    {\n        return $this->class->getTableName();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function load(\n        array $criteria,\n        object|null $entity = null,\n        AssociationMapping|null $assoc = null,\n        array $hints = [],\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        array|null $orderBy = null,\n    ): object|null {\n        $this->switchPersisterContext(null, $limit);\n\n        $sql              = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy);\n        [$params, $types] = $this->expandParameters($criteria);\n        $stmt             = $this->conn->executeQuery($sql, $params, $types);\n\n        if ($entity !== null) {\n            $hints[Query::HINT_REFRESH]        = true;\n            $hints[Query::HINT_REFRESH_ENTITY] = $entity;\n        }\n\n        $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);\n        $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints);\n\n        return $entities ? $entities[0] : null;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadById(array $identifier, object|null $entity = null): object|null\n    {\n        return $this->load($identifier, $entity);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null\n    {\n        $foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc->targetEntity);\n        if ($foundEntity !== false) {\n            return $foundEntity;\n        }\n\n        $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n\n        if ($assoc->isOwningSide()) {\n            $isInverseSingleValued = $assoc->inversedBy !== null && ! $targetClass->isCollectionValuedAssociation($assoc->inversedBy);\n\n            // Mark inverse side as fetched in the hints, otherwise the UoW would\n            // try to load it in a separate query (remember: to-one inverse sides can not be lazy).\n            $hints = [];\n\n            if ($isInverseSingleValued) {\n                $hints['fetched']['r'][$assoc->inversedBy] = true;\n            }\n\n            $targetEntity = $this->load($identifier, null, $assoc, $hints);\n\n            // Complete bidirectional association, if necessary\n            if ($targetEntity !== null && $isInverseSingleValued) {\n                $targetClass->propertyAccessors[$assoc->inversedBy]->setValue($targetEntity, $sourceEntity);\n            }\n\n            return $targetEntity;\n        }\n\n        assert(isset($assoc->mappedBy));\n        $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity);\n        $owningAssoc = $targetClass->getAssociationMapping($assoc->mappedBy);\n        assert($owningAssoc->isOneToOneOwningSide());\n\n        $computedIdentifier = [];\n\n        /** @var array<string,mixed>|null $sourceEntityData */\n        $sourceEntityData = null;\n\n        // TRICKY: since the association is specular source and target are flipped\n        foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {\n            if (! isset($sourceClass->fieldNames[$sourceKeyColumn])) {\n                // The likely case here is that the column is a join column\n                // in an association mapping. However, there is no guarantee\n                // at this point that a corresponding (generally identifying)\n                // association has been mapped in the source entity. To handle\n                // this case we directly reference the column-keyed data used\n                // to initialize the source entity before throwing an exception.\n                $resolvedSourceData = false;\n                if (! isset($sourceEntityData)) {\n                    $sourceEntityData = $this->em->getUnitOfWork()->getOriginalEntityData($sourceEntity);\n                }\n\n                if (isset($sourceEntityData[$sourceKeyColumn])) {\n                    $dataValue = $sourceEntityData[$sourceKeyColumn];\n                    if ($dataValue !== null) {\n                        $resolvedSourceData                                                    = true;\n                        $computedIdentifier[$targetClass->getFieldForColumn($targetKeyColumn)] =\n                            $dataValue;\n                    }\n                }\n\n                if (! $resolvedSourceData) {\n                    throw MappingException::joinColumnMustPointToMappedField(\n                        $sourceClass->name,\n                        $sourceKeyColumn,\n                    );\n                }\n            } else {\n                $computedIdentifier[$targetClass->getFieldForColumn($targetKeyColumn)] =\n                    $sourceClass->propertyAccessors[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);\n            }\n        }\n\n        $targetEntity = $this->load($computedIdentifier, null, $assoc);\n\n        if ($targetEntity !== null) {\n            $targetClass->setFieldValue($targetEntity, $assoc->mappedBy, $sourceEntity);\n        }\n\n        return $targetEntity;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void\n    {\n        $sql              = $this->getSelectSQL($id, null, $lockMode);\n        [$params, $types] = $this->expandParameters($id);\n        $stmt             = $this->conn->executeQuery($sql, $params, $types);\n\n        $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT);\n        $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [Query::HINT_REFRESH => true]);\n    }\n\n    public function count(array|Criteria $criteria = []): int\n    {\n        $sql = $this->getCountSQL($criteria);\n\n        [$params, $types] = $criteria instanceof Criteria\n            ? $this->expandCriteriaParameters($criteria)\n            : $this->expandParameters($criteria);\n\n        $count = (int) $this->conn->executeQuery($sql, $params, $types)->fetchOne();\n        assert($count >= 0);\n\n        return $count;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadCriteria(Criteria $criteria): array\n    {\n        $orderBy = array_map(\n            static fn (Order $order): string => $order->value,\n            $criteria->orderings(),\n        );\n        $limit   = $criteria->getMaxResults();\n        $offset  = $criteria->getFirstResult();\n        $query   = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);\n\n        [$params, $types] = $this->expandCriteriaParameters($criteria);\n\n        $stmt     = $this->conn->executeQuery($query, $params, $types);\n        $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);\n\n        return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true]);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function expandCriteriaParameters(Criteria $criteria): array\n    {\n        $expression = $criteria->getWhereExpression();\n        $sqlParams  = [];\n        $sqlTypes   = [];\n\n        if ($expression === null) {\n            return [$sqlParams, $sqlTypes];\n        }\n\n        $valueVisitor = new SqlValueVisitor();\n\n        $valueVisitor->dispatch($expression);\n\n        [, $types] = $valueVisitor->getParamsAndTypes();\n\n        foreach ($types as $type) {\n            [$field, $value, $operator] = $type;\n\n            if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {\n                continue;\n            }\n\n            if ($operator === Comparison::IN || $operator === Comparison::NIN) {\n                if (! is_array($value)) {\n                    $value = [$value];\n                }\n\n                foreach ($value as $item) {\n                    if ($item === null) {\n                        /*\n                         * Compare this to how \\Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister::getSelectConditionStatementSQL\n                         * creates the \"[NOT] IN (...)\" expression - for NULL values, it does _not_ insert a placeholder in the\n                         * SQL and instead adds an extra ... OR ... IS NULL condition. So we need to skip NULL values here as\n                         * well to create a parameters list that matches the SQL.\n                         */\n                        continue;\n                    }\n\n                    $sqlParams = [...$sqlParams, ...PersisterHelper::convertToParameterValue($item, $this->em)];\n                    $sqlTypes  = [...$sqlTypes, ...PersisterHelper::inferParameterTypes($field, $item, $this->class, $this->em)];\n                }\n\n                continue;\n            }\n\n            $sqlParams = [...$sqlParams, ...PersisterHelper::convertToParameterValue($value, $this->em)];\n            $sqlTypes  = [...$sqlTypes, ...PersisterHelper::inferParameterTypes($field, $value, $this->class, $this->em)];\n        }\n\n        return [$sqlParams, $sqlTypes];\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadAll(\n        array $criteria = [],\n        array|null $orderBy = null,\n        int|null $limit = null,\n        int|null $offset = null,\n    ): array {\n        $this->switchPersisterContext($offset, $limit);\n\n        $sql              = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);\n        [$params, $types] = $this->expandParameters($criteria);\n        $stmt             = $this->conn->executeQuery($sql, $params, $types);\n\n        $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);\n\n        return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true]);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getManyToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array {\n        assert($assoc->isManyToMany());\n        $this->switchPersisterContext($offset, $limit);\n\n        $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);\n\n        return $this->loadArrayFromResult($assoc, $stmt);\n    }\n\n    /**\n     * Loads an array of entities from a given DBAL statement.\n     *\n     * @return mixed[]\n     */\n    private function loadArrayFromResult(AssociationMapping $assoc, Result $stmt): array\n    {\n        $rsm   = $this->currentPersisterContext->rsm;\n        $hints = [UnitOfWork::HINT_DEFEREAGERLOAD => true];\n\n        if ($assoc->isIndexed()) {\n            $rsm = clone $this->currentPersisterContext->rsm; // this is necessary because the \"default rsm\" should be changed.\n            $rsm->addIndexBy('r', $assoc->indexBy());\n        }\n\n        return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints);\n    }\n\n    /**\n     * Hydrates a collection from a given DBAL statement.\n     *\n     * @return mixed[]\n     */\n    private function loadCollectionFromStatement(\n        AssociationMapping $assoc,\n        Result $stmt,\n        PersistentCollection $coll,\n    ): array {\n        $rsm   = $this->currentPersisterContext->rsm;\n        $hints = [\n            UnitOfWork::HINT_DEFEREAGERLOAD => true,\n            'collection' => $coll,\n        ];\n\n        if ($assoc->isIndexed()) {\n            $rsm = clone $this->currentPersisterContext->rsm; // this is necessary because the \"default rsm\" should be changed.\n            $rsm->addIndexBy('r', $assoc->indexBy());\n        }\n\n        return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function loadManyToManyCollection(AssociationMapping $assoc, object $sourceEntity, PersistentCollection $collection): array\n    {\n        assert($assoc->isManyToMany());\n        $stmt = $this->getManyToManyStatement($assoc, $sourceEntity);\n\n        return $this->loadCollectionFromStatement($assoc, $stmt, $collection);\n    }\n\n    /** @throws MappingException */\n    private function getManyToManyStatement(\n        AssociationMapping&ManyToManyAssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): Result {\n        $this->switchPersisterContext($offset, $limit);\n\n        $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity);\n        $class       = $sourceClass;\n        $association = $assoc;\n        $criteria    = [];\n        $parameters  = [];\n\n        if (! $assoc->isOwningSide()) {\n            $class = $this->em->getClassMetadata($assoc->targetEntity);\n        }\n\n        $association = $this->em->getMetadataFactory()->getOwningSide($assoc);\n        $joinColumns = $assoc->isOwningSide()\n            ? $association->joinTable->joinColumns\n            : $association->joinTable->inverseJoinColumns;\n\n        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform);\n\n        foreach ($joinColumns as $joinColumn) {\n            $sourceKeyColumn = $joinColumn->referencedColumnName;\n            $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n\n            switch (true) {\n                case $sourceClass->containsForeignIdentifier:\n                    $field = $sourceClass->getFieldForColumn($sourceKeyColumn);\n                    $value = $sourceClass->propertyAccessors[$field]->getValue($sourceEntity);\n\n                    if (isset($sourceClass->associationMappings[$field])) {\n                        $value = $this->em->getUnitOfWork()->getEntityIdentifier($value);\n                        $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]->targetEntity)->identifier[0]];\n                    }\n\n                    break;\n\n                case isset($sourceClass->fieldNames[$sourceKeyColumn]):\n                    $field = $sourceClass->fieldNames[$sourceKeyColumn];\n                    $value = $sourceClass->propertyAccessors[$field]->getValue($sourceEntity);\n\n                    break;\n\n                default:\n                    throw MappingException::joinColumnMustPointToMappedField(\n                        $sourceClass->name,\n                        $sourceKeyColumn,\n                    );\n            }\n\n            $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value;\n            $parameters[]                                        = [\n                'value' => $value,\n                'field' => $field,\n                'class' => $sourceClass,\n            ];\n        }\n\n        $sql              = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);\n        [$params, $types] = $this->expandToManyParameters($parameters);\n\n        return $this->conn->executeQuery($sql, $params, $types);\n    }\n\n    public function getSelectSQL(\n        array|Criteria $criteria,\n        AssociationMapping|null $assoc = null,\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        int|null $offset = null,\n        array|null $orderBy = null,\n    ): string {\n        $this->switchPersisterContext($offset, $limit);\n\n        $joinSql    = '';\n        $orderBySql = '';\n\n        if ($assoc !== null && $assoc->isManyToMany()) {\n            $joinSql = $this->getSelectManyToManyJoinSQL($assoc);\n        }\n\n        if ($assoc !== null && $assoc->isOrdered()) {\n            $orderBy = $assoc->orderBy();\n        }\n\n        if ($orderBy) {\n            $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name));\n        }\n\n        $conditionSql = $criteria instanceof Criteria\n            ? $this->getSelectConditionCriteriaSQL($criteria)\n            : $this->getSelectConditionSQL($criteria, $assoc);\n\n        $lockSql = match ($lockMode) {\n            LockMode::PESSIMISTIC_READ => ' ' . $this->getReadLockSQL($this->platform),\n            LockMode::PESSIMISTIC_WRITE => ' ' . $this->getWriteLockSQL($this->platform),\n            default => '',\n        };\n\n        $columnList = $this->getSelectColumnsSQL();\n        $tableAlias = $this->getSQLTableAlias($this->class->name);\n        $filterSql  = $this->generateFilterConditionSQL($this->class, $tableAlias);\n        $tableName  = $this->quoteStrategy->getTableName($this->class, $this->platform);\n\n        if ($filterSql !== '') {\n            $conditionSql = $conditionSql\n                ? $conditionSql . ' AND ' . $filterSql\n                : $filterSql;\n        }\n\n        $select = 'SELECT ' . $columnList;\n        $from   = ' FROM ' . $tableName . ' ' . $tableAlias;\n        $join   = $this->currentPersisterContext->selectJoinSql . $joinSql;\n        $where  = ($conditionSql ? ' WHERE ' . $conditionSql : '');\n        $lock   = $this->platform->appendLockHint($from, $lockMode ?? LockMode::NONE);\n        $query  = $select\n            . $lock\n            . $join\n            . $where\n            . $orderBySql;\n\n        return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql;\n    }\n\n    public function getCountSQL(array|Criteria $criteria = []): string\n    {\n        $tableName  = $this->quoteStrategy->getTableName($this->class, $this->platform);\n        $tableAlias = $this->getSQLTableAlias($this->class->name);\n\n        $conditionSql = $criteria instanceof Criteria\n            ? $this->getSelectConditionCriteriaSQL($criteria)\n            : $this->getSelectConditionSQL($criteria);\n\n        $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias);\n\n        if ($filterSql !== '') {\n            $conditionSql = $conditionSql\n                ? $conditionSql . ' AND ' . $filterSql\n                : $filterSql;\n        }\n\n        return 'SELECT COUNT(*) '\n            . 'FROM ' . $tableName . ' ' . $tableAlias\n            . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql);\n    }\n\n    /**\n     * Gets the ORDER BY SQL snippet for ordered collections.\n     *\n     * @phpstan-param array<string, string> $orderBy\n     *\n     * @throws InvalidOrientation\n     * @throws InvalidFindByCall\n     * @throws UnrecognizedField\n     */\n    final protected function getOrderBySQL(array $orderBy, string $baseTableAlias): string\n    {\n        $orderByList = [];\n\n        foreach ($orderBy as $fieldName => $orientation) {\n            $orientation = strtoupper(trim($orientation));\n\n            if ($orientation !== 'ASC' && $orientation !== 'DESC') {\n                throw InvalidOrientation::fromClassNameAndField($this->class->name, $fieldName);\n            }\n\n            if (isset($this->class->fieldMappings[$fieldName])) {\n                $tableAlias = isset($this->class->fieldMappings[$fieldName]->inherited)\n                    ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]->inherited)\n                    : $baseTableAlias;\n\n                $columnName    = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform);\n                $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation;\n\n                continue;\n            }\n\n            if (isset($this->class->associationMappings[$fieldName])) {\n                $association = $this->class->associationMappings[$fieldName];\n                if (! $association->isOwningSide()) {\n                    throw InvalidFindByCall::fromInverseSideUsage($this->class->name, $fieldName);\n                }\n\n                assert($association->isToOneOwningSide());\n\n                $tableAlias = isset($association->inherited)\n                    ? $this->getSQLTableAlias($association->inherited)\n                    : $baseTableAlias;\n\n                foreach ($association->joinColumns as $joinColumn) {\n                    $columnName    = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n                    $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation;\n                }\n\n                continue;\n            }\n\n            throw UnrecognizedField::byFullyQualifiedName($this->class->name, $fieldName);\n        }\n\n        return ' ORDER BY ' . implode(', ', $orderByList);\n    }\n\n    /**\n     * Gets the SQL fragment with the list of columns to select when querying for\n     * an entity in this persister.\n     *\n     * Subclasses should override this method to alter or change the select column\n     * list SQL fragment. Note that in the implementation of BasicEntityPersister\n     * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}.\n     * Subclasses may or may not do the same.\n     */\n    protected function getSelectColumnsSQL(): string\n    {\n        if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {\n            return $this->currentPersisterContext->selectColumnListSql;\n        }\n\n        $columnList = [];\n        $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); // r for root\n\n        // Add regular columns to select list\n        foreach ($this->class->fieldNames as $field) {\n            $columnList[] = $this->getSelectColumnSQL($field, $this->class);\n        }\n\n        $this->currentPersisterContext->selectJoinSql = '';\n        $eagerAliasCounter                            = 0;\n\n        foreach ($this->class->associationMappings as $assocField => $assoc) {\n            $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class);\n\n            if ($assocColumnSQL) {\n                $columnList[] = $assocColumnSQL;\n            }\n\n            $isAssocToOneInverseSide = $assoc->isToOne() && ! $assoc->isOwningSide();\n            $isAssocFromOneEager     = $assoc->isToOne() && $assoc->fetch === ClassMetadata::FETCH_EAGER;\n\n            if (! ($isAssocFromOneEager || $isAssocToOneInverseSide)) {\n                continue;\n            }\n\n            if ($assoc->isToMany() && $this->currentPersisterContext->handlesLimits) {\n                continue;\n            }\n\n            $eagerEntity = $this->em->getClassMetadata($assoc->targetEntity);\n\n            if ($eagerEntity->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {\n                continue; // now this is why you shouldn't use inheritance\n            }\n\n            $assocAlias = 'e' . ($eagerAliasCounter++);\n            $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc->targetEntity, $assocAlias, 'r', $assocField);\n\n            foreach ($eagerEntity->fieldNames as $field) {\n                $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias);\n            }\n\n            foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) {\n                $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL(\n                    $eagerAssocField,\n                    $eagerAssoc,\n                    $eagerEntity,\n                    $assocAlias,\n                );\n\n                if ($eagerAssocColumnSQL) {\n                    $columnList[] = $eagerAssocColumnSQL;\n                }\n            }\n\n            $association   = $assoc;\n            $joinCondition = [];\n\n            if ($assoc->isIndexed()) {\n                assert($assoc->isToMany());\n                $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc->indexBy());\n            }\n\n            if (! $assoc->isOwningSide()) {\n                $eagerEntity = $this->em->getClassMetadata($assoc->targetEntity);\n                $association = $eagerEntity->getAssociationMapping($assoc->mappedBy);\n            }\n\n            assert($association->isToOneOwningSide());\n\n            $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias);\n            $joinTableName  = $this->quoteStrategy->getTableName($eagerEntity, $this->platform);\n\n            if ($assoc->isOwningSide()) {\n                $tableAlias                                    = $this->getSQLTableAlias($association->targetEntity, $assocAlias);\n                $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association->joinColumns);\n\n                foreach ($association->joinColumns as $joinColumn) {\n                    $sourceCol       = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n                    $targetCol       = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);\n                    $joinCondition[] = $this->getSQLTableAlias($association->sourceEntity)\n                                        . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol;\n                }\n\n                // Add filter SQL\n                $filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias);\n                if ($filterSql) {\n                    $joinCondition[] = $filterSql;\n                }\n            } else {\n                $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN';\n\n                foreach ($association->joinColumns as $joinColumn) {\n                    $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n                    $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);\n\n                    $joinCondition[] = $this->getSQLTableAlias($association->sourceEntity, $assocAlias) . '.' . $sourceCol . ' = '\n                        . $this->getSQLTableAlias($association->targetEntity) . '.' . $targetCol;\n                }\n\n                // Add filter SQL\n                $filterSql = $this->generateFilterConditionSQL($eagerEntity, $joinTableAlias);\n                if ($filterSql) {\n                    $joinCondition[] = $filterSql;\n                }\n            }\n\n            $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON ';\n            $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition);\n        }\n\n        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);\n        $this->updateFilterHash();\n\n        return $this->currentPersisterContext->selectColumnListSql;\n    }\n\n    /** Gets the SQL join fragment used when selecting entities from an association. */\n    protected function getSelectColumnAssociationSQL(\n        string $field,\n        AssociationMapping $assoc,\n        ClassMetadata $class,\n        string $alias = 'r',\n    ): string {\n        if (! $assoc->isToOneOwningSide()) {\n            return '';\n        }\n\n        $columnList    = [];\n        $targetClass   = $this->em->getClassMetadata($assoc->targetEntity);\n        $isIdentifier  = isset($assoc->id) && $assoc->id === true;\n        $sqlTableAlias = $this->getSQLTableAlias($class->name, ($alias === 'r' ? '' : $alias));\n\n        foreach ($assoc->joinColumns as $joinColumn) {\n            $quotedColumn     = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n            $resultColumnName = $this->getSQLColumnAlias($joinColumn->name);\n            $type             = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n\n            $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn->name, $isIdentifier, $type);\n\n            $columnList[] = sprintf('%s.%s AS %s', $sqlTableAlias, $quotedColumn, $resultColumnName);\n        }\n\n        return implode(', ', $columnList);\n    }\n\n    /**\n     * Gets the SQL join fragment used when selecting entities from a\n     * many-to-many association.\n     */\n    protected function getSelectManyToManyJoinSQL(AssociationMapping&ManyToManyAssociationMapping $manyToMany): string\n    {\n        $conditions       = [];\n        $association      = $manyToMany;\n        $sourceTableAlias = $this->getSQLTableAlias($this->class->name);\n\n        $association   = $this->em->getMetadataFactory()->getOwningSide($manyToMany);\n        $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform);\n        $joinColumns   = $manyToMany->isOwningSide()\n            ? $association->joinTable->inverseJoinColumns\n            : $association->joinTable->joinColumns;\n\n        foreach ($joinColumns as $joinColumn) {\n            $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n            $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);\n            $conditions[]       = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn;\n        }\n\n        return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions);\n    }\n\n    public function getInsertSQL(): string\n    {\n        $columns   = $this->getInsertColumnList();\n        $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);\n\n        if ($columns === []) {\n            $identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);\n\n            return $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);\n        }\n\n        $placeholders = [];\n        $columns      = array_unique($columns);\n\n        foreach ($columns as $column) {\n            $placeholder = '?';\n\n            if (\n                isset($this->class->fieldNames[$column])\n                && isset($this->columnTypes[$this->class->fieldNames[$column]])\n                && isset($this->class->fieldMappings[$this->class->fieldNames[$column]])\n            ) {\n                $type        = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]);\n                $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);\n            }\n\n            $placeholders[] = $placeholder;\n        }\n\n        $columns      = implode(', ', $columns);\n        $placeholders = implode(', ', $placeholders);\n\n        return sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $placeholders);\n    }\n\n    /**\n     * Gets the list of columns to put in the INSERT SQL statement.\n     *\n     * Subclasses should override this method to alter or change the list of\n     * columns placed in the INSERT statements used by the persister.\n     *\n     * @phpstan-return list<string>\n     */\n    protected function getInsertColumnList(): array\n    {\n        $columns = [];\n\n        foreach ($this->class->propertyAccessors as $name => $field) {\n            if ($this->class->isVersioned && $this->class->versionField === $name) {\n                continue;\n            }\n\n            if (isset($this->class->embeddedClasses[$name])) {\n                continue;\n            }\n\n            if (isset($this->class->associationMappings[$name])) {\n                $assoc = $this->class->associationMappings[$name];\n\n                if ($assoc->isToOneOwningSide()) {\n                    foreach ($assoc->joinColumns as $joinColumn) {\n                        $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n                    }\n                }\n\n                continue;\n            }\n\n            if (! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] !== $name) {\n                if (isset($this->class->fieldMappings[$name]->notInsertable)) {\n                    continue;\n                }\n\n                $columns[]                = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform);\n                $this->columnTypes[$name] = $this->class->fieldMappings[$name]->type;\n            }\n        }\n\n        return $columns;\n    }\n\n    /**\n     * Gets the SQL snippet of a qualified column name for the given field name.\n     *\n     * @param ClassMetadata $class The class that declares this field. The table this class is\n     *                             mapped to must own the column for the given field.\n     */\n    protected function getSelectColumnSQL(string $field, ClassMetadata $class, string $alias = 'r'): string\n    {\n        $root         = $alias === 'r' ? '' : $alias;\n        $tableAlias   = $this->getSQLTableAlias($class->name, $root);\n        $fieldMapping = $class->fieldMappings[$field];\n        $sql          = sprintf('%s.%s', $tableAlias, $this->quoteStrategy->getColumnName($field, $class, $this->platform));\n\n        $columnAlias = null;\n        if ($this->currentPersisterContext->rsm->hasColumnAliasByField($alias, $field)) {\n            $columnAlias = $this->currentPersisterContext->rsm->getColumnAliasByField($alias, $field);\n        }\n\n        if ($columnAlias === null) {\n            $columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);\n        }\n\n        $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field);\n        if (! empty($fieldMapping->enumType)) {\n            $this->currentPersisterContext->rsm->addEnumResult($columnAlias, $fieldMapping->enumType);\n        }\n\n        $type = Type::getType($fieldMapping->type);\n        $sql  = $type->convertToPHPValueSQL($sql, $this->platform);\n\n        return $sql . ' AS ' . $columnAlias;\n    }\n\n    /**\n     * Gets the SQL table alias for the given class name.\n     *\n     * @todo Reconsider. Binding table aliases to class names is not such a good idea.\n     */\n    protected function getSQLTableAlias(string $className, string $assocName = ''): string\n    {\n        if ($assocName) {\n            $className .= '#' . $assocName;\n        }\n\n        if (isset($this->currentPersisterContext->sqlTableAliases[$className])) {\n            return $this->currentPersisterContext->sqlTableAliases[$className];\n        }\n\n        $tableAlias = 't' . $this->currentPersisterContext->sqlAliasCounter++;\n\n        $this->currentPersisterContext->sqlTableAliases[$className] = $tableAlias;\n\n        return $tableAlias;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function lock(array $criteria, LockMode|int $lockMode): void\n    {\n        $conditionSql = $this->getSelectConditionSQL($criteria);\n\n        $lockSql = match ($lockMode) {\n            LockMode::PESSIMISTIC_READ => $this->getReadLockSQL($this->platform),\n            LockMode::PESSIMISTIC_WRITE => $this->getWriteLockSQL($this->platform),\n            default => '',\n        };\n\n        $lock  = $this->getLockTablesSql($lockMode);\n        $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ';\n        $sql   = 'SELECT 1 '\n             . $lock\n             . $where\n             . $lockSql;\n\n        [$params, $types] = $this->expandParameters($criteria);\n\n        $this->conn->executeQuery($sql, $params, $types);\n    }\n\n    /**\n     * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister.\n     *\n     * @phpstan-param LockMode::* $lockMode\n     */\n    protected function getLockTablesSql(LockMode|int $lockMode): string\n    {\n        return $this->platform->appendLockHint(\n            'FROM '\n            . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' '\n            . $this->getSQLTableAlias($this->class->name),\n            $lockMode,\n        );\n    }\n\n    /**\n     * Gets the Select Where Condition from a Criteria object.\n     */\n    protected function getSelectConditionCriteriaSQL(Criteria $criteria): string\n    {\n        $expression = $criteria->getWhereExpression();\n\n        if ($expression === null) {\n            return '';\n        }\n\n        $visitor = new SqlExpressionVisitor($this, $this->class);\n\n        return $visitor->dispatch($expression);\n    }\n\n    public function getSelectConditionStatementSQL(\n        string $field,\n        mixed $value,\n        AssociationMapping|null $assoc = null,\n        string|null $comparison = null,\n    ): string {\n        $comparison ??= (is_array($value) ? Comparison::IN : Comparison::EQ);\n\n        $selectedColumns = [];\n        $columns         = $this->getSelectConditionStatementColumnSQL($field, $assoc);\n\n        if (count($columns) > 1 && $comparison === Comparison::IN) {\n            /*\n             *  @todo try to support multi-column IN expressions.\n             *  Example: (col1, col2) IN (('val1A', 'val2A'), ('val1B', 'val2B'))\n             */\n            throw CantUseInOperatorOnCompositeKeys::create();\n        }\n\n        foreach ($columns as $column) {\n            $placeholder = '?';\n\n            if (isset($this->class->fieldMappings[$field])) {\n                $type        = Type::getType($this->class->fieldMappings[$field]->type);\n                $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->platform);\n            }\n\n            // special case null value handling\n            if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) {\n                $selectedColumns[] = $column . ' IS NULL';\n\n                continue;\n            }\n\n            if ($comparison === Comparison::NEQ && $value === null) {\n                $selectedColumns[] = $column . ' IS NOT NULL';\n\n                continue;\n            }\n\n            if ($comparison === Comparison::IN || $comparison === Comparison::NIN) {\n                if (! is_array($value)) {\n                    $value = [$value];\n                }\n\n                if ($value === []) {\n                    $selectedColumns[] = '1=0';\n                    continue;\n                }\n\n                $nullKeys      = array_keys($value, null, true);\n                $nonNullValues = array_diff_key($value, array_flip($nullKeys));\n\n                $placeholders = implode(', ', array_fill(0, count($nonNullValues), $placeholder));\n\n                $in = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholders);\n\n                if ($nullKeys) {\n                    if ($nonNullValues) {\n                        $selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column);\n                    } else {\n                        $selectedColumns[] = $column . ' IS NULL';\n                    }\n                } else {\n                    $selectedColumns[] = $in;\n                }\n\n                continue;\n            }\n\n            $selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);\n        }\n\n        return implode(' AND ', $selectedColumns);\n    }\n\n    /**\n     * Builds the left-hand-side of a where condition statement.\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     *\n     * @throws InvalidFindByCall\n     * @throws UnrecognizedField\n     */\n    private function getSelectConditionStatementColumnSQL(\n        string $field,\n        AssociationMapping|null $assoc = null,\n    ): array {\n        if (isset($this->class->fieldMappings[$field])) {\n            $className = $this->class->fieldMappings[$field]->inherited ?? $this->class->name;\n\n            return [$this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)];\n        }\n\n        if (isset($this->class->associationMappings[$field])) {\n            $association = $this->class->associationMappings[$field];\n            // Many-To-Many requires join table check for joinColumn\n            $columns = [];\n            $class   = $this->class;\n\n            if ($association->isManyToMany()) {\n                assert($assoc !== null);\n                if (! $association->isOwningSide()) {\n                    $association = $assoc;\n                }\n\n                assert($association->isManyToManyOwningSide());\n\n                $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform);\n                $joinColumns   = $assoc->isOwningSide()\n                    ? $association->joinTable->joinColumns\n                    : $association->joinTable->inverseJoinColumns;\n\n                foreach ($joinColumns as $joinColumn) {\n                    $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n                }\n            } else {\n                if (! $association->isOwningSide()) {\n                    throw InvalidFindByCall::fromInverseSideUsage(\n                        $this->class->name,\n                        $field,\n                    );\n                }\n\n                assert($association->isToOneOwningSide());\n\n                $className = $association->inherited ?? $this->class->name;\n\n                foreach ($association->joinColumns as $joinColumn) {\n                    $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);\n                }\n            }\n\n            return $columns;\n        }\n\n        if ($assoc !== null && ! str_contains($field, ' ') && ! str_contains($field, '(')) {\n            // very careless developers could potentially open up this normally hidden api for userland attacks,\n            // therefore checking for spaces and function calls which are not allowed.\n\n            // found a join column condition, not really a \"field\"\n            return [$field];\n        }\n\n        throw UnrecognizedField::byFullyQualifiedName($this->class->name, $field);\n    }\n\n    /**\n     * Gets the conditional SQL fragment used in the WHERE clause when selecting\n     * entities in this persister.\n     *\n     * Subclasses are supposed to override this method if they intend to change\n     * or alter the criteria by which entities are selected.\n     *\n     * @phpstan-param array<string, mixed> $criteria\n     */\n    protected function getSelectConditionSQL(array $criteria, AssociationMapping|null $assoc = null): string\n    {\n        $conditions = [];\n\n        foreach ($criteria as $field => $value) {\n            $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc);\n        }\n\n        return implode(' AND ', $conditions);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array {\n        assert($assoc instanceof OneToManyAssociationMapping);\n        $this->switchPersisterContext($offset, $limit);\n\n        $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);\n\n        return $this->loadArrayFromResult($assoc, $stmt);\n    }\n\n    public function loadOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        PersistentCollection $collection,\n    ): mixed {\n        assert($assoc instanceof OneToManyAssociationMapping);\n        $stmt = $this->getOneToManyStatement($assoc, $sourceEntity);\n\n        return $this->loadCollectionFromStatement($assoc, $stmt, $collection);\n    }\n\n    /** Builds criteria and execute SQL statement to fetch the one to many entities from. */\n    private function getOneToManyStatement(\n        OneToManyAssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): Result {\n        $this->switchPersisterContext($offset, $limit);\n\n        $criteria    = [];\n        $parameters  = [];\n        $owningAssoc = $this->class->associationMappings[$assoc->mappedBy];\n        $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity);\n        $tableAlias  = $this->getSQLTableAlias($owningAssoc->inherited ?? $this->class->name);\n        assert($owningAssoc->isManyToOne());\n\n        foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {\n            if ($sourceClass->containsForeignIdentifier) {\n                $field = $sourceClass->getFieldForColumn($sourceKeyColumn);\n                $value = $sourceClass->propertyAccessors[$field]->getValue($sourceEntity);\n\n                if (isset($sourceClass->associationMappings[$field])) {\n                    $value = $this->em->getUnitOfWork()->getEntityIdentifier($value);\n                    $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]->targetEntity)->identifier[0]];\n                }\n\n                $criteria[$tableAlias . '.' . $targetKeyColumn] = $value;\n                $parameters[]                                   = [\n                    'value' => $value,\n                    'field' => $field,\n                    'class' => $sourceClass,\n                ];\n\n                continue;\n            }\n\n            $field = $sourceClass->fieldNames[$sourceKeyColumn];\n            $value = $sourceClass->propertyAccessors[$field]->getValue($sourceEntity);\n\n            $criteria[$tableAlias . '.' . $targetKeyColumn] = $value;\n            $parameters[]                                   = [\n                'value' => $value,\n                'field' => $field,\n                'class' => $sourceClass,\n            ];\n        }\n\n        $sql              = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);\n        [$params, $types] = $this->expandToManyParameters($parameters);\n\n        return $this->conn->executeQuery($sql, $params, $types);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function expandParameters(array $criteria): array\n    {\n        $params = [];\n        $types  = [];\n\n        foreach ($criteria as $field => $value) {\n            if ($value === null) {\n                continue; // skip null values.\n            }\n\n            if (is_array($value)) {\n                $nonNullValues = array_diff_key($value, array_flip(array_keys($value, null, true)));\n                foreach ($nonNullValues as $item) {\n                    $types  = [...$types, ...PersisterHelper::inferParameterTypes($field, $item, $this->class, $this->em)];\n                    $params = [...$params, ...PersisterHelper::convertToParameterValue($item, $this->em)];\n                }\n\n                continue;\n            }\n\n            $types  = [...$types, ...PersisterHelper::inferParameterTypes($field, $value, $this->class, $this->em)];\n            $params = [...$params, ...PersisterHelper::convertToParameterValue($value, $this->em)];\n        }\n\n        return [$params, $types];\n    }\n\n    /**\n     * Expands the parameters from the given criteria and use the correct binding types if found,\n     * specialized for OneToMany or ManyToMany associations.\n     *\n     * @param mixed[][] $criteria an array of arrays containing following:\n     *                             - field to which each criterion will be bound\n     *                             - value to be bound\n     *                             - class to which the field belongs to\n     *\n     * @return mixed[][]\n     * @phpstan-return array{0: array, 1: list<ParameterType::*|ArrayParameterType::*|string>}\n     */\n    private function expandToManyParameters(array $criteria): array\n    {\n        $params = [];\n        $types  = [];\n\n        foreach ($criteria as $criterion) {\n            if ($criterion['value'] === null) {\n                continue; // skip null values.\n            }\n\n            $types  = [...$types, ...PersisterHelper::inferParameterTypes($criterion['field'], $criterion['value'], $criterion['class'], $this->em)];\n            $params = [...$params, ...PersisterHelper::convertToParameterValue($criterion['value'], $this->em)];\n        }\n\n        return [$params, $types];\n    }\n\n    public function exists(object $entity, Criteria|null $extraConditions = null): bool\n    {\n        $criteria = $this->class->getIdentifierValues($entity);\n\n        if (! $criteria) {\n            return false;\n        }\n\n        $alias = $this->getSQLTableAlias($this->class->name);\n\n        $sql = 'SELECT 1 '\n             . $this->getLockTablesSql(LockMode::NONE)\n             . ' WHERE ' . $this->getSelectConditionSQL($criteria);\n\n        [$params, $types] = $this->expandParameters($criteria);\n\n        if ($extraConditions !== null) {\n            $sql                             .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions);\n            [$criteriaParams, $criteriaTypes] = $this->expandCriteriaParameters($extraConditions);\n\n            $params = [...$params, ...$criteriaParams];\n            $types  = [...$types, ...$criteriaTypes];\n        }\n\n        $filterSql = $this->generateFilterConditionSQL($this->class, $alias);\n        if ($filterSql) {\n            $sql .= ' AND ' . $filterSql;\n        }\n\n        return (bool) $this->conn->fetchOne($sql, $params, $types);\n    }\n\n    /**\n     * Generates the appropriate join SQL for the given join column.\n     *\n     * @param list<JoinColumnMapping> $joinColumns The join columns definition of an association.\n     *\n     * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.\n     */\n    protected function getJoinSQLForJoinColumns(array $joinColumns): string\n    {\n        // if one of the join columns is nullable, return left join\n        foreach ($joinColumns as $joinColumn) {\n            if (! isset($joinColumn->nullable) || $joinColumn->nullable) {\n                return 'LEFT JOIN';\n            }\n        }\n\n        return 'INNER JOIN';\n    }\n\n    public function getSQLColumnAlias(string $columnName): string\n    {\n        return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform);\n    }\n\n    /**\n     * Generates the filter SQL for a given entity and table alias.\n     *\n     * @param ClassMetadata $targetEntity     Metadata of the target entity.\n     * @param string        $targetTableAlias The table alias of the joined/selected table.\n     *\n     * @return string The SQL query part to add to a query.\n     */\n    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        $filterClauses = [];\n\n        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {\n            $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);\n            if ($filterExpr !== '') {\n                $filterClauses[] = '(' . $filterExpr . ')';\n            }\n        }\n\n        $sql = implode(' AND ', $filterClauses);\n\n        return $sql ? '(' . $sql . ')' : ''; // Wrap again to avoid \"X or Y and FilterConditionSQL\"\n    }\n\n    /**\n     * Switches persister context according to current query offset/limits\n     *\n     * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved\n     */\n    protected function switchPersisterContext(int|null $offset, int|null $limit): void\n    {\n        if ($offset === null && $limit === null) {\n            $this->currentPersisterContext = $this->noLimitsContext;\n\n            return;\n        }\n\n        $this->currentPersisterContext = $this->limitsHandlingContext;\n    }\n\n    /**\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    protected function getClassIdentifiersTypes(ClassMetadata $class): array\n    {\n        $entityManager = $this->em;\n\n        return array_map(\n            static function ($fieldName) use ($class, $entityManager): string {\n                $types = PersisterHelper::getTypeOfField($fieldName, $class, $entityManager);\n                assert(isset($types[0]));\n\n                return $types[0];\n            },\n            $class->identifier,\n        );\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Entity/CachedPersisterContext.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\n\n/**\n * A swappable persister context to use as a container for the current\n * generated query/resultSetMapping/type binding information.\n *\n * This class is a utility class to be used only by the persister API\n *\n * This object is highly mutable due to performance reasons. Same reasoning\n * behind its properties being public.\n */\nclass CachedPersisterContext\n{\n    /**\n     * The SELECT column list SQL fragment used for querying entities by this persister.\n     * This SQL fragment is only generated once per request, if at all.\n     */\n    public string|null $selectColumnListSql = null;\n\n    /**\n     * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one\n     * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.\n     */\n    public string|null $selectJoinSql = null;\n\n    /**\n     * Counter for creating unique SQL table and column aliases.\n     */\n    public int $sqlAliasCounter = 0;\n\n    /**\n     * Map from class names (FQCN) to the corresponding generated SQL table aliases.\n     *\n     * @var array<class-string, string>\n     */\n    public array $sqlTableAliases = [];\n\n    public function __construct(\n        /**\n         * Metadata object that describes the mapping of the mapped entity class.\n         */\n        public ClassMetadata $class,\n        /**\n         * ResultSetMapping that is used for all queries. Is generated lazily once per request.\n         */\n        public ResultSetMapping $rsm,\n        /**\n         * Whether this persistent context is considering limit operations applied to the selection queries\n         */\n        public bool $handlesLimits,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Entity/EntityPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\n\n/**\n * Entity persister interface\n * Define the behavior that should be implemented by all entity persisters.\n */\ninterface EntityPersister\n{\n    public function getClassMetadata(): ClassMetadata;\n\n    /**\n     * Gets the ResultSetMapping used for hydration.\n     */\n    public function getResultSetMapping(): ResultSetMapping;\n\n    /**\n     * Get all queued inserts.\n     *\n     * @return object[]\n     */\n    public function getInserts(): array;\n\n     /**\n      * Gets the INSERT SQL used by the persister to persist a new entity.\n      *\n      * @TODO It should not be here.\n      *       But its necessary since JoinedSubclassPersister#executeInserts invoke the root persister.\n      */\n    public function getInsertSQL(): string;\n\n    /**\n     * Gets the SELECT SQL to select one or more entities by a set of field criteria.\n     *\n     * @param mixed[]|Criteria $criteria\n     * @param mixed[]|null     $orderBy\n     * @phpstan-param AssociationMapping|null $assoc\n     * @phpstan-param LockMode::*|null $lockMode\n     */\n    public function getSelectSQL(\n        array|Criteria $criteria,\n        AssociationMapping|null $assoc = null,\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        int|null $offset = null,\n        array|null $orderBy = null,\n    ): string;\n\n    /**\n     * Get the COUNT SQL to count entities (optionally based on a criteria)\n     *\n     * @param mixed[]|Criteria $criteria\n     */\n    public function getCountSQL(array|Criteria $criteria = []): string;\n\n    /**\n     * Expands the parameters from the given criteria and use the correct binding types if found.\n     *\n     * @param array<string, mixed> $criteria\n     *\n     * @phpstan-return array{list<mixed>, list<ParameterType::*|ArrayParameterType::*|string>}\n     */\n    public function expandParameters(array $criteria): array;\n\n    /**\n     * Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it.\n     *\n     * @phpstan-return array{list<mixed>, list<ParameterType::*|ArrayParameterType::*|string>}\n     */\n    public function expandCriteriaParameters(Criteria $criteria): array;\n\n    /** Gets the SQL WHERE condition for matching a field with a given value. */\n    public function getSelectConditionStatementSQL(\n        string $field,\n        mixed $value,\n        AssociationMapping|null $assoc = null,\n        string|null $comparison = null,\n    ): string;\n\n    /**\n     * Adds an entity to the queued insertions.\n     * The entity remains queued until {@link executeInserts} is invoked.\n     */\n    public function addInsert(object $entity): void;\n\n    /**\n     * Executes all queued entity insertions.\n     *\n     * If no inserts are queued, invoking this method is a NOOP.\n     */\n    public function executeInserts(): void;\n\n    /**\n     * Updates a managed entity. The entity is updated according to its current changeset\n     * in the running UnitOfWork. If there is no changeset, nothing is updated.\n     */\n    public function update(object $entity): void;\n\n    /**\n     * Deletes a managed entity.\n     *\n     * The entity to delete must be managed and have a persistent identifier.\n     * The deletion happens instantaneously.\n     *\n     * Subclasses may override this method to customize the semantics of entity deletion.\n     *\n     * @return bool TRUE if the entity got deleted in the database, FALSE otherwise.\n     */\n    public function delete(object $entity): bool;\n\n    /**\n     * Count entities (optionally filtered by a criteria)\n     *\n     * @param mixed[]|Criteria $criteria\n     *\n     * @phpstan-return 0|positive-int\n     */\n    public function count(array|Criteria $criteria = []): int;\n\n    /**\n     * Gets the name of the table that owns the column the given field is mapped to.\n     *\n     * The default implementation in BasicEntityPersister always returns the name\n     * of the table the entity type of this persister is mapped to, since an entity\n     * is always persisted to a single table with a BasicEntityPersister.\n     */\n    public function getOwningTable(string $fieldName): string;\n\n    /**\n     * Loads an entity by a list of field criteria.\n     *\n     * @param mixed[]                 $criteria The criteria by which to load the entity.\n     * @param object|null             $entity   The entity to load the data into. If not specified,\n     *                                          a new entity is created.\n     * @param AssociationMapping|null $assoc    The association that connects the entity\n     *                                          to load to another entity, if any.\n     * @param mixed[]                 $hints    Hints for entity creation.\n     * @param LockMode|int|null       $lockMode One of the \\Doctrine\\DBAL\\LockMode::* constants\n     *                                          or NULL if no specific lock mode should be used\n     *                                          for loading the entity.\n     * @param int|null                $limit    Limit number of results.\n     * @param string[]|null           $orderBy  Criteria to order by.\n     * @phpstan-param array<string, mixed>       $criteria\n     * @phpstan-param array<string, mixed>       $hints\n     * @phpstan-param LockMode::*|null           $lockMode\n     * @phpstan-param array<string, string>|null $orderBy\n     *\n     * @return object|null The loaded and managed entity instance or NULL if the entity can not be found.\n     *\n     * @todo Check identity map? loadById method? Try to guess whether $criteria is the id?\n     */\n    public function load(\n        array $criteria,\n        object|null $entity = null,\n        AssociationMapping|null $assoc = null,\n        array $hints = [],\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        array|null $orderBy = null,\n    ): object|null;\n\n    /**\n     * Loads an entity by identifier.\n     *\n     * @param object|null $entity The entity to load the data into. If not specified, a new entity is created.\n     * @phpstan-param array<string, mixed> $identifier The entity identifier.\n     *\n     * @return object|null The loaded and managed entity instance or NULL if the entity can not be found.\n     *\n     * @todo Check parameters\n     */\n    public function loadById(array $identifier, object|null $entity = null): object|null;\n\n    /**\n     * Loads an entity of this persister's mapped class as part of a single-valued\n     * association from another entity.\n     *\n     * @param AssociationMapping $assoc        The association to load.\n     * @param object             $sourceEntity The entity that owns the association (not necessarily the \"owning side\").\n     * @phpstan-param array<string, mixed> $identifier The identifier of the entity to load. Must be provided if\n     *                                               the association to load represents the owning side, otherwise\n     *                                               the identifier is derived from the $sourceEntity.\n     *\n     * @return object|null The loaded and managed entity instance or NULL if the entity can not be found.\n     *\n     * @throws MappingException\n     */\n    public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null;\n\n    /**\n     * Refreshes a managed entity.\n     *\n     * @param LockMode|int|null $lockMode One of the \\Doctrine\\DBAL\\LockMode::* constants\n     *                                    or NULL if no specific lock mode should be used\n     *                                    for refreshing the managed entity.\n     * @phpstan-param array<string, mixed> $id The identifier of the entity as an\n     *                                       associative array from column or\n     *                                       field names to values.\n     * @phpstan-param LockMode::*|null $lockMode\n     */\n    public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void;\n\n    /**\n     * Loads Entities matching the given Criteria object.\n     *\n     * @return mixed[]\n     */\n    public function loadCriteria(Criteria $criteria): array;\n\n    /**\n     * Loads a list of entities by a list of field criteria.\n     *\n     * @phpstan-param array<string, string>|null $orderBy\n     * @phpstan-param array<string, mixed>       $criteria\n     *\n     * @return mixed[]\n     */\n    public function loadAll(\n        array $criteria = [],\n        array|null $orderBy = null,\n        int|null $limit = null,\n        int|null $offset = null,\n    ): array;\n\n    /**\n     * Gets (sliced or full) elements of the given collection.\n     *\n     * @return mixed[]\n     */\n    public function getManyToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array;\n\n    /**\n     * Loads a collection of entities of a many-to-many association.\n     *\n     * @param AssociationMapping   $assoc        The association mapping of the association being loaded.\n     * @param object               $sourceEntity The entity that owns the collection.\n     * @param PersistentCollection $collection   The collection to fill.\n     *\n     * @return mixed[]\n     */\n    public function loadManyToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        PersistentCollection $collection,\n    ): array;\n\n    /**\n     * Loads a collection of entities in a one-to-many association.\n     *\n     * @param PersistentCollection $collection The collection to load/fill.\n     */\n    public function loadOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        PersistentCollection $collection,\n    ): mixed;\n\n    /**\n     * Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode.\n     *\n     * @phpstan-param array<string, mixed> $criteria\n     * @phpstan-param LockMode::* $lockMode\n     */\n    public function lock(array $criteria, LockMode|int $lockMode): void;\n\n    /**\n     * Returns an array with (sliced or full list) of elements in the specified collection.\n     *\n     * @return mixed[]\n     */\n    public function getOneToManyCollection(\n        AssociationMapping $assoc,\n        object $sourceEntity,\n        int|null $offset = null,\n        int|null $limit = null,\n    ): array;\n\n    /**\n     * Checks whether the given managed entity exists in the database.\n     */\n    public function exists(object $entity, Criteria|null $extraConditions = null): bool;\n}\n"
  },
  {
    "path": "src/Persisters/Entity/JoinedSubclassPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Utility\\LockSqlHelper;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse LengthException;\n\nuse function array_combine;\nuse function array_keys;\nuse function array_values;\nuse function implode;\n\n/**\n * The joined subclass persister maps a single entity instance to several tables in the\n * database as it is defined by the <tt>Class Table Inheritance</tt> strategy.\n *\n * @see https://martinfowler.com/eaaCatalog/classTableInheritance.html\n */\nclass JoinedSubclassPersister extends AbstractEntityInheritancePersister\n{\n    use LockSqlHelper;\n    use SQLResultCasing;\n\n    /**\n     * Map that maps column names to the table names that own them.\n     * This is mainly a temporary cache, used during a single request.\n     *\n     * @phpstan-var array<string, string>\n     */\n    private array $owningTableMap = [];\n\n    /**\n     * Map of table to quoted table names.\n     *\n     * @phpstan-var array<string, string>\n     */\n    private array $quotedTableMap = [];\n\n    protected function getDiscriminatorColumnTableName(): string\n    {\n        $class = $this->class->name !== $this->class->rootEntityName\n            ? $this->em->getClassMetadata($this->class->rootEntityName)\n            : $this->class;\n\n        return $class->getTableName();\n    }\n\n    /**\n     * This function finds the ClassMetadata instance in an inheritance hierarchy\n     * that is responsible for enabling versioning.\n     */\n    private function getVersionedClassMetadata(): ClassMetadata\n    {\n        if ($this->class->versionField !== null && isset($this->class->fieldMappings[$this->class->versionField]->inherited)) {\n            $definingClassName = $this->class->fieldMappings[$this->class->versionField]->inherited;\n\n            return $this->em->getClassMetadata($definingClassName);\n        }\n\n        return $this->class;\n    }\n\n    /**\n     * Gets the name of the table that owns the column the given field is mapped to.\n     */\n    public function getOwningTable(string $fieldName): string\n    {\n        if (isset($this->owningTableMap[$fieldName])) {\n            return $this->owningTableMap[$fieldName];\n        }\n\n        $cm = match (true) {\n            isset($this->class->associationMappings[$fieldName]->inherited)\n                => $this->em->getClassMetadata($this->class->associationMappings[$fieldName]->inherited),\n            isset($this->class->fieldMappings[$fieldName]->inherited)\n                => $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]->inherited),\n            default => $this->class,\n        };\n\n        $tableName       = $cm->getTableName();\n        $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform);\n\n        $this->owningTableMap[$fieldName] = $tableName;\n        $this->quotedTableMap[$tableName] = $quotedTableName;\n\n        return $tableName;\n    }\n\n    public function executeInserts(): void\n    {\n        if (! $this->queuedInserts) {\n            return;\n        }\n\n        $uow            = $this->em->getUnitOfWork();\n        $idGenerator    = $this->class->idGenerator;\n        $isPostInsertId = $idGenerator->isPostInsertGenerator();\n        $rootClass      = $this->class->name !== $this->class->rootEntityName\n            ? $this->em->getClassMetadata($this->class->rootEntityName)\n            : $this->class;\n\n        // Prepare statement for the root table\n        $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->name);\n        $rootTableName = $rootClass->getTableName();\n        $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL());\n\n        // Prepare statements for sub tables.\n        $subTableStmts = [];\n\n        if ($rootClass !== $this->class) {\n            $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL());\n        }\n\n        foreach ($this->class->parentClasses as $parentClassName) {\n            $parentClass     = $this->em->getClassMetadata($parentClassName);\n            $parentTableName = $parentClass->getTableName();\n\n            if ($parentClass !== $rootClass) {\n                $parentPersister                 = $this->em->getUnitOfWork()->getEntityPersister($parentClassName);\n                $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL());\n            }\n        }\n\n        // Execute all inserts. For each entity:\n        // 1) Insert on root table\n        // 2) Insert on sub tables\n        foreach ($this->queuedInserts as $key => $entity) {\n            $insertData = $this->prepareInsertData($entity);\n\n            // Execute insert on root table\n            $paramIndex = 1;\n\n            foreach ($insertData[$rootTableName] as $columnName => $value) {\n                $rootTableStmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);\n            }\n\n            $rootTableStmt->executeStatement();\n\n            if ($isPostInsertId) {\n                $generatedId = $idGenerator->generateId($this->em, $entity);\n                $id          = [$this->class->identifier[0] => $generatedId];\n\n                $uow->assignPostInsertId($entity, $generatedId);\n            } else {\n                $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);\n            }\n\n            // Execute inserts on subtables.\n            // The order doesn't matter because all child tables link to the root table via FK.\n            foreach ($subTableStmts as $tableName => $stmt) {\n                $paramIndex = 1;\n                $data       = $insertData[$tableName] ?? [];\n\n                foreach ($id as $idName => $idVal) {\n                    $type = $this->columnTypes[$idName] ?? Types::STRING;\n\n                    $stmt->bindValue($paramIndex++, $idVal, $type);\n                }\n\n                foreach ($data as $columnName => $value) {\n                    if (! isset($id[$columnName])) {\n                        $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);\n                    }\n                }\n\n                $stmt->executeStatement();\n            }\n\n            if ($this->class->requiresFetchAfterChange) {\n                $this->assignDefaultVersionAndUpsertableValues($entity, $id);\n            }\n\n            // Unset this queued insert, so that the prepareUpdateData() method (called via prepareInsertData() method)\n            // knows right away (for the next entity already) that the current entity has been written to the database\n            // and no extra updates need to be scheduled to refer to it.\n            //\n            // In \\Doctrine\\ORM\\UnitOfWork::executeInserts(), the UoW already removed entities\n            // from its own list (\\Doctrine\\ORM\\UnitOfWork::$entityInsertions) right after they\n            // were given to our addInsert() method.\n            unset($this->queuedInserts[$key]);\n        }\n    }\n\n    public function update(object $entity): void\n    {\n        $updateData = $this->prepareUpdateData($entity);\n\n        if (! $updateData) {\n            return;\n        }\n\n        $isVersioned = $this->class->isVersioned;\n\n        $versionedClass = $this->getVersionedClassMetadata();\n        $versionedTable = $versionedClass->getTableName();\n\n        foreach ($updateData as $tableName => $data) {\n            $tableName = $this->quotedTableMap[$tableName];\n            $versioned = $isVersioned && $versionedTable === $tableName;\n\n            $this->updateTable($entity, $tableName, $data, $versioned);\n        }\n\n        if ($this->class->requiresFetchAfterChange) {\n            // Make sure the table with the version column is updated even if no columns on that\n            // table were affected.\n            if ($isVersioned && ! isset($updateData[$versionedTable])) {\n                $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform);\n\n                $this->updateTable($entity, $tableName, [], true);\n            }\n\n            $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity);\n\n            $this->assignDefaultVersionAndUpsertableValues($entity, $identifiers);\n        }\n    }\n\n    public function delete(object $entity): bool\n    {\n        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);\n        $id         = array_combine($this->class->getIdentifierColumnNames(), $identifier);\n        $types      = $this->getClassIdentifiersTypes($this->class);\n\n        $this->deleteJoinTableRecords($identifier, $types);\n\n        // Delete the row from the root table. Cascades do the rest.\n        $rootClass = $this->em->getClassMetadata($this->class->rootEntityName);\n        $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform);\n        $rootTypes = $this->getClassIdentifiersTypes($rootClass);\n\n        return (bool) $this->conn->delete($rootTable, $id, $rootTypes);\n    }\n\n    public function getSelectSQL(\n        array|Criteria $criteria,\n        AssociationMapping|null $assoc = null,\n        LockMode|int|null $lockMode = null,\n        int|null $limit = null,\n        int|null $offset = null,\n        array|null $orderBy = null,\n    ): string {\n        $this->switchPersisterContext($offset, $limit);\n\n        $baseTableAlias = $this->getSQLTableAlias($this->class->name);\n        $joinSql        = $this->getJoinSql($baseTableAlias);\n\n        if ($assoc !== null && $assoc->isManyToMany()) {\n            $joinSql .= $this->getSelectManyToManyJoinSQL($assoc);\n        }\n\n        $conditionSql = $criteria instanceof Criteria\n            ? $this->getSelectConditionCriteriaSQL($criteria)\n            : $this->getSelectConditionSQL($criteria, $assoc);\n\n        $filterSql = $this->generateFilterConditionSQL(\n            $this->em->getClassMetadata($this->class->rootEntityName),\n            $this->getSQLTableAlias($this->class->rootEntityName),\n        );\n        // If the current class in the root entity, add the filters\n        if ($filterSql) {\n            $conditionSql .= $conditionSql\n                ? ' AND ' . $filterSql\n                : $filterSql;\n        }\n\n        $orderBySql = '';\n\n        if ($assoc !== null && $assoc->isOrdered()) {\n            $orderBy = $assoc->orderBy();\n        }\n\n        if ($orderBy) {\n            $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias);\n        }\n\n        $lockSql = '';\n\n        switch ($lockMode) {\n            case LockMode::PESSIMISTIC_READ:\n                $lockSql = ' ' . $this->getReadLockSQL($this->platform);\n\n                break;\n\n            case LockMode::PESSIMISTIC_WRITE:\n                $lockSql = ' ' . $this->getWriteLockSQL($this->platform);\n\n                break;\n        }\n\n        $tableName  = $this->quoteStrategy->getTableName($this->class, $this->platform);\n        $from       = ' FROM ' . $tableName . ' ' . $baseTableAlias;\n        $where      = $conditionSql !== '' ? ' WHERE ' . $conditionSql : '';\n        $lock       = $this->platform->appendLockHint($from, $lockMode ?? LockMode::NONE);\n        $columnList = $this->getSelectColumnsSQL();\n        $query      = 'SELECT ' . $columnList\n                    . $lock\n                    . $joinSql\n                    . $where\n                    . $orderBySql;\n\n        return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql;\n    }\n\n    public function getCountSQL(array|Criteria $criteria = []): string\n    {\n        $tableName      = $this->quoteStrategy->getTableName($this->class, $this->platform);\n        $baseTableAlias = $this->getSQLTableAlias($this->class->name);\n        $joinSql        = $this->getJoinSql($baseTableAlias);\n\n        $conditionSql = $criteria instanceof Criteria\n            ? $this->getSelectConditionCriteriaSQL($criteria)\n            : $this->getSelectConditionSQL($criteria);\n\n        $filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName));\n\n        if ($filterSql !== '') {\n            $conditionSql = $conditionSql\n                ? $conditionSql . ' AND ' . $filterSql\n                : $filterSql;\n        }\n\n        return 'SELECT COUNT(*) '\n            . 'FROM ' . $tableName . ' ' . $baseTableAlias\n            . $joinSql\n            . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql);\n    }\n\n    protected function getLockTablesSql(LockMode|int $lockMode): string\n    {\n        $joinSql           = '';\n        $identifierColumns = $this->class->getIdentifierColumnNames();\n        $baseTableAlias    = $this->getSQLTableAlias($this->class->name);\n\n        // INNER JOIN parent tables\n        foreach ($this->class->parentClasses as $parentClassName) {\n            $conditions  = [];\n            $tableAlias  = $this->getSQLTableAlias($parentClassName);\n            $parentClass = $this->em->getClassMetadata($parentClassName);\n            $joinSql    .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';\n\n            foreach ($identifierColumns as $idColumn) {\n                $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;\n            }\n\n            $joinSql .= implode(' AND ', $conditions);\n        }\n\n        return parent::getLockTablesSql($lockMode) . $joinSql;\n    }\n\n    /**\n     * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly.\n     */\n    protected function getSelectColumnsSQL(): string\n    {\n        // Create the column list fragment only once\n        if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {\n            return $this->currentPersisterContext->selectColumnListSql;\n        }\n\n        $columnList       = [];\n        $discrColumn      = $this->class->getDiscriminatorColumn();\n        $discrColumnName  = $discrColumn->name;\n        $discrColumnType  = $discrColumn->type;\n        $baseTableAlias   = $this->getSQLTableAlias($this->class->name);\n        $resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName);\n\n        $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r');\n        $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);\n        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);\n\n        // Add regular columns\n        foreach ($this->class->fieldMappings as $fieldName => $mapping) {\n            $class = isset($mapping->inherited)\n                ? $this->em->getClassMetadata($mapping->inherited)\n                : $this->class;\n\n            $columnList[] = $this->getSelectColumnSQL($fieldName, $class);\n        }\n\n        // Add foreign key columns\n        foreach ($this->class->associationMappings as $mapping) {\n            if (! $mapping->isToOneOwningSide()) {\n                continue;\n            }\n\n            $tableAlias = isset($mapping->inherited)\n                ? $this->getSQLTableAlias($mapping->inherited)\n                : $baseTableAlias;\n\n            $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n\n            foreach ($mapping->joinColumns as $joinColumn) {\n                $columnList[] = $this->getSelectJoinColumnSQL(\n                    $tableAlias,\n                    $joinColumn->name,\n                    $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform),\n                    PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em),\n                );\n            }\n        }\n\n        // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult).\n        $tableAlias = $this->class->rootEntityName === $this->class->name\n            ? $baseTableAlias\n            : $this->getSQLTableAlias($this->class->rootEntityName);\n\n        $columnList[] = $tableAlias . '.' . $discrColumnName;\n\n        // sub tables\n        foreach ($this->class->subClasses as $subClassName) {\n            $subClass   = $this->em->getClassMetadata($subClassName);\n            $tableAlias = $this->getSQLTableAlias($subClassName);\n\n            // Add subclass columns\n            foreach ($subClass->fieldMappings as $fieldName => $mapping) {\n                if (isset($mapping->inherited)) {\n                    continue;\n                }\n\n                $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);\n            }\n\n            // Add join columns (foreign keys)\n            foreach ($subClass->associationMappings as $mapping) {\n                if (! $mapping->isToOneOwningSide() || isset($mapping->inherited)) {\n                    continue;\n                }\n\n                $targetClass = $this->em->getClassMetadata($mapping->targetEntity);\n\n                foreach ($mapping->joinColumns as $joinColumn) {\n                    $columnList[] = $this->getSelectJoinColumnSQL(\n                        $tableAlias,\n                        $joinColumn->name,\n                        $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform),\n                        PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em),\n                    );\n                }\n            }\n        }\n\n        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);\n        $this->updateFilterHash();\n\n        return $this->currentPersisterContext->selectColumnListSql;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function getInsertColumnList(): array\n    {\n        // Identifier columns must always come first in the column list of subclasses.\n        $columns = $this->class->parentClasses\n            ? $this->class->getIdentifierColumnNames()\n            : [];\n\n        foreach ($this->class->propertyAccessors as $name => $field) {\n            if (\n                isset($this->class->fieldMappings[$name]->inherited)\n                    && ! isset($this->class->fieldMappings[$name]->id)\n                    || isset($this->class->associationMappings[$name]->inherited)\n                    || ($this->class->isVersioned && $this->class->versionField === $name)\n                    || isset($this->class->embeddedClasses[$name])\n                    || isset($this->class->fieldMappings[$name]->notInsertable)\n            ) {\n                continue;\n            }\n\n            if (isset($this->class->associationMappings[$name])) {\n                $assoc = $this->class->associationMappings[$name];\n                if ($assoc->isToOneOwningSide()) {\n                    foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {\n                        $columns[] = $sourceCol;\n                    }\n                }\n            } elseif (\n                $this->class->name !== $this->class->rootEntityName ||\n                    ! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] !== $name\n            ) {\n                $columns[]                = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform);\n                $this->columnTypes[$name] = $this->class->fieldMappings[$name]->type;\n            }\n        }\n\n        // Add discriminator column if it is the topmost class.\n        if ($this->class->name === $this->class->rootEntityName) {\n            $columns[] = $this->class->getDiscriminatorColumn()->name;\n        }\n\n        return $columns;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function assignDefaultVersionAndUpsertableValues(object $entity, array $id): void\n    {\n        $values = $this->fetchVersionAndNotUpsertableValues($this->getVersionedClassMetadata(), $id);\n\n        foreach ($values as $field => $value) {\n            $value = Type::getType($this->class->fieldMappings[$field]->type)->convertToPHPValue($value, $this->platform);\n\n            $this->class->setFieldValue($entity, $field, $value);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function fetchVersionAndNotUpsertableValues(ClassMetadata $versionedClass, array $id): mixed\n    {\n        $columnNames = [];\n        foreach ($this->class->fieldMappings as $key => $column) {\n            $class = null;\n            if ($this->class->isVersioned && $key === $versionedClass->versionField) {\n                $class = $versionedClass;\n            } elseif (isset($column->generated)) {\n                $class = isset($column->inherited)\n                    ? $this->em->getClassMetadata($column->inherited)\n                    : $this->class;\n            } else {\n                continue;\n            }\n\n            $columnNames[$key] = $this->getSelectColumnSQL($key, $class);\n        }\n\n        $tableName      = $this->quoteStrategy->getTableName($versionedClass, $this->platform);\n        $baseTableAlias = $this->getSQLTableAlias($this->class->name);\n        $joinSql        = $this->getJoinSql($baseTableAlias);\n        $identifier     = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform);\n        foreach ($identifier as $i => $idValue) {\n            $identifier[$i] = $baseTableAlias . '.' . $idValue;\n        }\n\n        $sql = 'SELECT ' . implode(', ', $columnNames)\n            . ' FROM ' . $tableName . ' ' . $baseTableAlias\n            . $joinSql\n            . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';\n\n        $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id);\n        $values = $this->conn->fetchNumeric(\n            $sql,\n            array_values($flatId),\n            $this->extractIdentifierTypes($id, $versionedClass),\n        );\n\n        if ($values === false) {\n            throw new LengthException('Unexpected empty result for database query.');\n        }\n\n        $values = array_combine(array_keys($columnNames), $values);\n\n        if (! $values) {\n            throw new LengthException('Unexpected number of database columns.');\n        }\n\n        return $values;\n    }\n\n    private function getJoinSql(string $baseTableAlias): string\n    {\n        $joinSql          = '';\n        $identifierColumn = $this->class->getIdentifierColumnNames();\n\n        // INNER JOIN parent tables\n        foreach ($this->class->parentClasses as $parentClassName) {\n            $conditions  = [];\n            $parentClass = $this->em->getClassMetadata($parentClassName);\n            $tableAlias  = $this->getSQLTableAlias($parentClassName);\n            $joinSql    .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';\n\n            foreach ($identifierColumn as $idColumn) {\n                $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;\n            }\n\n            $joinSql .= implode(' AND ', $conditions);\n        }\n\n        // OUTER JOIN sub tables\n        foreach ($this->class->subClasses as $subClassName) {\n            $conditions = [];\n            $subClass   = $this->em->getClassMetadata($subClassName);\n            $tableAlias = $this->getSQLTableAlias($subClassName);\n            $joinSql   .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';\n\n            foreach ($identifierColumn as $idColumn) {\n                $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;\n            }\n\n            $joinSql .= implode(' AND ', $conditions);\n        }\n\n        return $joinSql;\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Entity/SingleTablePersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Entity;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\n\nuse function array_flip;\nuse function array_intersect;\nuse function array_map;\nuse function array_unshift;\nuse function implode;\nuse function strval;\n\n/**\n * Persister for entities that participate in a hierarchy mapped with the\n * SINGLE_TABLE strategy.\n *\n * @link https://martinfowler.com/eaaCatalog/singleTableInheritance.html\n */\nclass SingleTablePersister extends AbstractEntityInheritancePersister\n{\n    use SQLResultCasing;\n\n    protected function getDiscriminatorColumnTableName(): string\n    {\n        return $this->class->getTableName();\n    }\n\n    protected function getSelectColumnsSQL(): string\n    {\n        $columnList = [];\n        if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {\n            return $this->currentPersisterContext->selectColumnListSql;\n        }\n\n        $columnList[] = parent::getSelectColumnsSQL();\n\n        $rootClass  = $this->em->getClassMetadata($this->class->rootEntityName);\n        $tableAlias = $this->getSQLTableAlias($rootClass->name);\n\n        // Append discriminator column\n        $discrColumn     = $this->class->getDiscriminatorColumn();\n        $discrColumnName = $discrColumn->name;\n        $discrColumnType = $discrColumn->type;\n\n        $columnList[] = $tableAlias . '.' . $discrColumnName;\n\n        $resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName);\n\n        $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);\n        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);\n\n        // Append subclass columns\n        foreach ($this->class->subClasses as $subClassName) {\n            $subClass = $this->em->getClassMetadata($subClassName);\n\n            // Regular columns\n            foreach ($subClass->fieldMappings as $fieldName => $mapping) {\n                if (isset($mapping->inherited)) {\n                    continue;\n                }\n\n                $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);\n            }\n\n            // Foreign key columns\n            foreach ($subClass->associationMappings as $assoc) {\n                if (! $assoc->isToOneOwningSide() || isset($assoc->inherited)) {\n                    continue;\n                }\n\n                $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n\n                foreach ($assoc->joinColumns as $joinColumn) {\n                    $columnList[] = $this->getSelectJoinColumnSQL(\n                        $tableAlias,\n                        $joinColumn->name,\n                        $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform),\n                        PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em),\n                    );\n                }\n            }\n        }\n\n        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);\n        $this->updateFilterHash();\n\n        return $this->currentPersisterContext->selectColumnListSql;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function getInsertColumnList(): array\n    {\n        $columns = parent::getInsertColumnList();\n\n        // Add discriminator column to the INSERT SQL\n        $columns[] = $this->class->getDiscriminatorColumn()->name;\n\n        return $columns;\n    }\n\n    protected function getSQLTableAlias(string $className, string $assocName = ''): string\n    {\n        return parent::getSQLTableAlias($this->class->rootEntityName, $assocName);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function getSelectConditionSQL(array $criteria, AssociationMapping|null $assoc = null): string\n    {\n        $conditionSql = parent::getSelectConditionSQL($criteria, $assoc);\n\n        if ($conditionSql) {\n            $conditionSql .= ' AND ';\n        }\n\n        return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL();\n    }\n\n    protected function getSelectConditionCriteriaSQL(Criteria $criteria): string\n    {\n        $conditionSql = parent::getSelectConditionCriteriaSQL($criteria);\n\n        if ($conditionSql) {\n            $conditionSql .= ' AND ';\n        }\n\n        return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL();\n    }\n\n    protected function getSelectConditionDiscriminatorValueSQL(): string\n    {\n        $values = array_map($this->conn->quote(...), array_map(\n            strval(...),\n            array_flip(array_intersect($this->class->discriminatorMap, $this->class->subClasses)),\n        ));\n\n        if ($this->class->discriminatorValue !== null) { // discriminators can be 0\n            array_unshift($values, $this->conn->quote((string) $this->class->discriminatorValue));\n        }\n\n        $discColumnName = $this->class->getDiscriminatorColumn()->name;\n\n        $values     = implode(', ', $values);\n        $tableAlias = $this->getSQLTableAlias($this->class->name);\n\n        return $tableAlias . '.' . $discColumnName . ' IN (' . $values . ')';\n    }\n\n    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        // Ensure that the filters are applied to the root entity of the inheritance tree\n        $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);\n        // we don't care about the $targetTableAlias, in a STI there is only one table.\n\n        return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias);\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Exception/CantUseInOperatorOnCompositeKeys.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Exception;\n\nuse Doctrine\\ORM\\Exception\\PersisterException;\n\nclass CantUseInOperatorOnCompositeKeys extends PersisterException\n{\n    public static function create(): self\n    {\n        return new self(\"Can't use IN operator on entities that have composite keys.\");\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Exception/InvalidOrientation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Exception;\n\nuse Doctrine\\ORM\\Exception\\PersisterException;\n\nclass InvalidOrientation extends PersisterException\n{\n    public static function fromClassNameAndField(string $className, string $field): self\n    {\n        return new self('Invalid order by orientation specified for ' . $className . '#' . $field);\n    }\n}\n"
  },
  {
    "path": "src/Persisters/Exception/UnrecognizedField.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters\\Exception;\n\nuse Doctrine\\ORM\\Exception\\PersisterException;\n\nuse function sprintf;\n\nfinal class UnrecognizedField extends PersisterException\n{\n    /** @deprecated Use {@see byFullyQualifiedName()} instead. */\n    public static function byName(string $field): self\n    {\n        return new self(sprintf('Unrecognized field: %s', $field));\n    }\n\n    /** @param class-string $className */\n    public static function byFullyQualifiedName(string $className, string $field): self\n    {\n        return new self(sprintf('Unrecognized field: %s::$%s', $className, $field));\n    }\n}\n"
  },
  {
    "path": "src/Persisters/MatchingAssociationFieldRequiresObject.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters;\n\nuse Doctrine\\ORM\\Exception\\PersisterException;\n\nuse function sprintf;\n\nfinal class MatchingAssociationFieldRequiresObject extends PersisterException\n{\n    public static function fromClassAndAssociation(string $class, string $associationName): self\n    {\n        return new self(sprintf(\n            'Cannot match on %s::%s with a non-object value. Matching objects by id is ' .\n            'not compatible with matching on an in-memory collection, which compares objects by reference.',\n            $class,\n            $associationName,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Persisters/PersisterException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Exception;\n\nuse function sprintf;\n\nclass PersisterException extends Exception implements ORMException\n{\n    public static function matchingAssocationFieldRequiresObject(string $class, string $associationName): PersisterException\n    {\n        return new self(sprintf(\n            'Cannot match on %s::%s with a non-object value. Matching objects by id is ' .\n            'not compatible with matching on an in-memory collection, which compares objects by reference.',\n            $class,\n            $associationName,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Persisters/SqlExpressionVisitor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters;\n\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Common\\Collections\\Expr\\CompositeExpression;\nuse Doctrine\\Common\\Collections\\Expr\\ExpressionVisitor;\nuse Doctrine\\Common\\Collections\\Expr\\Value;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse RuntimeException;\n\nuse function implode;\nuse function in_array;\nuse function is_object;\n\n/**\n * Visit Expressions and generate SQL WHERE conditions from them.\n */\nclass SqlExpressionVisitor extends ExpressionVisitor\n{\n    public function __construct(\n        private readonly BasicEntityPersister $persister,\n        private readonly ClassMetadata $classMetadata,\n    ) {\n    }\n\n    /** Converts a comparison expression into the target query language output. */\n    public function walkComparison(Comparison $comparison): string\n    {\n        $field = $comparison->getField();\n        $value = $comparison->getValue()->getValue(); // shortcut for walkValue()\n\n        if (\n            isset($this->classMetadata->associationMappings[$field]) &&\n            $value !== null &&\n            ! is_object($value) &&\n            ! in_array($comparison->getOperator(), [Comparison::IN, Comparison::NIN], true)\n        ) {\n            throw MatchingAssociationFieldRequiresObject::fromClassAndAssociation(\n                $this->classMetadata->name,\n                $field,\n            );\n        }\n\n        return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator());\n    }\n\n    /**\n     * Converts a composite expression into the target query language output.\n     *\n     * @throws RuntimeException\n     */\n    public function walkCompositeExpression(CompositeExpression $expr): string\n    {\n        $expressionList = [];\n\n        foreach ($expr->getExpressionList() as $child) {\n            $expressionList[] = $this->dispatch($child);\n        }\n\n        return match ($expr->getType()) {\n            CompositeExpression::TYPE_AND => '(' . implode(' AND ', $expressionList) . ')',\n            CompositeExpression::TYPE_OR => '(' . implode(' OR ', $expressionList) . ')',\n            CompositeExpression::TYPE_NOT => 'NOT (' . $expressionList[0] . ')',\n            default => throw new RuntimeException('Unknown composite ' . $expr->getType()),\n        };\n    }\n\n    /**\n     * Converts a value expression into the target query language part.\n     */\n    public function walkValue(Value $value): string\n    {\n        return '?';\n    }\n}\n"
  },
  {
    "path": "src/Persisters/SqlValueVisitor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Persisters;\n\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Common\\Collections\\Expr\\CompositeExpression;\nuse Doctrine\\Common\\Collections\\Expr\\ExpressionVisitor;\nuse Doctrine\\Common\\Collections\\Expr\\Value;\n\n/**\n * Extract the values from a criteria/expression\n */\nclass SqlValueVisitor extends ExpressionVisitor\n{\n    /** @var mixed[] */\n    private array $values = [];\n\n    /** @var mixed[][] */\n    private array $types = [];\n\n    /**\n     * Converts a comparison expression into the target query language output.\n     *\n     * {@inheritDoc}\n     */\n    public function walkComparison(Comparison $comparison)\n    {\n        $value = $this->getValueFromComparison($comparison);\n\n        $this->values[] = $value;\n        $this->types[]  = [$comparison->getField(), $value, $comparison->getOperator()];\n\n        return null;\n    }\n\n    /**\n     * Converts a composite expression into the target query language output.\n     *\n     * {@inheritDoc}\n     */\n    public function walkCompositeExpression(CompositeExpression $expr)\n    {\n        foreach ($expr->getExpressionList() as $child) {\n            $this->dispatch($child);\n        }\n\n        return null;\n    }\n\n    /**\n     * Converts a value expression into the target query language part.\n     *\n     * {@inheritDoc}\n     */\n    public function walkValue(Value $value)\n    {\n        return null;\n    }\n\n    /**\n     * Returns the Parameters and Types necessary for matching the last visited expression.\n     *\n     * @return mixed[][]\n     * @phpstan-return array{0: array, 1: array<array<mixed>>}\n     */\n    public function getParamsAndTypes(): array\n    {\n        return [$this->values, $this->types];\n    }\n\n    /**\n     * Returns the value from a Comparison. In case of a CONTAINS comparison,\n     * the value is wrapped in %-signs, because it will be used in a LIKE clause.\n     */\n    protected function getValueFromComparison(Comparison $comparison): mixed\n    {\n        $value = $comparison->getValue()->getValue();\n\n        return match ($comparison->getOperator()) {\n            Comparison::CONTAINS => '%' . $value . '%',\n            Comparison::STARTS_WITH => $value . '%',\n            Comparison::ENDS_WITH => '%' . $value,\n            default => $value,\n        };\n    }\n}\n"
  },
  {
    "path": "src/PessimisticLockException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse RuntimeException;\n\nclass PessimisticLockException extends RuntimeException implements ORMException\n{\n    public static function lockFailed(): self\n    {\n        return new self('The pessimistic lock failed.');\n    }\n}\n"
  },
  {
    "path": "src/Proxy/Autoloader.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Proxy;\n\nuse Closure;\nuse Doctrine\\Deprecations\\Deprecation;\n\nuse function file_exists;\nuse function ltrim;\nuse function spl_autoload_register;\nuse function str_replace;\nuse function str_starts_with;\nuse function strlen;\nuse function substr;\n\nuse const DIRECTORY_SEPARATOR;\nuse const PHP_VERSION_ID;\n\n/**\n * Special Autoloader for Proxy classes, which are not PSR-0 compliant.\n */\nfinal class Autoloader\n{\n    /**\n     * Resolves proxy class name to a filename based on the following pattern.\n     *\n     * 1. Remove Proxy namespace from class name.\n     * 2. Remove namespace separators from remaining class name.\n     * 3. Return PHP filename from proxy-dir with the result from 2.\n     *\n     * @phpstan-param class-string $className\n     *\n     * @throws NotAProxyClass\n     */\n    public static function resolveFile(string $proxyDir, string $proxyNamespace, string $className): string\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Class \"%s\" is deprecated. Use native lazy objects instead.',\n                self::class,\n            );\n        }\n\n        if (! str_starts_with($className, $proxyNamespace)) {\n            throw new NotAProxyClass($className, $proxyNamespace);\n        }\n\n        // remove proxy namespace from class name\n        $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace));\n\n        // remove namespace separators from remaining class name\n        $fileName = str_replace('\\\\', '', $classNameRelativeToProxyNamespace);\n\n        return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php';\n    }\n\n    /**\n     * Registers and returns autoloader callback for the given proxy dir and namespace.\n     *\n     * @param Closure(string, string, class-string): void|null $notFoundCallback Invoked when the proxy file is not found.\n     *\n     * @return Closure(string): void\n     */\n    public static function register(\n        string $proxyDir,\n        string $proxyNamespace,\n        Closure|null $notFoundCallback = null,\n    ): Closure {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Class \"%s\" is deprecated. Use native lazy objects instead.',\n                self::class,\n            );\n        }\n\n        $proxyNamespace = ltrim($proxyNamespace, '\\\\');\n\n        $autoloader = /** @param class-string $className */ static function (string $className) use ($proxyDir, $proxyNamespace, $notFoundCallback): void {\n            if ($proxyNamespace === '') {\n                return;\n            }\n\n            if (! str_starts_with($className, $proxyNamespace)) {\n                return;\n            }\n\n            $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);\n\n            if ($notFoundCallback && ! file_exists($file)) {\n                $notFoundCallback($proxyDir, $proxyNamespace, $className);\n            }\n\n            require $file;\n        };\n\n        spl_autoload_register($autoloader);\n\n        return $autoloader;\n    }\n}\n"
  },
  {
    "path": "src/Proxy/DefaultProxyClassNameResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Proxy;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\Persistence\\Mapping\\ProxyClassNameResolver;\nuse Doctrine\\Persistence\\Proxy;\n\nuse function strrpos;\nuse function substr;\n\nuse const PHP_VERSION_ID;\n\n/**\n * Class-related functionality for objects that might or not be proxy objects\n * at the moment.\n */\nfinal class DefaultProxyClassNameResolver implements ProxyClassNameResolver\n{\n    public function resolveClassName(string $className): string\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::triggerIfCalledFromOutside(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Class \"%s\" is deprecated. Use native lazy objects instead.',\n                self::class,\n            );\n        }\n\n        $pos = strrpos($className, '\\\\' . Proxy::MARKER . '\\\\');\n\n        if ($pos === false) {\n            return $className;\n        }\n\n        return substr($className, $pos + Proxy::MARKER_LENGTH + 2);\n    }\n\n    /** @return class-string */\n    public static function getClass(object $object): string\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::triggerIfCalledFromOutside(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Class \"%s\" is deprecated. Use native lazy objects instead.',\n                self::class,\n            );\n        }\n\n        return (new self())->resolveClassName($object::class);\n    }\n}\n"
  },
  {
    "path": "src/Proxy/InternalProxy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Proxy;\n\nuse Doctrine\\Persistence\\Proxy;\n\n/**\n * @internal\n *\n * @template T of object\n * @template-extends Proxy<T>\n */\ninterface InternalProxy extends Proxy\n{\n    public function __setInitialized(bool $initialized): void;\n}\n"
  },
  {
    "path": "src/Proxy/NotAProxyClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Proxy;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse InvalidArgumentException;\n\nuse function sprintf;\n\nfinal class NotAProxyClass extends InvalidArgumentException implements ORMException\n{\n    public function __construct(string $className, string $proxyNamespace)\n    {\n        parent::__construct(sprintf(\n            'The class \"%s\" is not part of the proxy namespace \"%s\"',\n            $className,\n            $proxyNamespace,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Proxy/ProxyFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Proxy;\n\nuse Closure;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityNotFoundException;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Proxy;\nuse LogicException;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse Symfony\\Component\\VarExporter\\ProxyHelper;\n\nuse function array_combine;\nuse function array_flip;\nuse function array_keys;\nuse function assert;\nuse function bin2hex;\nuse function chmod;\nuse function class_exists;\nuse function count;\nuse function dirname;\nuse function file_exists;\nuse function file_put_contents;\nuse function filemtime;\nuse function func_num_args;\nuse function is_bool;\nuse function is_dir;\nuse function is_int;\nuse function is_writable;\nuse function ltrim;\nuse function method_exists;\nuse function mkdir;\nuse function preg_match_all;\nuse function random_bytes;\nuse function rename;\nuse function rtrim;\nuse function sprintf;\nuse function str_replace;\nuse function strpos;\nuse function strrpos;\nuse function strtr;\nuse function substr;\nuse function ucfirst;\n\nuse const DIRECTORY_SEPARATOR;\nuse const PHP_VERSION_ID;\n\n/**\n * This factory is used to create proxy objects for entities at runtime.\n */\nclass ProxyFactory\n{\n    /**\n     * Never autogenerate a proxy and rely that it was generated by some\n     * process before deployment.\n     */\n    public const AUTOGENERATE_NEVER = 0;\n\n    /**\n     * Always generates a new proxy in every request.\n     *\n     * This is only sane during development.\n     */\n    public const AUTOGENERATE_ALWAYS = 1;\n\n    /**\n     * Autogenerate the proxy class when the proxy file does not exist.\n     *\n     * This strategy causes a file_exists() call whenever any proxy is used the\n     * first time in a request.\n     */\n    public const AUTOGENERATE_FILE_NOT_EXISTS = 2;\n\n    /**\n     * Generate the proxy classes using eval().\n     *\n     * This strategy is only sane for development, and even then it gives me\n     * the creeps a little.\n     */\n    public const AUTOGENERATE_EVAL = 3;\n\n    /**\n     * Autogenerate the proxy class when the proxy file does not exist or\n     * when the proxied file changed.\n     *\n     * This strategy causes a file_exists() call whenever any proxy is used the\n     * first time in a request. When the proxied file is changed, the proxy will\n     * be updated.\n     */\n    public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;\n\n    private const PROXY_CLASS_TEMPLATE = <<<'EOPHP'\n<?php\n\nnamespace <namespace>;\n\n/**\n * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR\n */\nclass <proxyShortClassName> extends \\<className> implements \\<baseProxyInterface>\n{\n    <useLazyGhostTrait>\n\n    public function __isInitialized(): bool\n    {\n        return isset($this->lazyObjectState) && $this->isLazyObjectInitialized();\n    }\n\n    public function __serialize(): array\n    {\n        <serializeImpl>\n    }\n}\n\nEOPHP;\n\n    /** The UnitOfWork this factory uses to retrieve persisters */\n    private readonly UnitOfWork $uow;\n\n    /** @var self::AUTOGENERATE_* */\n    private $autoGenerate;\n\n    /** The IdentifierFlattener used for manipulating identifiers */\n    private readonly IdentifierFlattener $identifierFlattener;\n\n    /** @var array<class-string, Closure> */\n    private array $proxyFactories = [];\n\n    private readonly string $proxyDir;\n    private readonly string $proxyNs;\n\n    /**\n     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is\n     * connected to the given <tt>EntityManager</tt>.\n     *\n     * @param EntityManagerInterface    $em           The EntityManager the new factory works for.\n     * @param string                    $proxyDir     The directory to use for the proxy classes. It must exist.\n     * @param string                    $proxyNs      The namespace to use for the proxy classes.\n     * @param bool|self::AUTOGENERATE_* $autoGenerate The strategy for automatically generating proxy classes.\n     */\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        string|null $proxyDir = null,\n        string|null $proxyNs = null,\n        bool|int $autoGenerate = self::AUTOGENERATE_NEVER,\n    ) {\n        if (! $em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            if (PHP_VERSION_ID >= 80400) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/pull/12005',\n                    'Not enabling native lazy objects is deprecated and will be impossible in Doctrine ORM 4.0.',\n                );\n            }\n\n            // @phpstan-ignore function.impossibleType (This method has been removed in Symfony 8)\n            if (! method_exists(ProxyHelper::class, 'generateLazyGhost')) {\n                throw ORMInvalidArgumentException::lazyGhostUnavailable();\n            }\n\n            if (! $proxyDir) {\n                throw ORMInvalidArgumentException::proxyDirectoryRequired();\n            }\n\n            if (! $proxyNs) {\n                throw ORMInvalidArgumentException::proxyNamespaceRequired();\n            }\n        } elseif (PHP_VERSION_ID >= 80400 && func_num_args() > 1) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Passing more than just the EntityManager to the %s is deprecated and will not be possible in Doctrine ORM 4.0.',\n                __METHOD__,\n            );\n        }\n\n        if (is_int($autoGenerate) ? $autoGenerate < 0 || $autoGenerate > 4 : ! is_bool($autoGenerate)) {\n            throw ORMInvalidArgumentException::invalidAutoGenerateMode($autoGenerate);\n        }\n\n        if ($proxyDir === null && $em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            $proxyDir = '';\n        }\n\n        if ($proxyNs === null && $em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            $proxyNs = '';\n        }\n\n        $this->proxyDir = $proxyDir;\n        $this->proxyNs  = $proxyNs;\n\n        $this->uow                 = $em->getUnitOfWork();\n        $this->autoGenerate        = (int) $autoGenerate;\n        $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());\n    }\n\n    /**\n     * @param class-string $className\n     * @param array<mixed> $identifier\n     */\n    public function getProxy(string $className, array $identifier): object\n    {\n        if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            $classMetadata       = $this->em->getClassMetadata($className);\n            $entityPersister     = $this->uow->getEntityPersister($className);\n            $identifierFlattener = $this->identifierFlattener;\n\n            $proxy = $classMetadata->reflClass->newLazyGhost(static function (object $object) use (\n                $identifier,\n                $entityPersister,\n                $identifierFlattener,\n                $classMetadata,\n            ): void {\n                $original = $entityPersister->loadById($identifier, $object);\n                if ($original === null) {\n                    throw EntityNotFoundException::fromClassNameAndIdentifier(\n                        $classMetadata->getName(),\n                        $identifierFlattener->flattenIdentifier($classMetadata, $identifier),\n                    );\n                }\n            }, ReflectionClass::SKIP_INITIALIZATION_ON_SERIALIZE);\n\n            foreach ($identifier as $idField => $value) {\n                $classMetadata->propertyAccessors[$idField]->setValue($proxy, $value);\n            }\n\n            return $proxy;\n        }\n\n        $proxyFactory = $this->proxyFactories[$className] ?? $this->getProxyFactory($className);\n\n        return $proxyFactory($identifier);\n    }\n\n    /**\n     * Generates proxy classes for all given classes.\n     *\n     * @param ClassMetadata[] $classes  The classes (ClassMetadata instances) for which to generate proxies.\n     * @param string|null     $proxyDir The target directory of the proxy classes. If not specified, the\n     *                                  directory configured on the Configuration of the EntityManager used\n     *                                  by this factory is used.\n     *\n     * @return int Number of generated proxies.\n     */\n    public function generateProxyClasses(array $classes, string|null $proxyDir = null): int\n    {\n        if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            return 0;\n        }\n\n        $generated = 0;\n\n        foreach ($classes as $class) {\n            if ($this->skipClass($class)) {\n                continue;\n            }\n\n            $proxyFileName  = $this->getProxyFileName($class->getName(), $proxyDir ?: $this->proxyDir);\n            $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);\n\n            $this->generateProxyClass($class, $proxyFileName, $proxyClassName);\n\n            ++$generated;\n        }\n\n        return $generated;\n    }\n\n    protected function skipClass(ClassMetadata $metadata): bool\n    {\n        return $metadata->isMappedSuperclass\n            || $metadata->isEmbeddedClass\n            || $metadata->getReflectionClass()->isAbstract();\n    }\n\n    /**\n     * Creates a closure capable of initializing a proxy\n     *\n     * @return Closure(InternalProxy, array):void\n     *\n     * @throws EntityNotFoundException\n     */\n    private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure\n    {\n        return static function (InternalProxy $proxy, array $identifier) use ($entityPersister, $classMetadata, $identifierFlattener): void {\n            $original = $entityPersister->loadById($identifier);\n\n            if ($original === null) {\n                throw EntityNotFoundException::fromClassNameAndIdentifier(\n                    $classMetadata->getName(),\n                    $identifierFlattener->flattenIdentifier($classMetadata, $identifier),\n                );\n            }\n\n            if ($proxy === $original) {\n                return;\n            }\n\n            $class = $entityPersister->getClassMetadata();\n\n            foreach ($class->getPropertyAccessors() as $name => $property) {\n                if (isset($identifier[$name])) {\n                    continue;\n                }\n\n                $property->setValue($proxy, $property->getValue($original));\n            }\n        };\n    }\n\n    private function getProxyFileName(string $className, string $baseDirectory): string\n    {\n        $baseDirectory = $baseDirectory ?: $this->proxyDir;\n\n        return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . InternalProxy::MARKER\n            . str_replace('\\\\', '', $className) . '.php';\n    }\n\n    private function getProxyFactory(string $className): Closure\n    {\n        $skippedProperties = [];\n        $class             = $this->em->getClassMetadata($className);\n        $identifiers       = array_flip($class->getIdentifierFieldNames());\n        $filter            = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;\n        $reflector         = $class->getReflectionClass();\n\n        while ($reflector) {\n            foreach ($reflector->getProperties($filter) as $property) {\n                $name = $property->name;\n\n                if (PHP_VERSION_ID >= 80400 && count($property->getHooks()) > 0) {\n                    throw new LogicException(sprintf(\n                        'Doctrine ORM does not support property hook on %s::%s without using native lazy objects. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.',\n                        $property->getDeclaringClass()->getName(),\n                        $property->getName(),\n                    ));\n                }\n\n                if ($property->isStatic() || ! isset($identifiers[$name])) {\n                    continue;\n                }\n\n                $prefix = $property->isPrivate() ? \"\\0\" . $property->class . \"\\0\" : ($property->isProtected() ? \"\\0*\\0\" : '');\n\n                $skippedProperties[$prefix . $name] = true;\n            }\n\n            $filter    = ReflectionProperty::IS_PRIVATE;\n            $reflector = $reflector->getParentClass();\n        }\n\n        $className        = $class->getName(); // aliases and case sensitivity\n        $entityPersister  = $this->uow->getEntityPersister($className);\n        $initializer      = $this->createLazyInitializer($class, $entityPersister, $this->identifierFlattener);\n        $proxyClassName   = $this->loadProxyClass($class);\n        $identifierFields = [];\n\n        foreach (array_keys($identifiers) as $identifier) {\n            $identifierFields[$identifier] = $class->getPropertyAccessor($identifier);\n        }\n\n        $proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy {\n            $proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {\n                $initializer($object, $identifier);\n            }, $skippedProperties);\n\n            foreach ($identifierFields as $idField => $reflector) {\n                if (! isset($identifier[$idField])) {\n                    throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField);\n                }\n\n                assert($reflector !== null);\n                $reflector->setValue($proxy, $identifier[$idField]);\n            }\n\n            return $proxy;\n        }, null, $proxyClassName);\n\n        return $this->proxyFactories[$className] = $proxyFactory;\n    }\n\n    private function loadProxyClass(ClassMetadata $class): string\n    {\n        $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);\n\n        if (class_exists($proxyClassName, false)) {\n            return $proxyClassName;\n        }\n\n        if ($this->autoGenerate === self::AUTOGENERATE_EVAL) {\n            $this->generateProxyClass($class, null, $proxyClassName);\n\n            return $proxyClassName;\n        }\n\n        $fileName = $this->getProxyFileName($class->getName(), $this->proxyDir);\n\n        switch ($this->autoGenerate) {\n            case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:\n                if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) {\n                    break;\n                }\n                // no break\n            case self::AUTOGENERATE_FILE_NOT_EXISTS:\n                if (file_exists($fileName)) {\n                    break;\n                }\n                // no break\n            case self::AUTOGENERATE_ALWAYS:\n                $this->generateProxyClass($class, $fileName, $proxyClassName);\n                break;\n        }\n\n        require $fileName;\n\n        return $proxyClassName;\n    }\n\n    private function generateProxyClass(ClassMetadata $class, string|null $fileName, string $proxyClassName): void\n    {\n        $i            = strrpos($proxyClassName, '\\\\');\n        $placeholders = [\n            '<className>' => $class->getName(),\n            '<namespace>' => substr($proxyClassName, 0, $i),\n            '<proxyShortClassName>' => substr($proxyClassName, 1 + $i),\n            '<baseProxyInterface>' => InternalProxy::class,\n        ];\n\n        preg_match_all('(<([a-zA-Z]+)>)', self::PROXY_CLASS_TEMPLATE, $placeholderMatches);\n\n        foreach (array_combine($placeholderMatches[0], $placeholderMatches[1]) as $placeholder => $name) {\n            $placeholders[$placeholder] ?? $placeholders[$placeholder] = $this->{'generate' . ucfirst($name)}($class);\n        }\n\n        $proxyCode = strtr(self::PROXY_CLASS_TEMPLATE, $placeholders);\n\n        if (! $fileName) {\n            if (! class_exists($proxyClassName)) {\n                eval(substr($proxyCode, 5));\n            }\n\n            return;\n        }\n\n        $parentDirectory = dirname($fileName);\n\n        if (! is_dir($parentDirectory) && ! @mkdir($parentDirectory, 0775, true)) {\n            throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);\n        }\n\n        if (! is_writable($parentDirectory)) {\n            throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);\n        }\n\n        $tmpFileName = $fileName . '.' . bin2hex(random_bytes(12));\n\n        file_put_contents($tmpFileName, $proxyCode);\n        @chmod($tmpFileName, 0664);\n        rename($tmpFileName, $fileName);\n    }\n\n    private function generateUseLazyGhostTrait(ClassMetadata $class): string\n    {\n        // @phpstan-ignore staticMethod.notFound (This method has been removed in Symfony 8)\n        $code = ProxyHelper::generateLazyGhost($class->getReflectionClass());\n        $code = substr($code, 7 + (int) strpos($code, \"\\n{\"));\n        $code = substr($code, 0, (int) strpos($code, \"\\n}\"));\n        $code = str_replace('LazyGhostTrait;', str_replace(\"\\n    \", \"\\n\", 'LazyGhostTrait {\n            initializeLazyObject as private;\n            setLazyObjectAsInitialized as public __setInitialized;\n            isLazyObjectInitialized as private;\n            createLazyGhost as private;\n            resetLazyObject as private;\n        }\n\n        public function __load(): void\n        {\n            $this->initializeLazyObject();\n        }\n        '), $code);\n\n        return $code;\n    }\n\n    private function generateSerializeImpl(ClassMetadata $class): string\n    {\n        $reflector  = $class->getReflectionClass();\n        $properties = $reflector->hasMethod('__serialize') ? 'parent::__serialize()' : '(array) $this';\n\n        $code = '$properties = ' . $properties . ';\n        unset($properties[\"\\0\" . self::class . \"\\0lazyObjectState\"]);\n\n        ';\n\n        if ($reflector->hasMethod('__serialize') || ! $reflector->hasMethod('__sleep')) {\n            return $code . 'return $properties;';\n        }\n\n        return $code . '$data = [];\n\n        foreach (parent::__sleep() as $name) {\n            $value = $properties[$k = $name] ?? $properties[$k = \"\\0*\\0$name\"] ?? $properties[$k = \"\\0' . $reflector->name . '\\0$name\"] ?? $k = null;\n\n            if (null === $k) {\n                trigger_error(sprintf(\\'serialize(): \"%s\" returned as member variable from __sleep() but does not exist\\', $name), \\E_USER_NOTICE);\n            } else {\n                $data[$k] = $value;\n            }\n        }\n\n        return $data;';\n    }\n\n    private static function generateProxyClassName(string $className, string $proxyNamespace): string\n    {\n        return rtrim($proxyNamespace, '\\\\') . '\\\\' . Proxy::MARKER . '\\\\' . ltrim($className, '\\\\');\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ASTException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\QueryException;\n\nuse function get_debug_type;\n\n/**\n * Base exception class for AST exceptions.\n */\nclass ASTException extends QueryException\n{\n    public static function noDispatchForNode(Node $node): self\n    {\n        return new self('Double-dispatch for node ' . get_debug_type($node) . ' is not supported.');\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/AggregateExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass AggregateExpression extends Node\n{\n    /** @param bool $isDistinct Some aggregate expressions support distinct, eg COUNT. */\n    public function __construct(\n        public string $functionName,\n        public Node|string $pathExpression,\n        public bool $isDistinct,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkAggregateExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ArithmeticExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ArithmeticExpression ::= SimpleArithmeticExpression | \"(\" Subselect \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass ArithmeticExpression extends Node\n{\n    public Node|string|null $simpleArithmeticExpression = null;\n\n    public Subselect|null $subselect = null;\n\n    public function isSimpleArithmeticExpression(): bool\n    {\n        return (bool) $this->simpleArithmeticExpression;\n    }\n\n    public function isSubselect(): bool\n    {\n        return (bool) $this->subselect;\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkArithmeticExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ArithmeticFactor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ArithmeticFactor ::= [(\"+\" | \"-\")] ArithmeticPrimary\n *\n * @link    www.doctrine-project.org\n */\nclass ArithmeticFactor extends Node\n{\n    public function __construct(\n        public mixed $arithmeticPrimary,\n        public bool|null $sign = null,\n    ) {\n    }\n\n    public function isPositiveSigned(): bool\n    {\n        return $this->sign === true;\n    }\n\n    public function isNegativeSigned(): bool\n    {\n        return $this->sign === false;\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkArithmeticFactor($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ArithmeticTerm.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ArithmeticTerm ::= ArithmeticFactor {(\"*\" | \"/\") ArithmeticFactor}*\n *\n * @link    www.doctrine-project.org\n */\nclass ArithmeticTerm extends Node\n{\n    /** @param mixed[] $arithmeticFactors */\n    public function __construct(public array $arithmeticFactors)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkArithmeticTerm($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/BetweenExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass BetweenExpression extends Node\n{\n    public function __construct(\n        public ArithmeticExpression $expression,\n        public ArithmeticExpression $leftBetweenExpression,\n        public ArithmeticExpression $rightBetweenExpression,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkBetweenExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/CoalesceExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * CoalesceExpression ::= \"COALESCE\" \"(\" ScalarExpression {\",\" ScalarExpression}* \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass CoalesceExpression extends Node\n{\n    /** @param mixed[] $scalarExpressions */\n    public function __construct(public array $scalarExpressions)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkCoalesceExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/CollectionMemberExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * CollectionMemberExpression ::= EntityExpression [\"NOT\"] \"MEMBER\" [\"OF\"] CollectionValuedPathExpression\n *\n * @link    www.doctrine-project.org\n */\nclass CollectionMemberExpression extends Node\n{\n    public function __construct(\n        public mixed $entityExpression,\n        public PathExpression $collectionValuedPathExpression,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkCollectionMemberExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ComparisonExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) |\n *                          StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) |\n *                          BooleanExpression (\"=\" | \"<>\" | \"!=\") (BooleanExpression | QuantifiedExpression) |\n *                          EnumExpression (\"=\" | \"<>\" | \"!=\") (EnumExpression | QuantifiedExpression) |\n *                          DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) |\n *                          EntityExpression (\"=\" | \"<>\") (EntityExpression | QuantifiedExpression)\n *\n * @link    www.doctrine-project.org\n */\nclass ComparisonExpression extends Node\n{\n    public function __construct(\n        public Node|string $leftExpression,\n        public string $operator,\n        public Node|string $rightExpression,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkComparisonExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ConditionalExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ConditionalExpression ::= ConditionalTerm {\"OR\" ConditionalTerm}*\n *\n * @link    www.doctrine-project.org\n */\nclass ConditionalExpression extends Node\n{\n    /** @param mixed[] $conditionalTerms */\n    public function __construct(public array $conditionalTerms)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkConditionalExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ConditionalFactor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ConditionalFactor ::= [\"NOT\"] ConditionalPrimary\n *\n * @link    www.doctrine-project.org\n */\nclass ConditionalFactor extends Node implements Phase2OptimizableConditional\n{\n    public function __construct(\n        public ConditionalPrimary $conditionalPrimary,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkConditionalFactor($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ConditionalPrimary.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ConditionalPrimary ::= SimpleConditionalExpression | \"(\" ConditionalExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass ConditionalPrimary extends Node implements Phase2OptimizableConditional\n{\n    public Node|null $simpleConditionalExpression = null;\n\n    public ConditionalExpression|Phase2OptimizableConditional|null $conditionalExpression = null;\n\n    public function isSimpleConditionalExpression(): bool\n    {\n        return (bool) $this->simpleConditionalExpression;\n    }\n\n    public function isConditionalExpression(): bool\n    {\n        return (bool) $this->conditionalExpression;\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkConditionalPrimary($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ConditionalTerm.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ConditionalTerm ::= ConditionalFactor {\"AND\" ConditionalFactor}*\n *\n * @link    www.doctrine-project.org\n */\nclass ConditionalTerm extends Node implements Phase2OptimizableConditional\n{\n    /** @param mixed[] $conditionalFactors */\n    public function __construct(public array $conditionalFactors)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkConditionalTerm($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/DeleteClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * DeleteClause ::= \"DELETE\" [\"FROM\"] AbstractSchemaName [[\"AS\"] AliasIdentificationVariable]\n *\n * @link    www.doctrine-project.org\n */\nclass DeleteClause extends Node\n{\n    public string $aliasIdentificationVariable;\n\n    public function __construct(public string $abstractSchemaName)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkDeleteClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/DeleteStatement.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * DeleteStatement = DeleteClause [WhereClause]\n *\n * @link    www.doctrine-project.org\n */\nclass DeleteStatement extends Node\n{\n    public WhereClause|null $whereClause = null;\n\n    public function __construct(public DeleteClause $deleteClause)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkDeleteStatement($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/EmptyCollectionComparisonExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression \"IS\" [\"NOT\"] \"EMPTY\"\n *\n * @link    www.doctrine-project.org\n */\nclass EmptyCollectionComparisonExpression extends Node\n{\n    public function __construct(\n        public PathExpression $expression,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkEmptyCollectionComparisonExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/EntityAsDtoArgumentExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * EntityAsDtoArgumentExpression ::= IdentificationVariable\n *\n * @link    www.doctrine-project.org\n */\nclass EntityAsDtoArgumentExpression extends Node\n{\n    public function __construct(\n        public mixed $expression,\n        public string|null $identificationVariable,\n        public string|null $aliasVariable = null,\n    ) {\n        if (! $aliasVariable) {\n            $this->aliasVariable = $expression;\n        }\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkEntityAsDtoArgumentExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ExistsExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ExistsExpression ::= [\"NOT\"] \"EXISTS\" \"(\" Subselect \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass ExistsExpression extends Node\n{\n    public function __construct(\n        public Subselect $subselect,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkExistsExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/FromClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * FromClause ::= \"FROM\" IdentificationVariableDeclaration {\",\" IdentificationVariableDeclaration}\n *\n * @link    www.doctrine-project.org\n */\nclass FromClause extends Node\n{\n    /** @param mixed[] $identificationVariableDeclarations */\n    public function __construct(public array $identificationVariableDeclarations)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkFromClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/AbsFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"ABS\" \"(\" SimpleArithmeticExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass AbsFunction extends FunctionNode\n{\n    public Node|string $simpleArithmeticExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression(\n            $this->simpleArithmeticExpression,\n        ) . ')';\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/AvgFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * \"AVG\" \"(\" [\"DISTINCT\"] StringPrimary \")\"\n */\nfinal class AvgFunction extends FunctionNode\n{\n    private AggregateExpression $aggregateExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/BitAndFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"BIT_AND\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass BitAndFunction extends FunctionNode\n{\n    public Node $firstArithmetic;\n    public Node $secondArithmetic;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $platform = $sqlWalker->getConnection()->getDatabasePlatform();\n\n        return $platform->getBitAndComparisonExpression(\n            $this->firstArithmetic->dispatch($sqlWalker),\n            $this->secondArithmetic->dispatch($sqlWalker),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstArithmetic = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->secondArithmetic = $parser->ArithmeticPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/BitOrFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"BIT_OR\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass BitOrFunction extends FunctionNode\n{\n    public Node $firstArithmetic;\n    public Node $secondArithmetic;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $platform = $sqlWalker->getConnection()->getDatabasePlatform();\n\n        return $platform->getBitOrComparisonExpression(\n            $this->firstArithmetic->dispatch($sqlWalker),\n            $this->secondArithmetic->dispatch($sqlWalker),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstArithmetic = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->secondArithmetic = $parser->ArithmeticPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/ConcatFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"CONCAT\" \"(\" StringPrimary \",\" StringPrimary {\",\" StringPrimary }* \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass ConcatFunction extends FunctionNode\n{\n    public Node $firstStringPrimary;\n    public Node $secondStringPrimary;\n\n    /** @phpstan-var list<Node> */\n    public array $concatExpressions = [];\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $platform = $sqlWalker->getConnection()->getDatabasePlatform();\n\n        $args = [];\n\n        foreach ($this->concatExpressions as $expression) {\n            $args[] = $sqlWalker->walkStringPrimary($expression);\n        }\n\n        return $platform->getConcatExpression(...$args);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstStringPrimary  = $parser->StringPrimary();\n        $this->concatExpressions[] = $this->firstStringPrimary;\n\n        $parser->match(TokenType::T_COMMA);\n\n        $this->secondStringPrimary = $parser->StringPrimary();\n        $this->concatExpressions[] = $this->secondStringPrimary;\n\n        while ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {\n            $parser->match(TokenType::T_COMMA);\n            $this->concatExpressions[] = $parser->StringPrimary();\n        }\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/CountFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\AST\\TypedExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * \"COUNT\" \"(\" [\"DISTINCT\"] StringPrimary \")\"\n */\nfinal class CountFunction extends FunctionNode implements TypedExpression\n{\n    private AggregateExpression $aggregateExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n\n    public function getReturnType(): Type\n    {\n        return Type::getType(Types::INTEGER);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/CurrentDateFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"CURRENT_DATE\"\n *\n * @link    www.doctrine-project.org\n */\nclass CurrentDateFunction extends FunctionNode\n{\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL();\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/CurrentTimeFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"CURRENT_TIME\"\n *\n * @link    www.doctrine-project.org\n */\nclass CurrentTimeFunction extends FunctionNode\n{\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL();\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/CurrentTimestampFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"CURRENT_TIMESTAMP\"\n *\n * @link    www.doctrine-project.org\n */\nclass CurrentTimestampFunction extends FunctionNode\n{\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL();\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/DateAddFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\ASTException;\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function strtolower;\n\n/**\n * \"DATE_ADD\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \",\" StringPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass DateAddFunction extends FunctionNode\n{\n    public Node $firstDateExpression;\n    public Node $intervalExpression;\n    public Node $unit;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return match (strtolower((string) $this->unit->value)) {\n            'second' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'minute' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMinutesExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'hour' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'day' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'week' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddWeeksExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'month' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'year' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddYearsExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            default => throw QueryException::semanticalError(\n                'DATE_ADD() only supports units of type second, minute, hour, day, week, month and year.',\n            ),\n        };\n    }\n\n    /** @throws ASTException */\n    private function dispatchIntervalExpression(SqlWalker $sqlWalker): string\n    {\n        return $this->intervalExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstDateExpression = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->intervalExpression = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->unit = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/DateDiffFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"DATE_DIFF\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass DateDiffFunction extends FunctionNode\n{\n    public Node $date1;\n    public Node $date2;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression(\n            $this->date1->dispatch($sqlWalker),\n            $this->date2->dispatch($sqlWalker),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->date1 = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->date2 = $parser->ArithmeticPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/DateSubFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\ASTException;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nuse function strtolower;\n\n/**\n * \"DATE_SUB(date1, interval, unit)\"\n *\n * @link    www.doctrine-project.org\n */\nclass DateSubFunction extends DateAddFunction\n{\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return match (strtolower((string) $this->unit->value)) {\n            'second' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubSecondsExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'minute' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMinutesExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'hour' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'day' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'week' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubWeeksExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'month' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            'year' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubYearsExpression(\n                $this->firstDateExpression->dispatch($sqlWalker),\n                $this->dispatchIntervalExpression($sqlWalker),\n            ),\n            default => throw QueryException::semanticalError(\n                'DATE_SUB() only supports units of type second, minute, hour, day, week, month and year.',\n            ),\n        };\n    }\n\n    /** @throws ASTException */\n    private function dispatchIntervalExpression(SqlWalker $sqlWalker): string\n    {\n        return $this->intervalExpression->dispatch($sqlWalker);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/FunctionNode.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * Abstract Function Node.\n *\n * @link    www.doctrine-project.org\n *\n * @phpstan-consistent-constructor\n */\nabstract class FunctionNode extends Node\n{\n    public function __construct(public string $name)\n    {\n    }\n\n    abstract public function getSql(SqlWalker $sqlWalker): string;\n\n    public function dispatch(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->walkFunction($this);\n    }\n\n    abstract public function parse(Parser $parser): void;\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/IdentityFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function assert;\nuse function reset;\nuse function sprintf;\n\n/**\n * \"IDENTITY\" \"(\" SingleValuedAssociationPathExpression {\",\" string} \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass IdentityFunction extends FunctionNode\n{\n    public PathExpression $pathExpression;\n\n    public string|null $fieldMapping = null;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        assert($this->pathExpression->field !== null);\n        $entityManager = $sqlWalker->getEntityManager();\n        $platform      = $entityManager->getConnection()->getDatabasePlatform();\n        $quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();\n        $dqlAlias      = $this->pathExpression->identificationVariable;\n        $assocField    = $this->pathExpression->field;\n        $assoc         = $sqlWalker->getMetadataForDqlAlias($dqlAlias)->associationMappings[$assocField];\n        $targetEntity  = $entityManager->getClassMetadata($assoc->targetEntity);\n\n        assert($assoc->isToOneOwningSide());\n        $joinColumn = reset($assoc->joinColumns);\n\n        if ($this->fieldMapping !== null) {\n            if (! isset($targetEntity->fieldMappings[$this->fieldMapping])) {\n                throw new QueryException(sprintf('Undefined reference field mapping \"%s\"', $this->fieldMapping));\n            }\n\n            $field      = $targetEntity->fieldMappings[$this->fieldMapping];\n            $joinColumn = null;\n\n            foreach ($assoc->joinColumns as $mapping) {\n                if ($mapping->referencedColumnName === $field->columnName) {\n                    $joinColumn = $mapping;\n\n                    break;\n                }\n            }\n\n            if ($joinColumn === null) {\n                throw new QueryException(sprintf('Unable to resolve the reference field mapping \"%s\"', $this->fieldMapping));\n            }\n        }\n\n        // The table with the relation may be a subclass, so get the table name from the association definition\n        $tableName = $entityManager->getClassMetadata($assoc->sourceEntity)->getTableName();\n\n        $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias);\n        $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform);\n\n        return $tableAlias . '.' . $columnName;\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->pathExpression = $parser->SingleValuedAssociationPathExpression();\n\n        if ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {\n            $parser->match(TokenType::T_COMMA);\n            $parser->match(TokenType::T_STRING);\n\n            $token = $parser->getLexer()->token;\n            assert($token !== null);\n            $this->fieldMapping = $token->value;\n        }\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/LengthFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\AST\\TypedExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"LENGTH\" \"(\" StringPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass LengthFunction extends FunctionNode implements TypedExpression\n{\n    public Node $stringPrimary;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression(\n            $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->stringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n\n    public function getReturnType(): Type\n    {\n        return Type::getType(Types::INTEGER);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/LocateFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"LOCATE\" \"(\" StringPrimary \",\" StringPrimary [\",\" SimpleArithmeticExpression]\")\"\n *\n * @link    www.doctrine-project.org\n */\nclass LocateFunction extends FunctionNode\n{\n    public Node|string $firstStringPrimary;\n    public Node|string $secondStringPrimary;\n\n    public Node|string|bool $simpleArithmeticExpression = false;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $platform = $sqlWalker->getConnection()->getDatabasePlatform();\n\n        $firstString  = $sqlWalker->walkStringPrimary($this->firstStringPrimary);\n        $secondString = $sqlWalker->walkStringPrimary($this->secondStringPrimary);\n\n        if ($this->simpleArithmeticExpression) {\n            return $platform->getLocateExpression(\n                $secondString,\n                $firstString,\n                $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression),\n            );\n        }\n\n        return $platform->getLocateExpression($secondString, $firstString);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstStringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_COMMA);\n\n        $this->secondStringPrimary = $parser->StringPrimary();\n\n        $lexer = $parser->getLexer();\n        if ($lexer->isNextToken(TokenType::T_COMMA)) {\n            $parser->match(TokenType::T_COMMA);\n\n            $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n        }\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/LowerFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function sprintf;\n\n/**\n * \"LOWER\" \"(\" StringPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass LowerFunction extends FunctionNode\n{\n    public Node $stringPrimary;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return sprintf(\n            'LOWER(%s)',\n            $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->stringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/MaxFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * \"MAX\" \"(\" [\"DISTINCT\"] StringPrimary \")\"\n */\nfinal class MaxFunction extends FunctionNode\n{\n    private AggregateExpression $aggregateExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/MinFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * \"MIN\" \"(\" [\"DISTINCT\"] StringPrimary \")\"\n */\nfinal class MinFunction extends FunctionNode\n{\n    private AggregateExpression $aggregateExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/ModFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"MOD\" \"(\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass ModFunction extends FunctionNode\n{\n    public Node|string $firstSimpleArithmeticExpression;\n    public Node|string $secondSimpleArithmeticExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression(\n            $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),\n            $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $parser->match(TokenType::T_COMMA);\n\n        $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/SizeFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function assert;\n\n/**\n * \"SIZE\" \"(\" CollectionValuedPathExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass SizeFunction extends FunctionNode\n{\n    public PathExpression $collectionPathExpression;\n\n    /**\n     * @inheritdoc\n     * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient).\n     */\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        assert($this->collectionPathExpression->field !== null);\n        $entityManager = $sqlWalker->getEntityManager();\n        $platform      = $entityManager->getConnection()->getDatabasePlatform();\n        $quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();\n        $dqlAlias      = $this->collectionPathExpression->identificationVariable;\n        $assocField    = $this->collectionPathExpression->field;\n\n        $class = $sqlWalker->getMetadataForDqlAlias($dqlAlias);\n        $assoc = $class->associationMappings[$assocField];\n        $sql   = 'SELECT COUNT(*) FROM ';\n\n        if ($assoc->isOneToMany()) {\n            $targetClass      = $entityManager->getClassMetadata($assoc->targetEntity);\n            $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());\n            $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);\n\n            $sql .= $quoteStrategy->getTableName($targetClass, $platform) . ' ' . $targetTableAlias . ' WHERE ';\n\n            $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy];\n            assert($owningAssoc->isManyToOne());\n\n            $first = true;\n\n            foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) {\n                if ($first) {\n                    $first = false;\n                } else {\n                    $sql .= ' AND ';\n                }\n\n                $sql .= $targetTableAlias . '.' . $sourceColumn\n                      . ' = '\n                      . $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform);\n            }\n        } else { // many-to-many\n            assert($assoc->isManyToMany());\n            $owningAssoc = $entityManager->getMetadataFactory()->getOwningSide($assoc);\n            $joinTable   = $owningAssoc->joinTable;\n\n            // SQL table aliases\n            $joinTableAlias   = $sqlWalker->getSQLTableAlias($joinTable->name);\n            $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);\n\n            // join to target table\n            $targetClass = $entityManager->getClassMetadata($assoc->targetEntity);\n            $sql        .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE ';\n\n            $joinColumns = $assoc->isOwningSide()\n                ? $joinTable->joinColumns\n                : $joinTable->inverseJoinColumns;\n\n            $first = true;\n\n            foreach ($joinColumns as $joinColumn) {\n                if ($first) {\n                    $first = false;\n                } else {\n                    $sql .= ' AND ';\n                }\n\n                $sourceColumnName = $quoteStrategy->getColumnName(\n                    $class->fieldNames[$joinColumn->referencedColumnName],\n                    $class,\n                    $platform,\n                );\n\n                $sql .= $joinTableAlias . '.' . $joinColumn->name\n                      . ' = '\n                      . $sourceTableAlias . '.' . $sourceColumnName;\n            }\n        }\n\n        return '(' . $sql . ')';\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->collectionPathExpression = $parser->CollectionValuedPathExpression();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/SqrtFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function sprintf;\n\n/**\n * \"SQRT\" \"(\" SimpleArithmeticExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass SqrtFunction extends FunctionNode\n{\n    public Node|string $simpleArithmeticExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return sprintf(\n            'SQRT(%s)',\n            $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/SubstringFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\n/**\n * \"SUBSTRING\" \"(\" StringPrimary \",\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass SubstringFunction extends FunctionNode\n{\n    public Node $stringPrimary;\n\n    public Node|string $firstSimpleArithmeticExpression;\n    public Node|string|null $secondSimpleArithmeticExpression = null;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $optionalSecondSimpleArithmeticExpression = null;\n        if ($this->secondSimpleArithmeticExpression !== null) {\n            $optionalSecondSimpleArithmeticExpression = $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression);\n        }\n\n        return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression(\n            $sqlWalker->walkStringPrimary($this->stringPrimary),\n            $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),\n            $optionalSecondSimpleArithmeticExpression,\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->stringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_COMMA);\n\n        $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $lexer = $parser->getLexer();\n        if ($lexer->isNextToken(TokenType::T_COMMA)) {\n            $parser->match(TokenType::T_COMMA);\n\n            $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n        }\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/SumFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * \"SUM\" \"(\" [\"DISTINCT\"] StringPrimary \")\"\n */\nfinal class SumFunction extends FunctionNode\n{\n    private AggregateExpression $aggregateExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/TrimFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\DBAL\\Platforms\\TrimMode;\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function assert;\nuse function strcasecmp;\n\n/**\n * \"TRIM\" \"(\" [[\"LEADING\" | \"TRAILING\" | \"BOTH\"] [char] \"FROM\"] StringPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass TrimFunction extends FunctionNode\n{\n    public bool $leading          = false;\n    public bool $trailing         = false;\n    public bool $both             = false;\n    public string|false $trimChar = false;\n    public Node $stringPrimary;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        $stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary);\n        $platform      = $sqlWalker->getConnection()->getDatabasePlatform();\n        $trimMode      = $this->getTrimMode();\n\n        if ($this->trimChar !== false) {\n            return $platform->getTrimExpression(\n                $stringPrimary,\n                $trimMode,\n                $platform->quoteStringLiteral($this->trimChar),\n            );\n        }\n\n        return $platform->getTrimExpression($stringPrimary, $trimMode);\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $lexer = $parser->getLexer();\n\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->parseTrimMode($parser);\n\n        if ($lexer->isNextToken(TokenType::T_STRING)) {\n            $parser->match(TokenType::T_STRING);\n\n            assert($lexer->token !== null);\n            $this->trimChar = $lexer->token->value;\n        }\n\n        if ($this->leading || $this->trailing || $this->both || ($this->trimChar !== false)) {\n            $parser->match(TokenType::T_FROM);\n        }\n\n        $this->stringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n\n    /** @phpstan-return TrimMode::* */\n    private function getTrimMode(): TrimMode|int\n    {\n        if ($this->leading) {\n            return TrimMode::LEADING;\n        }\n\n        if ($this->trailing) {\n            return TrimMode::TRAILING;\n        }\n\n        if ($this->both) {\n            return TrimMode::BOTH;\n        }\n\n        return TrimMode::UNSPECIFIED;\n    }\n\n    private function parseTrimMode(Parser $parser): void\n    {\n        $lexer = $parser->getLexer();\n        assert($lexer->lookahead !== null);\n        $value = $lexer->lookahead->value;\n\n        if (strcasecmp('leading', $value) === 0) {\n            $parser->match(TokenType::T_LEADING);\n\n            $this->leading = true;\n\n            return;\n        }\n\n        if (strcasecmp('trailing', $value) === 0) {\n            $parser->match(TokenType::T_TRAILING);\n\n            $this->trailing = true;\n\n            return;\n        }\n\n        if (strcasecmp('both', $value) === 0) {\n            $parser->match(TokenType::T_BOTH);\n\n            $this->both = true;\n\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Functions/UpperFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST\\Functions;\n\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\n\nuse function sprintf;\n\n/**\n * \"UPPER\" \"(\" StringPrimary \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass UpperFunction extends FunctionNode\n{\n    public Node $stringPrimary;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return sprintf(\n            'UPPER(%s)',\n            $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary),\n        );\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->stringPrimary = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/GeneralCaseExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * GeneralCaseExpression ::= \"CASE\" WhenClause {WhenClause}* \"ELSE\" ScalarExpression \"END\"\n *\n * @link    www.doctrine-project.org\n */\nclass GeneralCaseExpression extends Node\n{\n    /** @param mixed[] $whenClauses */\n    public function __construct(\n        public array $whenClauses,\n        public mixed $elseScalarExpression = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkGeneralCaseExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/GroupByClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass GroupByClause extends Node\n{\n    /** @param mixed[] $groupByItems */\n    public function __construct(public array $groupByItems)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkGroupByClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/HavingClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass HavingClause extends Node\n{\n    public function __construct(public ConditionalExpression|Phase2OptimizableConditional $conditionalExpression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkHavingClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/IdentificationVariableDeclaration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*\n *\n * @link    www.doctrine-project.org\n */\nclass IdentificationVariableDeclaration extends Node\n{\n    /** @param mixed[] $joins */\n    public function __construct(\n        public RangeVariableDeclaration|null $rangeVariableDeclaration = null,\n        public IndexBy|null $indexBy = null,\n        public array $joins = [],\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkIdentificationVariableDeclaration($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/InListExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass InListExpression extends Node\n{\n    /** @param non-empty-list<mixed> $literals */\n    public function __construct(\n        public ArithmeticExpression $expression,\n        public array $literals,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkInListExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/InSubselectExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass InSubselectExpression extends Node\n{\n    public function __construct(\n        public ArithmeticExpression $expression,\n        public Subselect $subselect,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkInSubselectExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/IndexBy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * IndexBy ::= \"INDEX\" \"BY\" SingleValuedPathExpression\n *\n * @link    www.doctrine-project.org\n */\nclass IndexBy extends Node\n{\n    public function __construct(public PathExpression $singleValuedPathExpression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        $walker->walkIndexBy($this);\n\n        return '';\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/InputParameter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nuse function is_numeric;\nuse function strlen;\nuse function substr;\n\nclass InputParameter extends Node\n{\n    public bool $isNamed;\n    public string $name;\n\n    /** @throws QueryException */\n    public function __construct(string $value)\n    {\n        if (strlen($value) === 1) {\n            throw QueryException::invalidParameterFormat($value);\n        }\n\n        $param         = substr($value, 1);\n        $this->isNamed = ! is_numeric($param);\n        $this->name    = $param;\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkInputParameter($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/InstanceOfExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * InstanceOfExpression ::= IdentificationVariable [\"NOT\"] \"INSTANCE\" [\"OF\"] (InstanceOfParameter | \"(\" InstanceOfParameter {\",\" InstanceOfParameter}* \")\")\n * InstanceOfParameter  ::= AbstractSchemaName | InputParameter\n *\n * @link    www.doctrine-project.org\n */\nclass InstanceOfExpression extends Node\n{\n    /** @param non-empty-list<InputParameter|string> $value */\n    public function __construct(\n        public string $identificationVariable,\n        public array $value,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkInstanceOfExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Join.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * Join ::= [\"LEFT\" [\"OUTER\"] | \"INNER\"] \"JOIN\" JoinAssociationPathExpression\n *          [\"AS\"] AliasIdentificationVariable [(\"ON\" | \"WITH\") ConditionalExpression]\n *\n * @link    www.doctrine-project.org\n */\nclass Join extends Node\n{\n    final public const JOIN_TYPE_LEFT      = 1;\n    final public const JOIN_TYPE_LEFTOUTER = 2;\n    final public const JOIN_TYPE_INNER     = 3;\n\n    public ConditionalExpression|Phase2OptimizableConditional|null $conditionalExpression = null;\n\n    /** @phpstan-param self::JOIN_TYPE_* $joinType */\n    public function __construct(\n        public int $joinType,\n        public Node|null $joinAssociationDeclaration = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkJoin($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/JoinAssociationDeclaration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * JoinAssociationDeclaration ::= JoinAssociationPathExpression [\"AS\"] AliasIdentificationVariable\n *\n * @link    www.doctrine-project.org\n */\nclass JoinAssociationDeclaration extends Node\n{\n    public function __construct(\n        public JoinAssociationPathExpression $joinAssociationPathExpression,\n        public string $aliasIdentificationVariable,\n        public IndexBy|null $indexBy,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkJoinAssociationDeclaration($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/JoinAssociationPathExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\n/**\n * JoinAssociationPathExpression ::= IdentificationVariable \".\" (SingleValuedAssociationField | CollectionValuedAssociationField)\n *\n * @link    www.doctrine-project.org\n */\nclass JoinAssociationPathExpression extends Node\n{\n    public function __construct(\n        public string $identificationVariable,\n        public string $associationField,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/JoinClassPathExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * JoinClassPathExpression ::= AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n *\n * @link    www.doctrine-project.org\n */\nclass JoinClassPathExpression extends Node\n{\n    public function __construct(\n        public mixed $abstractSchemaName,\n        public mixed $aliasIdentificationVariable,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkJoinPathExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/JoinVariableDeclaration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * JoinVariableDeclaration ::= Join [IndexBy]\n *\n * @link    www.doctrine-project.org\n */\nclass JoinVariableDeclaration extends Node\n{\n    public function __construct(public Join $join, public IndexBy|null $indexBy)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkJoinVariableDeclaration($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/LikeExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * LikeExpression ::= StringExpression [\"NOT\"] \"LIKE\" string [\"ESCAPE\" char]\n *\n * @link    www.doctrine-project.org\n */\nclass LikeExpression extends Node\n{\n    public function __construct(\n        public Node|string $stringExpression,\n        public InputParameter|FunctionNode|PathExpression|Literal $stringPattern,\n        public Literal|null $escapeChar = null,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkLikeExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Literal.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nclass Literal extends Node\n{\n    final public const STRING  = 1;\n    final public const BOOLEAN = 2;\n    final public const NUMERIC = 3;\n\n    /** @phpstan-param self::* $type */\n    public function __construct(\n        public int $type,\n        public mixed $value,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkLiteral($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/NewObjectExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nuse function func_get_arg;\nuse function func_num_args;\n\n/**\n * NewObjectExpression ::= \"NEW\" IdentificationVariable \"(\" NewObjectArg {\",\" NewObjectArg}* \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass NewObjectExpression extends Node\n{\n    /**\n     * @param class-string $className\n     * @param mixed[]      $args\n     */\n    public function __construct(public string $className, public array $args)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker /*, string|null $parentAlias = null */): string\n    {\n        $parentAlias = func_num_args() > 1 ? func_get_arg(1) : null;\n\n        return $walker->walkNewObject($this, $parentAlias);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Node.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Stringable;\n\nuse function get_debug_type;\nuse function get_object_vars;\nuse function is_array;\nuse function is_object;\nuse function str_repeat;\nuse function var_export;\n\nuse const PHP_EOL;\n\n/**\n * Abstract class of an AST node.\n *\n * @link    www.doctrine-project.org\n */\nabstract class Node implements Stringable\n{\n    /**\n     * Double-dispatch method, supposed to dispatch back to the walker.\n     *\n     * Implementation is not mandatory for all nodes.\n     *\n     * @throws ASTException\n     */\n    public function dispatch(SqlWalker $walker): string\n    {\n        throw ASTException::noDispatchForNode($this);\n    }\n\n    /**\n     * Dumps the AST Node into a string representation for information purpose only.\n     */\n    public function __toString(): string\n    {\n        return $this->dump($this);\n    }\n\n    public function dump(mixed $value): string\n    {\n        static $ident = 0;\n\n        $str = '';\n\n        if ($value instanceof Node) {\n            $str  .= get_debug_type($value) . '(' . PHP_EOL;\n            $props = get_object_vars($value);\n\n            foreach ($props as $name => $prop) {\n                $ident += 4;\n                $str   .= str_repeat(' ', $ident) . '\"' . $name . '\": '\n                      . $this->dump($prop) . ',' . PHP_EOL;\n                $ident -= 4;\n            }\n\n            $str .= str_repeat(' ', $ident) . ')';\n        } elseif (is_array($value)) {\n            $ident += 4;\n            $str   .= 'array(';\n            $some   = false;\n\n            foreach ($value as $k => $v) {\n                $str .= PHP_EOL . str_repeat(' ', $ident) . '\"'\n                      . $k . '\" => ' . $this->dump($v) . ',';\n                $some = true;\n            }\n\n            $ident -= 4;\n            $str   .= ($some ? PHP_EOL . str_repeat(' ', $ident) : '') . ')';\n        } elseif (is_object($value)) {\n            $str .= 'instanceof(' . get_debug_type($value) . ')';\n        } else {\n            $str .= var_export($value, true);\n        }\n\n        return $str;\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/NullComparisonExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) \"IS\" [\"NOT\"] \"NULL\"\n *\n * @link    www.doctrine-project.org\n */\nclass NullComparisonExpression extends Node\n{\n    public function __construct(\n        public Node|string $expression,\n        public bool $not = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkNullComparisonExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/NullIfExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * NullIfExpression ::= \"NULLIF\" \"(\" ScalarExpression \",\" ScalarExpression \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass NullIfExpression extends Node\n{\n    public function __construct(public mixed $firstExpression, public mixed $secondExpression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkNullIfExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/OrderByClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * OrderByClause ::= \"ORDER\" \"BY\" OrderByItem {\",\" OrderByItem}*\n *\n * @link    www.doctrine-project.org\n */\nclass OrderByClause extends Node\n{\n    /** @param OrderByItem[] $orderByItems */\n    public function __construct(public array $orderByItems)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkOrderByClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/OrderByItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nuse function strtoupper;\n\n/**\n * OrderByItem ::= (ResultVariable | StateFieldPathExpression) [\"ASC\" | \"DESC\"]\n *\n * @link    www.doctrine-project.org\n */\nclass OrderByItem extends Node\n{\n    public string $type;\n\n    public function __construct(public mixed $expression)\n    {\n    }\n\n    public function isAsc(): bool\n    {\n        return strtoupper($this->type) === 'ASC';\n    }\n\n    public function isDesc(): bool\n    {\n        return strtoupper($this->type) === 'DESC';\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkOrderByItem($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/ParenthesisExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * ParenthesisExpression ::= \"(\" ArithmeticPrimary \")\"\n */\nclass ParenthesisExpression extends Node\n{\n    public function __construct(public Node $expression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkParenthesisExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/PartialObjectExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nclass PartialObjectExpression extends Node\n{\n    /** @param mixed[] $partialFieldSet */\n    public function __construct(\n        public string $identificationVariable,\n        public array $partialFieldSet,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/PathExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression\n * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression\n * StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression\n * SingleValuedAssociationPathExpression ::= IdentificationVariable \".\" SingleValuedAssociationField\n * CollectionValuedPathExpression ::= IdentificationVariable \".\" CollectionValuedAssociationField\n * StateField ::= {EmbeddedClassStateField \".\"}* SimpleStateField\n * SimpleStateFieldPathExpression ::= IdentificationVariable \".\" StateField\n */\nclass PathExpression extends Node\n{\n    final public const TYPE_COLLECTION_VALUED_ASSOCIATION = 2;\n    final public const TYPE_SINGLE_VALUED_ASSOCIATION     = 4;\n    final public const TYPE_STATE_FIELD                   = 8;\n\n    /** @phpstan-var self::TYPE_*|null */\n    public int|null $type = null;\n\n    /** @phpstan-param int-mask-of<self::TYPE_*> $expectedType */\n    public function __construct(\n        public int $expectedType,\n        public string $identificationVariable,\n        public string|null $field = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkPathExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Phase2OptimizableConditional.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\n/**\n * Marks types that can be used in place of a ConditionalExpression as a phase\n * 2 optimization.\n *\n * @internal\n */\ninterface Phase2OptimizableConditional\n{\n}\n"
  },
  {
    "path": "src/Query/AST/QuantifiedExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\nuse function strtoupper;\n\n/**\n * QuantifiedExpression ::= (\"ALL\" | \"ANY\" | \"SOME\") \"(\" Subselect \")\"\n *\n * @link    www.doctrine-project.org\n */\nclass QuantifiedExpression extends Node\n{\n    public string $type;\n\n    public function __construct(public Subselect $subselect)\n    {\n    }\n\n    public function isAll(): bool\n    {\n        return strtoupper($this->type) === 'ALL';\n    }\n\n    public function isAny(): bool\n    {\n        return strtoupper($this->type) === 'ANY';\n    }\n\n    public function isSome(): bool\n    {\n        return strtoupper($this->type) === 'SOME';\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkQuantifiedExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/RangeVariableDeclaration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * RangeVariableDeclaration ::= AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n *\n * @link    www.doctrine-project.org\n */\nclass RangeVariableDeclaration extends Node\n{\n    public function __construct(\n        public string $abstractSchemaName,\n        public string $aliasIdentificationVariable,\n        public bool $isRoot = true,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkRangeVariableDeclaration($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SelectClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SelectClause = \"SELECT\" [\"DISTINCT\"] SelectExpression {\",\" SelectExpression}\n *\n * @link    www.doctrine-project.org\n */\nclass SelectClause extends Node\n{\n    /** @param mixed[] $selectExpressions */\n    public function __construct(\n        public array $selectExpressions,\n        public bool $isDistinct,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSelectClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SelectExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SelectExpression ::= IdentificationVariable [\".\" \"*\"] | StateFieldPathExpression |\n *                      (AggregateExpression | \"(\" Subselect \")\") [[\"AS\"] [\"HIDDEN\"] FieldAliasIdentificationVariable]\n *\n * @link    www.doctrine-project.org\n */\nclass SelectExpression extends Node\n{\n    public function __construct(\n        public mixed $expression,\n        public string|null $fieldIdentificationVariable,\n        public bool $hiddenAliasResultVariable = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSelectExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SelectStatement.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n *\n * @link    www.doctrine-project.org\n */\nclass SelectStatement extends Node\n{\n    public WhereClause|null $whereClause = null;\n\n    public GroupByClause|null $groupByClause = null;\n\n    public HavingClause|null $havingClause = null;\n\n    public OrderByClause|null $orderByClause = null;\n\n    public function __construct(public SelectClause $selectClause, public FromClause $fromClause)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSelectStatement($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SimpleArithmeticExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SimpleArithmeticExpression ::= ArithmeticTerm {(\"+\" | \"-\") ArithmeticTerm}*\n *\n * @link    www.doctrine-project.org\n */\nclass SimpleArithmeticExpression extends Node\n{\n    /** @param mixed[] $arithmeticTerms */\n    public function __construct(public array $arithmeticTerms)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSimpleArithmeticExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SimpleCaseExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SimpleCaseExpression ::= \"CASE\" CaseOperand SimpleWhenClause {SimpleWhenClause}* \"ELSE\" ScalarExpression \"END\"\n *\n * @link    www.doctrine-project.org\n */\nclass SimpleCaseExpression extends Node\n{\n    /** @param mixed[] $simpleWhenClauses */\n    public function __construct(\n        public PathExpression|null $caseOperand = null,\n        public array $simpleWhenClauses = [],\n        public mixed $elseScalarExpression = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSimpleCaseExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SimpleSelectClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SimpleSelectClause  ::= \"SELECT\" [\"DISTINCT\"] SimpleSelectExpression\n *\n * @link    www.doctrine-project.org\n */\nclass SimpleSelectClause extends Node\n{\n    public function __construct(\n        public SimpleSelectExpression $simpleSelectExpression,\n        public bool $isDistinct = false,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSimpleSelectClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SimpleSelectExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable\n *                          | (AggregateExpression [[\"AS\"] FieldAliasIdentificationVariable])\n *\n * @link    www.doctrine-project.org\n */\nclass SimpleSelectExpression extends Node\n{\n    public string|null $fieldIdentificationVariable = null;\n\n    public function __construct(public Node|string $expression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSimpleSelectExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SimpleWhenClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SimpleWhenClause ::= \"WHEN\" ScalarExpression \"THEN\" ScalarExpression\n *\n * @link    www.doctrine-project.org\n */\nclass SimpleWhenClause extends Node\n{\n    public function __construct(\n        public mixed $caseScalarExpression = null,\n        public mixed $thenScalarExpression = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkWhenClauseExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/Subselect.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n *\n * @link    www.doctrine-project.org\n */\nclass Subselect extends Node\n{\n    public WhereClause|null $whereClause = null;\n\n    public GroupByClause|null $groupByClause = null;\n\n    public HavingClause|null $havingClause = null;\n\n    public OrderByClause|null $orderByClause = null;\n\n    public function __construct(public SimpleSelectClause $simpleSelectClause, public SubselectFromClause $subselectFromClause)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSubselect($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SubselectFromClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * SubselectFromClause ::= \"FROM\" SubselectIdentificationVariableDeclaration {\",\" SubselectIdentificationVariableDeclaration}*\n *\n * @link    www.doctrine-project.org\n */\nclass SubselectFromClause extends Node\n{\n    /** @param mixed[] $identificationVariableDeclarations */\n    public function __construct(public array $identificationVariableDeclarations)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkSubselectFromClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/SubselectIdentificationVariableDeclaration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\n/**\n * SubselectIdentificationVariableDeclaration ::= AssociationPathExpression [\"AS\"] AliasIdentificationVariable\n *\n * @link    www.doctrine-project.org\n */\nclass SubselectIdentificationVariableDeclaration\n{\n    public function __construct(\n        public PathExpression $associationPathExpression,\n        public string $aliasIdentificationVariable,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/TypedExpression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\DBAL\\Types\\Type;\n\n/**\n * Provides an API for resolving the type of a Node\n */\ninterface TypedExpression\n{\n    public function getReturnType(): Type;\n}\n"
  },
  {
    "path": "src/Query/AST/UpdateClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * UpdateClause ::= \"UPDATE\" AbstractSchemaName [[\"AS\"] AliasIdentificationVariable] \"SET\" UpdateItem {\",\" UpdateItem}*\n *\n * @link    www.doctrine-project.org\n */\nclass UpdateClause extends Node\n{\n    public string $aliasIdentificationVariable;\n\n    /** @param mixed[] $updateItems */\n    public function __construct(\n        public string $abstractSchemaName,\n        public array $updateItems,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkUpdateClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/UpdateItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * UpdateItem ::= [IdentificationVariable \".\"] {StateField | SingleValuedAssociationField} \"=\" NewValue\n * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |\n *              EnumPrimary | SimpleEntityExpression | \"NULL\"\n *\n * @link    www.doctrine-project.org\n */\nclass UpdateItem extends Node\n{\n    public function __construct(public PathExpression $pathExpression, public InputParameter|ArithmeticExpression|null $newValue)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkUpdateItem($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/UpdateStatement.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * UpdateStatement = UpdateClause [WhereClause]\n *\n * @link    www.doctrine-project.org\n */\nclass UpdateStatement extends Node\n{\n    public WhereClause|null $whereClause = null;\n\n    public function __construct(public UpdateClause $updateClause)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkUpdateStatement($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/WhenClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * WhenClause ::= \"WHEN\" ConditionalExpression \"THEN\" ScalarExpression\n *\n * @link    www.doctrine-project.org\n */\nclass WhenClause extends Node\n{\n    public function __construct(\n        public ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression,\n        public mixed $thenScalarExpression = null,\n    ) {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkWhenClauseExpression($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/AST/WhereClause.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\AST;\n\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * WhereClause ::= \"WHERE\" ConditionalExpression\n *\n * @link    www.doctrine-project.org\n */\nclass WhereClause extends Node\n{\n    public function __construct(public ConditionalExpression|Phase2OptimizableConditional $conditionalExpression)\n    {\n    }\n\n    public function dispatch(SqlWalker $walker): string\n    {\n        return $walker->walkWhereClause($this);\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/AbstractSqlExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\DBAL\\Types\\Type;\n\n/**\n * Base class for SQL statement executors.\n *\n * @link        http://www.doctrine-project.org\n *\n * @todo Rename: AbstractSQLExecutor\n * @phpstan-type WrapperParameterType = string|Type|ParameterType::*|ArrayParameterType::*\n * @phpstan-type WrapperParameterTypeArray = array<int<0, max>, WrapperParameterType>|array<string, WrapperParameterType>\n */\nabstract class AbstractSqlExecutor\n{\n    /** @var list<string>|string */\n    protected array|string $sqlStatements;\n\n    protected QueryCacheProfile|null $queryCacheProfile = null;\n\n    /**\n     * Gets the SQL statements that are executed by the executor.\n     *\n     * @return list<string>|string  All the SQL update statements.\n     */\n    public function getSqlStatements(): array|string\n    {\n        return $this->sqlStatements;\n    }\n\n    public function setQueryCacheProfile(QueryCacheProfile $qcp): void\n    {\n        $this->queryCacheProfile = $qcp;\n    }\n\n    /**\n     * Do not use query cache\n     */\n    public function removeQueryCacheProfile(): void\n    {\n        $this->queryCacheProfile = null;\n    }\n\n    /**\n     * Executes all sql statements.\n     *\n     * @param Connection                       $conn   The database connection that is used to execute the queries.\n     * @param list<mixed>|array<string, mixed> $params The parameters.\n     * @phpstan-param WrapperParameterTypeArray  $types  The parameter types.\n     */\n    abstract public function execute(Connection $conn, array $params, array $types): Result|int;\n}\n"
  },
  {
    "path": "src/Query/Exec/FinalizedSelectExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Result;\n\n/**\n * SQL executor for a given, final, single SELECT SQL query\n *\n * @method string getSqlStatements()\n */\nclass FinalizedSelectExecutor extends AbstractSqlExecutor\n{\n    public function __construct(string $sql)\n    {\n        $this->sqlStatements = $sql;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function execute(Connection $conn, array $params, array $types): Result\n    {\n        return $conn->executeQuery($this->getSqlStatements(), $params, $types, $this->queryCacheProfile);\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/MultiTableDeleteExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\AST\\DeleteStatement;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse Throwable;\n\nuse function array_reverse;\nuse function implode;\n\n/**\n * Executes the SQL statements for bulk DQL DELETE statements on classes in\n * Class Table Inheritance (JOINED).\n *\n * @link        http://www.doctrine-project.org\n */\nclass MultiTableDeleteExecutor extends AbstractSqlExecutor\n{\n    private readonly string $createTempTableSql;\n    private readonly string $dropTempTableSql;\n    private readonly string $insertSql;\n\n    /**\n     * Initializes a new <tt>MultiTableDeleteExecutor</tt>.\n     *\n     * Internal note: Any SQL construction and preparation takes place in the constructor for\n     *                best performance. With a query cache the executor will be cached.\n     *\n     * @param DeleteStatement $AST       The root AST node of the DQL query.\n     * @param SqlWalker       $sqlWalker The walker used for SQL generation from the AST.\n     */\n    public function __construct(AST\\Node $AST, SqlWalker $sqlWalker)\n    {\n        $em            = $sqlWalker->getEntityManager();\n        $conn          = $em->getConnection();\n        $platform      = $conn->getDatabasePlatform();\n        $quoteStrategy = $em->getConfiguration()->getQuoteStrategy();\n\n        if ($conn instanceof PrimaryReadReplicaConnection) {\n            $conn->ensureConnectedToPrimary();\n        }\n\n        $primaryClass    = $em->getClassMetadata($AST->deleteClause->abstractSchemaName);\n        $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable;\n        $rootClass       = $em->getClassMetadata($primaryClass->rootEntityName);\n\n        $tempTable     = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());\n        $idColumnNames = $rootClass->getIdentifierColumnNames();\n        $idColumnList  = implode(', ', $idColumnNames);\n\n        // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()\n        $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias);\n\n        $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'\n                . ' SELECT t0.' . implode(', t0.', $idColumnNames);\n\n        $rangeDecl  = new AST\\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias);\n        $fromClause = new AST\\FromClause([new AST\\IdentificationVariableDeclaration($rangeDecl, null, [])]);\n        $insertSql .= $sqlWalker->walkFromClause($fromClause);\n\n        // Append WHERE clause, if there is one.\n        if ($AST->whereClause) {\n            $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);\n        }\n\n        $this->insertSql = $insertSql;\n\n        // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect)\n        $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;\n\n        // 3. Create and store DELETE statements\n        $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses];\n        foreach (array_reverse($classNames) as $className) {\n            $tableName             = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform);\n            $this->sqlStatements[] = 'DELETE FROM ' . $tableName\n                    . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';\n        }\n\n        // 4. Store DDL for temporary identifier table.\n        $columnDefinitions = [];\n        foreach ($idColumnNames as $idColumnName) {\n            $columnDefinitions[$idColumnName] = [\n                'name'    => $idColumnName,\n                'notnull' => true,\n                'type'    => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),\n            ];\n        }\n\n        $this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('\n                . $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))';\n        $this->dropTempTableSql   = $platform->getDropTemporaryTableSQL($tempTable);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function execute(Connection $conn, array $params, array $types): int\n    {\n        // Create temporary id table\n        $conn->executeStatement($this->createTempTableSql);\n\n        try {\n            // Insert identifiers\n            $numDeleted = $conn->executeStatement($this->insertSql, $params, $types);\n\n            // Execute DELETE statements\n            foreach ($this->sqlStatements as $sql) {\n                $conn->executeStatement($sql);\n            }\n        } catch (Throwable $exception) {\n            // FAILURE! Drop temporary table to avoid possible collisions\n            $conn->executeStatement($this->dropTempTableSql);\n\n            // Re-throw exception\n            throw $exception;\n        }\n\n        // Drop temporary table\n        $conn->executeStatement($this->dropTempTableSql);\n\n        return $numDeleted;\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/MultiTableUpdateExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\AST\\UpdateStatement;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\n\nuse function array_reverse;\nuse function array_slice;\nuse function implode;\n\n/**\n * Executes the SQL statements for bulk DQL UPDATE statements on classes in\n * Class Table Inheritance (JOINED).\n */\nclass MultiTableUpdateExecutor extends AbstractSqlExecutor\n{\n    private readonly string $createTempTableSql;\n    private readonly string $dropTempTableSql;\n    private readonly string $insertSql;\n\n    /** @var mixed[] */\n    private array $sqlParameters             = [];\n    private int $numParametersInUpdateClause = 0;\n\n    /**\n     * Initializes a new <tt>MultiTableUpdateExecutor</tt>.\n     *\n     * Internal note: Any SQL construction and preparation takes place in the constructor for\n     *                best performance. With a query cache the executor will be cached.\n     *\n     * @param UpdateStatement $AST       The root AST node of the DQL query.\n     * @param SqlWalker       $sqlWalker The walker used for SQL generation from the AST.\n     */\n    public function __construct(AST\\Node $AST, SqlWalker $sqlWalker)\n    {\n        $em                  = $sqlWalker->getEntityManager();\n        $conn                = $em->getConnection();\n        $platform            = $conn->getDatabasePlatform();\n        $quoteStrategy       = $em->getConfiguration()->getQuoteStrategy();\n        $this->sqlStatements = [];\n\n        if ($conn instanceof PrimaryReadReplicaConnection) {\n            $conn->ensureConnectedToPrimary();\n        }\n\n        $updateClause = $AST->updateClause;\n        $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);\n        $rootClass    = $em->getClassMetadata($primaryClass->rootEntityName);\n\n        $updateItems = $updateClause->updateItems;\n\n        $tempTable     = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());\n        $idColumnNames = $rootClass->getIdentifierColumnNames();\n        $idColumnList  = implode(', ', $idColumnNames);\n\n        // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()\n        $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable);\n\n        $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'\n                . ' SELECT t0.' . implode(', t0.', $idColumnNames);\n\n        $rangeDecl  = new AST\\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable);\n        $fromClause = new AST\\FromClause([new AST\\IdentificationVariableDeclaration($rangeDecl, null, [])]);\n\n        $insertSql .= $sqlWalker->walkFromClause($fromClause);\n\n        // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect)\n        $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;\n\n        // 3. Create and store UPDATE statements\n        $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses];\n\n        foreach (array_reverse($classNames) as $className) {\n            $affected  = false;\n            $class     = $em->getClassMetadata($className);\n            $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET ';\n\n            $sqlParameters = [];\n            foreach ($updateItems as $updateItem) {\n                $field = $updateItem->pathExpression->field;\n\n                if (\n                    (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]->inherited)) ||\n                    (isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]->inherited))\n                ) {\n                    $newValue = $updateItem->newValue;\n\n                    if (! $affected) {\n                        $affected = true;\n                    } else {\n                        $updateSql .= ', ';\n                    }\n\n                    $updateSql .= $sqlWalker->walkUpdateItem($updateItem);\n\n                    if ($newValue instanceof AST\\InputParameter) {\n                        $sqlParameters[] = $newValue->name;\n\n                        ++$this->numParametersInUpdateClause;\n                    }\n                }\n            }\n\n            if ($affected) {\n                $this->sqlParameters[] = $sqlParameters;\n                $this->sqlStatements[] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';\n            }\n        }\n\n        // Append WHERE clause to insertSql, if there is one.\n        if ($AST->whereClause) {\n            $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);\n        }\n\n        $this->insertSql = $insertSql;\n\n        // 4. Store DDL for temporary identifier table.\n        $columnDefinitions = [];\n\n        foreach ($idColumnNames as $idColumnName) {\n            $columnDefinitions[$idColumnName] = [\n                'name'    => $idColumnName,\n                'notnull' => true,\n                'type'    => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),\n            ];\n        }\n\n        $this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('\n                . $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))';\n\n        $this->dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function execute(Connection $conn, array $params, array $types): int\n    {\n        // Create temporary id table\n        $conn->executeStatement($this->createTempTableSql);\n\n        try {\n            // Insert identifiers. Parameters from the update clause are cut off.\n            $numUpdated = $conn->executeStatement(\n                $this->insertSql,\n                array_slice($params, $this->numParametersInUpdateClause),\n                array_slice($types, $this->numParametersInUpdateClause),\n            );\n\n            // Execute UPDATE statements\n            foreach ($this->sqlStatements as $key => $statement) {\n                $paramValues = [];\n                $paramTypes  = [];\n\n                if (isset($this->sqlParameters[$key])) {\n                    foreach ($this->sqlParameters[$key] as $parameterKey => $parameterName) {\n                        $paramValues[] = $params[$parameterKey];\n                        $paramTypes[]  = $types[$parameterKey] ?? ParameterTypeInferer::inferType($params[$parameterKey]);\n                    }\n                }\n\n                $conn->executeStatement($statement, $paramValues, $paramTypes);\n            }\n        } finally {\n            // Drop temporary table\n            $conn->executeStatement($this->dropTempTableSql);\n        }\n\n        return $numUpdated;\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/PreparedExecutorFinalizer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\ORM\\Query;\n\n/**\n * PreparedExecutorFinalizer is a wrapper for the SQL finalization\n * phase that does nothing - it is constructed with the sql executor\n * already.\n */\nfinal class PreparedExecutorFinalizer implements SqlFinalizer\n{\n    private AbstractSqlExecutor $executor;\n\n    public function __construct(AbstractSqlExecutor $exeutor)\n    {\n        $this->executor = $exeutor;\n    }\n\n    public function createExecutor(Query $query): AbstractSqlExecutor\n    {\n        return $this->executor;\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/SingleSelectExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * Executor that executes the SQL statement for simple DQL SELECT statements.\n *\n * @deprecated This class is no longer needed by the ORM and will be removed in 4.0.\n *\n * @link        www.doctrine-project.org\n */\nclass SingleSelectExecutor extends AbstractSqlExecutor\n{\n    public function __construct(SelectStatement $AST, SqlWalker $sqlWalker)\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11188/',\n            'The %s is no longer needed by the ORM and will be removed in 4.0',\n            self::class,\n        );\n\n        $this->sqlStatements = $sqlWalker->walkSelectStatement($AST);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function execute(Connection $conn, array $params, array $types): Result\n    {\n        return $conn->executeQuery($this->sqlStatements, $params, $types, $this->queryCacheProfile);\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/SingleSelectSqlFinalizer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Utility\\LockSqlHelper;\n\n/**\n * SingleSelectSqlFinalizer finalizes a given SQL query by applying\n * the query's firstResult/maxResult values as well as extra read lock/write lock\n * statements, both through the platform-specific methods.\n *\n * The resulting, \"finalized\" SQL is passed to a FinalizedSelectExecutor.\n */\nclass SingleSelectSqlFinalizer implements SqlFinalizer\n{\n    use LockSqlHelper;\n\n    public function __construct(private string $sql)\n    {\n    }\n\n    /**\n     * This method exists temporarily to support old SqlWalker interfaces.\n     *\n     * @internal\n     */\n    public function finalizeSql(Query $query): string\n    {\n        $platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();\n\n        $sql = $platform->modifyLimitQuery($this->sql, $query->getMaxResults(), $query->getFirstResult());\n\n        $lockMode = $query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE;\n\n        if ($lockMode !== LockMode::NONE && $lockMode !== LockMode::OPTIMISTIC && $lockMode !== LockMode::PESSIMISTIC_READ && $lockMode !== LockMode::PESSIMISTIC_WRITE) {\n            throw QueryException::invalidLockMode();\n        }\n\n        if ($lockMode === LockMode::PESSIMISTIC_READ) {\n            $sql .= ' ' . $this->getReadLockSQL($platform);\n        } elseif ($lockMode === LockMode::PESSIMISTIC_WRITE) {\n            $sql .= ' ' . $this->getWriteLockSQL($platform);\n        }\n\n        return $sql;\n    }\n\n    /** @return FinalizedSelectExecutor */\n    public function createExecutor(Query $query): AbstractSqlExecutor\n    {\n        return new FinalizedSelectExecutor($this->finalizeSql($query));\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/SingleTableDeleteUpdateExecutor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\SqlWalker;\n\n/**\n * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes\n * that are mapped to a single table.\n *\n * @link        www.doctrine-project.org\n */\nclass SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor\n{\n    public function __construct(AST\\Node $AST, SqlWalker $sqlWalker)\n    {\n        if ($AST instanceof AST\\UpdateStatement) {\n            $this->sqlStatements = $sqlWalker->walkUpdateStatement($AST);\n        } elseif ($AST instanceof AST\\DeleteStatement) {\n            $this->sqlStatements = $sqlWalker->walkDeleteStatement($AST);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function execute(Connection $conn, array $params, array $types): int\n    {\n        if ($conn instanceof PrimaryReadReplicaConnection) {\n            $conn->ensureConnectedToPrimary();\n        }\n\n        return $conn->executeStatement($this->sqlStatements, $params, $types);\n    }\n}\n"
  },
  {
    "path": "src/Query/Exec/SqlFinalizer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Exec;\n\nuse Doctrine\\ORM\\Query;\n\n/**\n * SqlFinalizers are created by OutputWalkers that traversed the DQL AST.\n * The SqlFinalizer instance can be kept in the query cache and re-used\n * at a later time.\n *\n * Once the SqlFinalizer has been created or retrieved from the query cache,\n * it receives the Query object again in order to yield the AbstractSqlExecutor\n * that will then be used to execute the query.\n *\n * The SqlFinalizer may assume that the DQL that was used to build the AST\n * and run the OutputWalker (which created the SqlFinalizer) is equivalent to\n * the query that will be passed to the createExecutor() method. Potential differences\n * are the parameter values or firstResult/maxResult settings.\n */\ninterface SqlFinalizer\n{\n    public function createExecutor(Query $query): AbstractSqlExecutor;\n}\n"
  },
  {
    "path": "src/Query/Expr/Andx.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for building DQL and parts.\n *\n * @link    www.doctrine-project.org\n */\nclass Andx extends Composite\n{\n    protected string $separator = ' AND ';\n\n    /** @var list<class-string<Stringable>> */\n    protected array $allowedClasses = [\n        Comparison::class,\n        Func::class,\n        Orx::class,\n        self::class,\n    ];\n\n    /** @phpstan-var list<string|Comparison|Func|Orx|self> */\n    protected array $parts = [];\n\n    /** @phpstan-return list<string|Comparison|Func|Orx|self> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Base.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse InvalidArgumentException;\nuse Stringable;\n\nuse function array_key_exists;\nuse function count;\nuse function get_debug_type;\nuse function implode;\nuse function in_array;\nuse function is_array;\nuse function is_object;\nuse function is_string;\nuse function sprintf;\n\n/**\n * Abstract base Expr class for building DQL parts.\n *\n * @link    www.doctrine-project.org\n */\nabstract class Base implements Stringable\n{\n    protected string $preSeparator  = '(';\n    protected string $separator     = ', ';\n    protected string $postSeparator = ')';\n\n    /** @var list<class-string<Stringable>> */\n    protected array $allowedClasses = [];\n\n    /** @var list<string|Stringable> */\n    protected array $parts = [];\n\n    public function __construct(mixed $args = [])\n    {\n        if (is_array($args) && array_key_exists(0, $args) && is_array($args[0])) {\n            $args = $args[0];\n        }\n\n        $this->addMultiple($args);\n    }\n\n    /**\n     * @param string[]|object[]|string|object $args\n     * @phpstan-param list<string|object>|string|object $args\n     *\n     * @return $this\n     */\n    public function addMultiple(array|string|object $args = []): static\n    {\n        foreach ((array) $args as $arg) {\n            $this->add($arg);\n        }\n\n        return $this;\n    }\n\n    /**\n     * @param string|Stringable|null $arg\n     *\n     * @return $this\n     *\n     * @throws InvalidArgumentException\n     */\n    public function add(mixed $arg): static\n    {\n        if ($arg !== null && (! $arg instanceof self || $arg->count() > 0)) {\n            // If we decide to keep Expr\\Base instances, we can use this check\n            // @phpstan-ignore function.alreadyNarrowedType (input validation)\n            if (! is_string($arg) && ! (is_object($arg) && in_array($arg::class, $this->allowedClasses, true))) {\n                throw new InvalidArgumentException(sprintf(\n                    \"Expression of type '%s' not allowed in this context.\",\n                    get_debug_type($arg),\n                ));\n            }\n\n            $this->parts[] = $arg;\n        }\n\n        return $this;\n    }\n\n    /** @phpstan-return 0|positive-int */\n    public function count(): int\n    {\n        return count($this->parts);\n    }\n\n    public function __toString(): string\n    {\n        if ($this->count() === 1) {\n            return (string) $this->parts[0];\n        }\n\n        return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Comparison.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for DQL comparison expressions.\n *\n * @link    www.doctrine-project.org\n */\nclass Comparison implements Stringable\n{\n    final public const EQ  = '=';\n    final public const NEQ = '<>';\n    final public const LT  = '<';\n    final public const LTE = '<=';\n    final public const GT  = '>';\n    final public const GTE = '>=';\n\n    /** Creates a comparison expression with the given arguments. */\n    public function __construct(protected mixed $leftExpr, protected string $operator, protected mixed $rightExpr)\n    {\n    }\n\n    public function getLeftExpr(): mixed\n    {\n        return $this->leftExpr;\n    }\n\n    public function getOperator(): string\n    {\n        return $this->operator;\n    }\n\n    public function getRightExpr(): mixed\n    {\n        return $this->rightExpr;\n    }\n\n    public function __toString(): string\n    {\n        return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Composite.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\nuse function implode;\nuse function is_object;\nuse function preg_match;\n\n/**\n * Expression class for building DQL and parts.\n *\n * @link    www.doctrine-project.org\n */\nclass Composite extends Base\n{\n    public function __toString(): string\n    {\n        if ($this->count() === 1) {\n            return (string) $this->parts[0];\n        }\n\n        $components = [];\n\n        foreach ($this->parts as $part) {\n            $components[] = $this->processQueryPart($part);\n        }\n\n        return implode($this->separator, $components);\n    }\n\n    private function processQueryPart(string|Stringable $part): string\n    {\n        $queryPart = (string) $part;\n\n        if (is_object($part) && $part instanceof self && $part->count() > 1) {\n            return $this->preSeparator . $queryPart . $this->postSeparator;\n        }\n\n        // Fixes DDC-1237: User may have added a where item containing nested expression (with \"OR\" or \"AND\")\n        if (preg_match('/\\s(OR|AND)\\s/i', $queryPart)) {\n            return $this->preSeparator . $queryPart . $this->postSeparator;\n        }\n\n        return $queryPart;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/From.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for DQL from.\n *\n * @link    www.doctrine-project.org\n */\nclass From implements Stringable\n{\n    /**\n     * @param class-string $from  The class name.\n     * @param string       $alias The alias of the class.\n     */\n    public function __construct(\n        protected string $from,\n        protected string $alias,\n        protected string|null $indexBy = null,\n    ) {\n    }\n\n    /** @return class-string */\n    public function getFrom(): string\n    {\n        return $this->from;\n    }\n\n    public function getAlias(): string\n    {\n        return $this->alias;\n    }\n\n    public function getIndexBy(): string|null\n    {\n        return $this->indexBy;\n    }\n\n    public function __toString(): string\n    {\n        return $this->from . ' ' . $this->alias .\n                ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '');\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Func.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\nuse function implode;\n\n/**\n * Expression class for generating DQL functions.\n *\n * @link    www.doctrine-project.org\n */\nclass Func implements Stringable\n{\n    /** @var mixed[] */\n    protected array $arguments;\n\n    /**\n     * Creates a function, with the given argument.\n     *\n     * @phpstan-param list<mixed>|mixed $arguments\n     */\n    public function __construct(\n        protected string $name,\n        mixed $arguments,\n    ) {\n        $this->arguments = (array) $arguments;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    /** @phpstan-return list<mixed> */\n    public function getArguments(): array\n    {\n        return $this->arguments;\n    }\n\n    public function __toString(): string\n    {\n        return $this->name . '(' . implode(', ', $this->arguments) . ')';\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/GroupBy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\n/**\n * Expression class for building DQL Group By parts.\n *\n * @link    www.doctrine-project.org\n */\nclass GroupBy extends Base\n{\n    protected string $preSeparator  = '';\n    protected string $postSeparator = '';\n\n    /** @phpstan-var list<string> */\n    protected array $parts = [];\n\n    /** @phpstan-return list<string> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Join.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\nuse function strtoupper;\n\n/**\n * Expression class for DQL join.\n *\n * @link    www.doctrine-project.org\n */\nclass Join implements Stringable\n{\n    final public const INNER_JOIN = 'INNER';\n    final public const LEFT_JOIN  = 'LEFT';\n\n    final public const ON   = 'ON';\n    final public const WITH = 'WITH';\n\n    /**\n     * @phpstan-param self::INNER_JOIN|self::LEFT_JOIN $joinType\n     * @phpstan-param self::ON|self::WITH|null $conditionType\n     */\n    public function __construct(\n        protected string $joinType,\n        protected string $join,\n        protected string|null $alias = null,\n        protected string|null $conditionType = null,\n        protected string|Comparison|Composite|Func|null $condition = null,\n        protected string|null $indexBy = null,\n    ) {\n    }\n\n    /** @phpstan-return self::INNER_JOIN|self::LEFT_JOIN */\n    public function getJoinType(): string\n    {\n        return $this->joinType;\n    }\n\n    public function getJoin(): string\n    {\n        return $this->join;\n    }\n\n    public function getAlias(): string|null\n    {\n        return $this->alias;\n    }\n\n    /** @phpstan-return self::ON|self::WITH|null */\n    public function getConditionType(): string|null\n    {\n        return $this->conditionType;\n    }\n\n    public function getCondition(): string|Comparison|Composite|Func|null\n    {\n        return $this->condition;\n    }\n\n    public function getIndexBy(): string|null\n    {\n        return $this->indexBy;\n    }\n\n    public function __toString(): string\n    {\n        return strtoupper($this->joinType) . ' JOIN ' . $this->join\n             . ($this->alias ? ' ' . $this->alias : '')\n             . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '')\n             . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : '');\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Literal.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\n/**\n * Expression class for generating DQL functions.\n *\n * @link    www.doctrine-project.org\n */\nclass Literal extends Base\n{\n    protected string $preSeparator  = '';\n    protected string $postSeparator = '';\n\n    /** @phpstan-var list<string> */\n    protected array $parts = [];\n\n    /** @phpstan-return list<string> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Math.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for DQL math statements.\n *\n * @link    www.doctrine-project.org\n */\nclass Math implements Stringable\n{\n    /**\n     * Creates a mathematical expression with the given arguments.\n     */\n    public function __construct(\n        protected mixed $leftExpr,\n        protected string $operator,\n        protected mixed $rightExpr,\n    ) {\n    }\n\n    public function getLeftExpr(): mixed\n    {\n        return $this->leftExpr;\n    }\n\n    public function getOperator(): string\n    {\n        return $this->operator;\n    }\n\n    public function getRightExpr(): mixed\n    {\n        return $this->rightExpr;\n    }\n\n    public function __toString(): string\n    {\n        // Adjusting Left Expression\n        $leftExpr = (string) $this->leftExpr;\n\n        if ($this->leftExpr instanceof Math) {\n            $leftExpr = '(' . $leftExpr . ')';\n        }\n\n        // Adjusting Right Expression\n        $rightExpr = (string) $this->rightExpr;\n\n        if ($this->rightExpr instanceof Math) {\n            $rightExpr = '(' . $rightExpr . ')';\n        }\n\n        return $leftExpr . ' ' . $this->operator . ' ' . $rightExpr;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/OrderBy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\nuse function count;\nuse function implode;\n\n/**\n * Expression class for building DQL Order By parts.\n *\n * @link    www.doctrine-project.org\n */\nclass OrderBy implements Stringable\n{\n    protected string $preSeparator  = '';\n    protected string $separator     = ', ';\n    protected string $postSeparator = '';\n\n    /** @var string[] */\n    protected array $allowedClasses = [];\n\n    /** @phpstan-var list<string> */\n    protected array $parts = [];\n\n    public function __construct(\n        string|null $sort = null,\n        string|null $order = null,\n    ) {\n        if ($sort) {\n            $this->add($sort, $order);\n        }\n    }\n\n    public function add(string $sort, string|null $order = null): void\n    {\n        $order         = ! $order ? 'ASC' : $order;\n        $this->parts[] = $sort . ' ' . $order;\n    }\n\n    /** @phpstan-return 0|positive-int */\n    public function count(): int\n    {\n        return count($this->parts);\n    }\n\n    /** @phpstan-return list<string> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n\n    public function __toString(): string\n    {\n        return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Orx.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for building DQL OR clauses.\n *\n * @link    www.doctrine-project.org\n */\nclass Orx extends Composite\n{\n    protected string $separator = ' OR ';\n\n    /** @var list<class-string<Stringable>> */\n    protected array $allowedClasses = [\n        Comparison::class,\n        Func::class,\n        Andx::class,\n        self::class,\n    ];\n\n    /** @phpstan-var list<string|Comparison|Func|Andx|self> */\n    protected array $parts = [];\n\n    /** @phpstan-return list<string|Comparison|Func|Andx|self> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr/Select.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Expr;\n\nuse Stringable;\n\n/**\n * Expression class for building DQL select statements.\n *\n * @link    www.doctrine-project.org\n */\nclass Select extends Base\n{\n    protected string $preSeparator  = '';\n    protected string $postSeparator = '';\n\n    /** @var list<class-string<Stringable>> */\n    protected array $allowedClasses = [Func::class];\n\n    /** @phpstan-var list<string|Func> */\n    protected array $parts = [];\n\n    /** @phpstan-return list<string|Func> */\n    public function getParts(): array\n    {\n        return $this->parts;\n    }\n}\n"
  },
  {
    "path": "src/Query/Expr.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\Internal\\NoUnknownNamedArguments;\nuse Traversable;\n\nuse function implode;\nuse function is_bool;\nuse function is_float;\nuse function is_int;\nuse function is_iterable;\nuse function iterator_to_array;\nuse function str_replace;\n\n/**\n * This class is used to generate DQL expressions via a set of PHP static functions.\n *\n * @link    www.doctrine-project.org\n *\n * @todo Rename: ExpressionBuilder\n */\nclass Expr\n{\n    use NoUnknownNamedArguments;\n\n    /**\n     * Creates a conjunction of the given boolean expressions.\n     *\n     * Example:\n     *\n     *     [php]\n     *     // (u.type = ?1) AND (u.role = ?2)\n     *     $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));\n     *\n     * @param Expr\\Comparison|Expr\\Func|Expr\\Andx|Expr\\Orx|string ...$x Optional clause. Defaults to null,\n     *                                                                  but requires at least one defined\n     *                                                                  when converting to string.\n     */\n    public function andX(Expr\\Comparison|Expr\\Func|Expr\\Andx|Expr\\Orx|string ...$x): Expr\\Andx\n    {\n        self::validateVariadicParameter($x);\n\n        return new Expr\\Andx($x);\n    }\n\n    /**\n     * Creates a disjunction of the given boolean expressions.\n     *\n     * Example:\n     *\n     *     [php]\n     *     // (u.type = ?1) OR (u.role = ?2)\n     *     $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));\n     *\n     * @param Expr\\Comparison|Expr\\Func|Expr\\Andx|Expr\\Orx|string ...$x Optional clause. Defaults to null,\n     *                                                                  but requires at least one defined\n     *                                                                  when converting to string.\n     */\n    public function orX(Expr\\Comparison|Expr\\Func|Expr\\Andx|Expr\\Orx|string ...$x): Expr\\Orx\n    {\n        self::validateVariadicParameter($x);\n\n        return new Expr\\Orx($x);\n    }\n\n    /**\n     * Creates an ASCending order expression.\n     */\n    public function asc(mixed $expr): Expr\\OrderBy\n    {\n        return new Expr\\OrderBy($expr, 'ASC');\n    }\n\n    /**\n     * Creates a DESCending order expression.\n     */\n    public function desc(mixed $expr): Expr\\OrderBy\n    {\n        return new Expr\\OrderBy($expr, 'DESC');\n    }\n\n    /**\n     * Creates an equality comparison expression with the given arguments.\n     *\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> = <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id = ?1\n     *     $expr->eq('u.id', '?1');\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function eq(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::EQ, $y);\n    }\n\n    /**\n     * Creates an instance of Expr\\Comparison, with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> <> <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id <> ?1\n     *     $q->where($q->expr()->neq('u.id', '?1'));\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function neq(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::NEQ, $y);\n    }\n\n    /**\n     * Creates an instance of Expr\\Comparison, with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> < <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id < ?1\n     *     $q->where($q->expr()->lt('u.id', '?1'));\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function lt(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::LT, $y);\n    }\n\n    /**\n     * Creates an instance of Expr\\Comparison, with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> <= <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id <= ?1\n     *     $q->where($q->expr()->lte('u.id', '?1'));\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function lte(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::LTE, $y);\n    }\n\n    /**\n     * Creates an instance of Expr\\Comparison, with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> > <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id > ?1\n     *     $q->where($q->expr()->gt('u.id', '?1'));\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function gt(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::GT, $y);\n    }\n\n    /**\n     * Creates an instance of Expr\\Comparison, with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> >= <right expr>. Example:\n     *\n     *     [php]\n     *     // u.id >= ?1\n     *     $q->where($q->expr()->gte('u.id', '?1'));\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function gte(mixed $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, Expr\\Comparison::GTE, $y);\n    }\n\n    /**\n     * Creates an instance of AVG() function, with the given argument.\n     *\n     * @param mixed $x Argument to be used in AVG() function.\n     */\n    public function avg(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('AVG', [$x]);\n    }\n\n    /**\n     * Creates an instance of MAX() function, with the given argument.\n     *\n     * @param mixed $x Argument to be used in MAX() function.\n     */\n    public function max(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('MAX', [$x]);\n    }\n\n    /**\n     * Creates an instance of MIN() function, with the given argument.\n     *\n     * @param mixed $x Argument to be used in MIN() function.\n     */\n    public function min(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('MIN', [$x]);\n    }\n\n    /**\n     * Creates an instance of COUNT() function, with the given argument.\n     *\n     * @param mixed $x Argument to be used in COUNT() function.\n     */\n    public function count(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('COUNT', [$x]);\n    }\n\n    /**\n     * Creates an instance of COUNT(DISTINCT) function, with the given argument.\n     *\n     * @param mixed ...$x Argument to be used in COUNT(DISTINCT) function.\n     */\n    public function countDistinct(mixed ...$x): string\n    {\n        self::validateVariadicParameter($x);\n\n        return 'COUNT(DISTINCT ' . implode(', ', $x) . ')';\n    }\n\n    /**\n     * Creates an instance of EXISTS() function, with the given DQL Subquery.\n     *\n     * @param mixed $subquery DQL Subquery to be used in EXISTS() function.\n     */\n    public function exists(mixed $subquery): Expr\\Func\n    {\n        return new Expr\\Func('EXISTS', [$subquery]);\n    }\n\n    /**\n     * Creates an instance of ALL() function, with the given DQL Subquery.\n     *\n     * @param mixed $subquery DQL Subquery to be used in ALL() function.\n     */\n    public function all(mixed $subquery): Expr\\Func\n    {\n        return new Expr\\Func('ALL', [$subquery]);\n    }\n\n    /**\n     * Creates a SOME() function expression with the given DQL subquery.\n     *\n     * @param mixed $subquery DQL Subquery to be used in SOME() function.\n     */\n    public function some(mixed $subquery): Expr\\Func\n    {\n        return new Expr\\Func('SOME', [$subquery]);\n    }\n\n    /**\n     * Creates an ANY() function expression with the given DQL subquery.\n     *\n     * @param mixed $subquery DQL Subquery to be used in ANY() function.\n     */\n    public function any(mixed $subquery): Expr\\Func\n    {\n        return new Expr\\Func('ANY', [$subquery]);\n    }\n\n    /**\n     * Creates a negation expression of the given restriction.\n     *\n     * @param mixed $restriction Restriction to be used in NOT() function.\n     */\n    public function not(mixed $restriction): Expr\\Func\n    {\n        return new Expr\\Func('NOT', [$restriction]);\n    }\n\n    /**\n     * Creates an ABS() function expression with the given argument.\n     *\n     * @param mixed $x Argument to be used in ABS() function.\n     */\n    public function abs(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('ABS', [$x]);\n    }\n\n    /**\n     * Creates a MOD($x, $y) function expression to return the remainder of $x divided by $y.\n     */\n    public function mod(mixed $x, mixed $y): Expr\\Func\n    {\n        return new Expr\\Func('MOD', [$x, $y]);\n    }\n\n    /**\n     * Creates a product mathematical expression with the given arguments.\n     *\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> * <right expr>. Example:\n     *\n     *     [php]\n     *     // u.salary * u.percentAnnualSalaryIncrease\n     *     $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease')\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function prod(mixed $x, mixed $y): Expr\\Math\n    {\n        return new Expr\\Math($x, '*', $y);\n    }\n\n    /**\n     * Creates a difference mathematical expression with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> - <right expr>. Example:\n     *\n     *     [php]\n     *     // u.monthlySubscriptionCount - 1\n     *     $q->expr()->diff('u.monthlySubscriptionCount', '1')\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function diff(mixed $x, mixed $y): Expr\\Math\n    {\n        return new Expr\\Math($x, '-', $y);\n    }\n\n    /**\n     * Creates a sum mathematical expression with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> + <right expr>. Example:\n     *\n     *     [php]\n     *     // u.numChildren + 1\n     *     $q->expr()->sum('u.numChildren', '1')\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function sum(mixed $x, mixed $y): Expr\\Math\n    {\n        return new Expr\\Math($x, '+', $y);\n    }\n\n    /**\n     * Creates a quotient mathematical expression with the given arguments.\n     * First argument is considered the left expression and the second is the right expression.\n     * When converted to string, it will generated a <left expr> / <right expr>. Example:\n     *\n     *     [php]\n     *     // u.total / u.period\n     *     $expr->quot('u.total', 'u.period')\n     *\n     * @param mixed $x Left expression.\n     * @param mixed $y Right expression.\n     */\n    public function quot(mixed $x, mixed $y): Expr\\Math\n    {\n        return new Expr\\Math($x, '/', $y);\n    }\n\n    /**\n     * Creates a SQRT() function expression with the given argument.\n     *\n     * @param mixed $x Argument to be used in SQRT() function.\n     */\n    public function sqrt(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('SQRT', [$x]);\n    }\n\n    /**\n     * Creates an IN() expression with the given arguments.\n     *\n     * @param string $x Field in string format to be restricted by IN() function.\n     * @param mixed  $y Argument to be used in IN() function.\n     */\n    public function in(string $x, mixed $y): Expr\\Func\n    {\n        if (is_iterable($y)) {\n            if ($y instanceof Traversable) {\n                $y = iterator_to_array($y);\n            }\n\n            foreach ($y as &$literal) {\n                if (! ($literal instanceof Expr\\Literal)) {\n                    $literal = $this->quoteLiteral($literal);\n                }\n            }\n        }\n\n        return new Expr\\Func($x . ' IN', (array) $y);\n    }\n\n    /**\n     * Creates a NOT IN() expression with the given arguments.\n     *\n     * @param string $x Field in string format to be restricted by NOT IN() function.\n     * @param mixed  $y Argument to be used in NOT IN() function.\n     */\n    public function notIn(string $x, mixed $y): Expr\\Func\n    {\n        if (is_iterable($y)) {\n            if ($y instanceof Traversable) {\n                $y = iterator_to_array($y);\n            }\n\n            foreach ($y as &$literal) {\n                if (! ($literal instanceof Expr\\Literal)) {\n                    $literal = $this->quoteLiteral($literal);\n                }\n            }\n        }\n\n        return new Expr\\Func($x . ' NOT IN', (array) $y);\n    }\n\n    /**\n     * Creates an IS NULL expression with the given arguments.\n     *\n     * @param string $x Field in string format to be restricted by IS NULL.\n     */\n    public function isNull(string $x): string\n    {\n        return $x . ' IS NULL';\n    }\n\n    /**\n     * Creates an IS NOT NULL expression with the given arguments.\n     *\n     * @param string $x Field in string format to be restricted by IS NOT NULL.\n     */\n    public function isNotNull(string $x): string\n    {\n        return $x . ' IS NOT NULL';\n    }\n\n    /**\n     * Creates a LIKE() comparison expression with the given arguments.\n     *\n     * @param string $x Field in string format to be inspected by LIKE() comparison.\n     * @param mixed  $y Argument to be used in LIKE() comparison.\n     */\n    public function like(string $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, 'LIKE', $y);\n    }\n\n    /**\n     * Creates a NOT LIKE() comparison expression with the given arguments.\n     *\n     * @param string $x Field in string format to be inspected by LIKE() comparison.\n     * @param mixed  $y Argument to be used in LIKE() comparison.\n     */\n    public function notLike(string $x, mixed $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, 'NOT LIKE', $y);\n    }\n\n    /**\n     * Creates a CONCAT() function expression with the given arguments.\n     *\n     * @param mixed ...$x Arguments to be used in CONCAT() function.\n     */\n    public function concat(mixed ...$x): Expr\\Func\n    {\n        self::validateVariadicParameter($x);\n\n        return new Expr\\Func('CONCAT', $x);\n    }\n\n    /**\n     * Creates a SUBSTRING() function expression with the given arguments.\n     *\n     * @param mixed    $x    Argument to be used as string to be cropped by SUBSTRING() function.\n     * @param int      $from Initial offset to start cropping string. May accept negative values.\n     * @param int|null $len  Length of crop. May accept negative values.\n     */\n    public function substring(mixed $x, int $from, int|null $len = null): Expr\\Func\n    {\n        $args = [$x, $from];\n        if ($len !== null) {\n            $args[] = $len;\n        }\n\n        return new Expr\\Func('SUBSTRING', $args);\n    }\n\n    /**\n     * Creates a LOWER() function expression with the given argument.\n     *\n     * @param mixed $x Argument to be used in LOWER() function.\n     *\n     * @return Expr\\Func A LOWER function expression.\n     */\n    public function lower(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('LOWER', [$x]);\n    }\n\n    /**\n     * Creates an UPPER() function expression with the given argument.\n     *\n     * @param mixed $x Argument to be used in UPPER() function.\n     *\n     * @return Expr\\Func An UPPER function expression.\n     */\n    public function upper(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('UPPER', [$x]);\n    }\n\n    /**\n     * Creates a LENGTH() function expression with the given argument.\n     *\n     * @param mixed $x Argument to be used as argument of LENGTH() function.\n     *\n     * @return Expr\\Func A LENGTH function expression.\n     */\n    public function length(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('LENGTH', [$x]);\n    }\n\n    /**\n     * Creates a literal expression of the given argument.\n     *\n     * @param scalar $literal Argument to be converted to literal.\n     */\n    public function literal(bool|string|int|float $literal): Expr\\Literal\n    {\n        return new Expr\\Literal($this->quoteLiteral($literal));\n    }\n\n    /**\n     * Quotes a literal value, if necessary, according to the DQL syntax.\n     *\n     * @param scalar $literal The literal value.\n     */\n    private function quoteLiteral(bool|string|int|float $literal): string\n    {\n        if (is_int($literal) || is_float($literal)) {\n            return (string) $literal;\n        }\n\n        if (is_bool($literal)) {\n            return $literal ? 'true' : 'false';\n        }\n\n        return \"'\" . str_replace(\"'\", \"''\", $literal) . \"'\";\n    }\n\n    /**\n     * Creates an instance of BETWEEN() function, with the given argument.\n     *\n     * @param mixed      $val Valued to be inspected by range values.\n     * @param int|string $x   Starting range value to be used in BETWEEN() function.\n     * @param int|string $y   End point value to be used in BETWEEN() function.\n     *\n     * @return string A BETWEEN expression.\n     */\n    public function between(mixed $val, int|string $x, int|string $y): string\n    {\n        return $val . ' BETWEEN ' . $x . ' AND ' . $y;\n    }\n\n    /**\n     * Creates an instance of TRIM() function, with the given argument.\n     *\n     * @param mixed $x Argument to be used as argument of TRIM() function.\n     *\n     * @return Expr\\Func a TRIM expression.\n     */\n    public function trim(mixed $x): Expr\\Func\n    {\n        return new Expr\\Func('TRIM', $x);\n    }\n\n    /**\n     * Creates an instance of MEMBER OF function, with the given arguments.\n     *\n     * @param string $x Value to be checked\n     * @param string $y Value to be checked against\n     */\n    public function isMemberOf(string $x, string $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, 'MEMBER OF', $y);\n    }\n\n    /**\n     * Creates an instance of INSTANCE OF function, with the given arguments.\n     *\n     * @param string $x Value to be checked\n     * @param string $y Value to be checked against\n     */\n    public function isInstanceOf(string $x, string $y): Expr\\Comparison\n    {\n        return new Expr\\Comparison($x, 'INSTANCE OF', $y);\n    }\n}\n"
  },
  {
    "path": "src/Query/Filter/FilterException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Filter;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nuse function sprintf;\n\nclass FilterException extends LogicException implements ORMException\n{\n    public static function cannotConvertListParameterIntoSingleValue(string $name): self\n    {\n        return new self(sprintf('Cannot convert list-based SQL filter parameter \"%s\" into a single value.', $name));\n    }\n\n    public static function cannotConvertSingleParameterIntoListValue(string $name): self\n    {\n        return new self(sprintf('Cannot convert single SQL filter parameter \"%s\" into a list value.', $name));\n    }\n}\n"
  },
  {
    "path": "src/Query/Filter/Parameter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Filter;\n\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\n\n/** @internal */\nfinal class Parameter\n{\n    /** @param ParameterType::*|ArrayParameterType::*|string $type */\n    public function __construct(\n        public readonly mixed $value,\n        public readonly ParameterType|ArrayParameterType|int|string $type,\n        public readonly bool $isList,\n    ) {\n    }\n}\n"
  },
  {
    "path": "src/Query/Filter/SQLFilter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query\\Filter;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\nuse InvalidArgumentException;\nuse Stringable;\n\nuse function array_map;\nuse function implode;\nuse function ksort;\nuse function serialize;\n\n/**\n * The base class that user defined filters should extend.\n *\n * Handles the setting and escaping of parameters.\n *\n * @abstract\n */\nabstract class SQLFilter implements Stringable\n{\n    /**\n     * Parameters for the filter.\n     *\n     * @phpstan-var array<string, Parameter>\n     */\n    private array $parameters = [];\n\n    final public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n    }\n\n    /**\n     * Sets a parameter list that can be used by the filter.\n     *\n     * @param array<mixed> $values List of parameter values.\n     * @param string       $type   The parameter type. If specified, the given value will be run through\n     *                             the type conversion of this type.\n     *\n     * @return $this\n     */\n    final public function setParameterList(string $name, array $values, string $type = Types::STRING): static\n    {\n        $this->parameters[$name] = new Parameter(value: $values, type: $type, isList: true);\n\n        // Keep the parameters sorted for the hash\n        ksort($this->parameters);\n\n        // The filter collection of the EM is now dirty\n        $this->em->getFilters()->setFiltersStateDirty();\n\n        return $this;\n    }\n\n    /**\n     * Sets a parameter that can be used by the filter.\n     *\n     * @param string|null $type The parameter type. If specified, the given value will be run through\n     *                          the type conversion of this type. This is usually not needed for\n     *                          strings and numeric types.\n     *\n     * @return $this\n     */\n    final public function setParameter(string $name, mixed $value, string|null $type = null): static\n    {\n        $this->parameters[$name] = new Parameter(\n            value: $value,\n            type: $type ?? ParameterTypeInferer::inferType($value),\n            isList: false,\n        );\n\n        // Keep the parameters sorted for the hash\n        ksort($this->parameters);\n\n        // The filter collection of the EM is now dirty\n        $this->em->getFilters()->setFiltersStateDirty();\n\n        return $this;\n    }\n\n    /**\n     * Gets a parameter to use in a query.\n     *\n     * The function is responsible for the right output escaping to use the\n     * value in a query.\n     *\n     * @return string The SQL escaped parameter to use in a query.\n     *\n     * @throws InvalidArgumentException\n     */\n    final public function getParameter(string $name): string\n    {\n        if (! isset($this->parameters[$name])) {\n            throw new InvalidArgumentException(\"Parameter '\" . $name . \"' does not exist.\");\n        }\n\n        if ($this->parameters[$name]->isList) {\n            throw FilterException::cannotConvertListParameterIntoSingleValue($name);\n        }\n\n        return $this->em->getConnection()->quote((string) $this->parameters[$name]->value);\n    }\n\n    /**\n     * Gets a parameter to use in a query assuming it's a list of entries.\n     *\n     * The function is responsible for the right output escaping to use the\n     * value in a query, separating each entry by comma to inline it into\n     * an IN() query part.\n     *\n     * @throws InvalidArgumentException\n     */\n    final public function getParameterList(string $name): string\n    {\n        if (! isset($this->parameters[$name])) {\n            throw new InvalidArgumentException(\"Parameter '\" . $name . \"' does not exist.\");\n        }\n\n        if (! $this->parameters[$name]->isList) {\n            throw FilterException::cannotConvertSingleParameterIntoListValue($name);\n        }\n\n        $param      = $this->parameters[$name];\n        $connection = $this->em->getConnection();\n\n        $quoted = array_map(\n            static fn (mixed $value): string => $connection->quote((string) $value),\n            $param->value,\n        );\n\n        return implode(',', $quoted);\n    }\n\n    /**\n     * Checks if a parameter was set for the filter.\n     */\n    final public function hasParameter(string $name): bool\n    {\n        return isset($this->parameters[$name]);\n    }\n\n    /**\n     * Returns as string representation of the SQLFilter parameters (the state).\n     */\n    final public function __toString(): string\n    {\n        return serialize(array_map(\n            static fn (Parameter $value): array => [\n                'value'  => $value->value,\n                'type'   => $value->type,\n                'is_list' => $value->isList,\n            ],\n            $this->parameters,\n        ));\n    }\n\n    /**\n     * Returns the database connection used by the entity manager\n     */\n    final protected function getConnection(): Connection\n    {\n        return $this->em->getConnection();\n    }\n\n    /**\n     * Gets the SQL query part to add to a query.\n     *\n     * @phpstan-param ClassMetadata<object> $targetEntity\n     *\n     * @return string The constraint SQL if there is available, empty string otherwise.\n     */\n    abstract public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string;\n}\n"
  },
  {
    "path": "src/Query/FilterCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse InvalidArgumentException;\n\nuse function assert;\nuse function ksort;\n\n/**\n * Collection class for all the query filters.\n */\nclass FilterCollection\n{\n    /* Filter STATES */\n\n    /**\n     * A filter object is in CLEAN state when it has no changed parameters.\n     */\n    public const FILTERS_STATE_CLEAN = 1;\n\n    /**\n     * A filter object is in DIRTY state when it has changed parameters.\n     */\n    public const FILTERS_STATE_DIRTY = 2;\n\n    private readonly Configuration $config;\n\n    /**\n     * Instances of enabled filters.\n     *\n     * @var array<string, SQLFilter>\n     */\n    private array $enabledFilters = [];\n\n    /** The filter hash from the last time the query was parsed. */\n    private string $filterHash = '';\n\n    /**\n     * Instances of suspended filters.\n     *\n     * @var SQLFilter[]\n     * @phpstan-var array<string, SQLFilter>\n     */\n    private array $suspendedFilters = [];\n\n    /**\n     * The current state of this filter.\n     *\n     * @phpstan-var self::FILTERS_STATE_*\n     */\n    private int $filtersState = self::FILTERS_STATE_CLEAN;\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->config = $em->getConfiguration();\n    }\n\n    /**\n     * Gets all the enabled filters.\n     *\n     * @return array<string, SQLFilter> The enabled filters.\n     */\n    public function getEnabledFilters(): array\n    {\n        return $this->enabledFilters;\n    }\n\n    /**\n     * Gets all the suspended filters.\n     *\n     * @return SQLFilter[] The suspended filters.\n     * @phpstan-return array<string, SQLFilter>\n     */\n    public function getSuspendedFilters(): array\n    {\n        return $this->suspendedFilters;\n    }\n\n    /**\n     * Enables a filter from the collection.\n     *\n     * @throws InvalidArgumentException If the filter does not exist.\n     */\n    public function enable(string $name): SQLFilter\n    {\n        if (! $this->has($name)) {\n            throw new InvalidArgumentException(\"Filter '\" . $name . \"' does not exist.\");\n        }\n\n        if (! $this->isEnabled($name)) {\n            $filterClass = $this->config->getFilterClassName($name);\n\n            assert($filterClass !== null);\n\n            $this->enabledFilters[$name] = new $filterClass($this->em);\n\n            // In case a suspended filter with the same name was forgotten\n            unset($this->suspendedFilters[$name]);\n\n            // Keep the enabled filters sorted for the hash\n            ksort($this->enabledFilters);\n\n            $this->setFiltersStateDirty();\n        }\n\n        return $this->enabledFilters[$name];\n    }\n\n    /**\n     * Disables a filter.\n     *\n     * @throws InvalidArgumentException If the filter does not exist.\n     */\n    public function disable(string $name): SQLFilter\n    {\n        // Get the filter to return it\n        $filter = $this->getFilter($name);\n\n        unset($this->enabledFilters[$name]);\n\n        $this->setFiltersStateDirty();\n\n        return $filter;\n    }\n\n    /**\n     * Suspend a filter.\n     *\n     * @param string $name Name of the filter.\n     *\n     * @return SQLFilter The suspended filter.\n     *\n     * @throws InvalidArgumentException If the filter does not exist.\n     */\n    public function suspend(string $name): SQLFilter\n    {\n        // Get the filter to return it\n        $filter = $this->getFilter($name);\n\n        $this->suspendedFilters[$name] = $filter;\n        unset($this->enabledFilters[$name]);\n\n        $this->setFiltersStateDirty();\n\n        return $filter;\n    }\n\n    /**\n     * Restore a disabled filter from the collection.\n     *\n     * @param string $name Name of the filter.\n     *\n     * @return SQLFilter The restored filter.\n     *\n     * @throws InvalidArgumentException If the filter does not exist.\n     */\n    public function restore(string $name): SQLFilter\n    {\n        if (! $this->isSuspended($name)) {\n            throw new InvalidArgumentException(\"Filter '\" . $name . \"' is not suspended.\");\n        }\n\n        $this->enabledFilters[$name] = $this->suspendedFilters[$name];\n        unset($this->suspendedFilters[$name]);\n\n        // Keep the enabled filters sorted for the hash\n        ksort($this->enabledFilters);\n\n        $this->setFiltersStateDirty();\n\n        return $this->enabledFilters[$name];\n    }\n\n    /**\n     * Gets an enabled filter from the collection.\n     *\n     * @throws InvalidArgumentException If the filter is not enabled.\n     */\n    public function getFilter(string $name): SQLFilter\n    {\n        if (! $this->isEnabled($name)) {\n            throw new InvalidArgumentException(\"Filter '\" . $name . \"' is not enabled.\");\n        }\n\n        return $this->enabledFilters[$name];\n    }\n\n    /**\n     * Checks whether filter with given name is defined.\n     */\n    public function has(string $name): bool\n    {\n        return $this->config->getFilterClassName($name) !== null;\n    }\n\n    /**\n     * Checks if a filter is enabled.\n     */\n    public function isEnabled(string $name): bool\n    {\n        return isset($this->enabledFilters[$name]);\n    }\n\n    /**\n     * Checks if a filter is suspended.\n     *\n     * @param string $name Name of the filter.\n     *\n     * @return bool True if the filter is suspended, false otherwise.\n     */\n    public function isSuspended(string $name): bool\n    {\n        return isset($this->suspendedFilters[$name]);\n    }\n\n    /**\n     * Checks if the filter collection is clean.\n     */\n    public function isClean(): bool\n    {\n        return $this->filtersState === self::FILTERS_STATE_CLEAN;\n    }\n\n    /**\n     * Generates a string of currently enabled filters to use for the cache id.\n     */\n    public function getHash(): string\n    {\n        // If there are only clean filters, the previous hash can be returned\n        if ($this->filtersState === self::FILTERS_STATE_CLEAN) {\n            return $this->filterHash;\n        }\n\n        $filterHash = '';\n\n        foreach ($this->enabledFilters as $name => $filter) {\n            $filterHash .= $name . $filter;\n        }\n\n        $this->filterHash   = $filterHash;\n        $this->filtersState = self::FILTERS_STATE_CLEAN;\n\n        return $filterHash;\n    }\n\n    /**\n     * Sets the filter state to dirty.\n     */\n    public function setFiltersStateDirty(): void\n    {\n        $this->filtersState = self::FILTERS_STATE_DIRTY;\n    }\n}\n"
  },
  {
    "path": "src/Query/Lexer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\Common\\Lexer\\AbstractLexer;\n\nuse function constant;\nuse function ctype_alpha;\nuse function defined;\nuse function is_numeric;\nuse function str_contains;\nuse function str_replace;\nuse function stripos;\nuse function strlen;\nuse function strtoupper;\nuse function substr;\n\n/**\n * Scans a DQL query for tokens.\n *\n * @extends AbstractLexer<TokenType, string>\n */\nclass Lexer extends AbstractLexer\n{\n    /**\n     * Creates a new query scanner object.\n     *\n     * @param string $input A query string.\n     */\n    public function __construct(string $input)\n    {\n        $this->setInput($input);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function getCatchablePatterns(): array\n    {\n        return [\n            '[a-z_][a-z0-9_]*\\:[a-z_][a-z0-9_]*(?:\\\\\\[a-z_][a-z0-9_]*)*', // aliased name\n            '[a-z_\\\\\\][a-z0-9_]*(?:\\\\\\[a-z_][a-z0-9_]*)*', // identifier or qualified name\n            '(?:[0-9]+(?:[\\.][0-9]+)*)(?:e[+-]?[0-9]+)?', // numbers\n            \"'(?:[^']|'')*'\", // quoted strings\n            '\\?[0-9]*|:[a-z_][a-z0-9_]*', // parameters\n        ];\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function getNonCatchablePatterns(): array\n    {\n        return ['\\s+', '--.*', '(.)'];\n    }\n\n    protected function getType(string &$value): TokenType\n    {\n        $type = TokenType::T_NONE;\n\n        switch (true) {\n            // Recognize numeric values\n            case is_numeric($value):\n                if (str_contains($value, '.') || stripos($value, 'e') !== false) {\n                    return TokenType::T_FLOAT;\n                }\n\n                return TokenType::T_INTEGER;\n\n            // Recognize quoted strings\n            case $value[0] === \"'\":\n                $value = str_replace(\"''\", \"'\", substr($value, 1, strlen($value) - 2));\n\n                return TokenType::T_STRING;\n\n            // Recognize identifiers, aliased or qualified names\n            case ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\\\':\n                $name = 'Doctrine\\ORM\\Query\\TokenType::T_' . strtoupper($value);\n\n                if (defined($name)) {\n                    $type = constant($name);\n\n                    if ($type->value > 100) {\n                        return $type;\n                    }\n                }\n\n                if (str_contains($value, '\\\\')) {\n                    return TokenType::T_FULLY_QUALIFIED_NAME;\n                }\n\n                return TokenType::T_IDENTIFIER;\n\n            // Recognize input parameters\n            case $value[0] === '?' || $value[0] === ':':\n                return TokenType::T_INPUT_PARAMETER;\n\n            // Recognize symbols\n            case $value === '.':\n                return TokenType::T_DOT;\n\n            case $value === ',':\n                return TokenType::T_COMMA;\n\n            case $value === '(':\n                return TokenType::T_OPEN_PARENTHESIS;\n\n            case $value === ')':\n                return TokenType::T_CLOSE_PARENTHESIS;\n\n            case $value === '=':\n                return TokenType::T_EQUALS;\n\n            case $value === '>':\n                return TokenType::T_GREATER_THAN;\n\n            case $value === '<':\n                return TokenType::T_LOWER_THAN;\n\n            case $value === '+':\n                return TokenType::T_PLUS;\n\n            case $value === '-':\n                return TokenType::T_MINUS;\n\n            case $value === '*':\n                return TokenType::T_MULTIPLY;\n\n            case $value === '/':\n                return TokenType::T_DIVIDE;\n\n            case $value === '!':\n                return TokenType::T_NEGATE;\n\n            case $value === '{':\n                return TokenType::T_OPEN_CURLY_BRACE;\n\n            case $value === '}':\n                return TokenType::T_CLOSE_CURLY_BRACE;\n\n            // Default\n            default:\n                // Do nothing\n        }\n\n        return $type;\n    }\n}\n"
  },
  {
    "path": "src/Query/OutputWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\n\n/**\n * Interface for output walkers\n *\n * Output walkers, like tree walkers, can traverse the DQL AST to perform\n * their purpose.\n *\n * The goal of an OutputWalker is to ultimately provide the SqlFinalizer\n * which produces the final, executable SQL statement in a \"finalization\" phase.\n *\n * It must be possible to use the same SqlFinalizer for Queries with different\n * firstResult/maxResult values. In other words, SQL produced by the\n * output walker should not depend on those values, and any SQL generation/modification\n * specific to them should happen in the finalizer's `\\Doctrine\\ORM\\Query\\Exec\\SqlFinalizer::createExecutor()`\n * method instead.\n */\ninterface OutputWalker\n{\n    public function getFinalizer(AST\\DeleteStatement|AST\\UpdateStatement|AST\\SelectStatement $AST): SqlFinalizer;\n}\n"
  },
  {
    "path": "src/Query/Parameter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse function trim;\n\n/**\n * Defines a Query Parameter.\n *\n * @link    www.doctrine-project.org\n */\nclass Parameter\n{\n    /**\n     * Returns the internal representation of a parameter name.\n     */\n    public static function normalizeName(int|string $name): string\n    {\n        return trim((string) $name, ':');\n    }\n\n    /**\n     * The parameter name.\n     */\n    private readonly string $name;\n\n    /**\n     * The parameter value.\n     */\n    private mixed $value;\n\n    /**\n     * The parameter type.\n     */\n    private mixed $type;\n\n    /**\n     * Whether the parameter type was explicitly specified or not\n     */\n    private readonly bool $typeSpecified;\n\n    public function __construct(int|string $name, mixed $value, mixed $type = null)\n    {\n        $this->name          = self::normalizeName($name);\n        $this->typeSpecified = $type !== null;\n\n        $this->setValue($value, $type);\n    }\n\n    /**\n     * Retrieves the Parameter name.\n     */\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    /**\n     * Retrieves the Parameter value.\n     */\n    public function getValue(): mixed\n    {\n        return $this->value;\n    }\n\n    /**\n     * Retrieves the Parameter type.\n     */\n    public function getType(): mixed\n    {\n        return $this->type;\n    }\n\n    /**\n     * Defines the Parameter value.\n     */\n    public function setValue(mixed $value, mixed $type = null): void\n    {\n        $this->value = $value;\n        $this->type  = $type ?: ParameterTypeInferer::inferType($value);\n    }\n\n    public function typeWasSpecified(): bool\n    {\n        return $this->typeSpecified;\n    }\n}\n"
  },
  {
    "path": "src/Query/ParameterTypeInferer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse BackedEnum;\nuse DateInterval;\nuse DateTimeImmutable;\nuse DateTimeInterface;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Types\\Types;\n\nuse function current;\nuse function is_array;\nuse function is_bool;\nuse function is_int;\n\n/**\n * Provides an enclosed support for parameter inferring.\n *\n * @link    www.doctrine-project.org\n */\nfinal class ParameterTypeInferer\n{\n    /**\n     * Infers the type of a given value\n     *\n     * @return ParameterType::*|ArrayParameterType::*|Types::*\n     */\n    public static function inferType(mixed $value): ParameterType|ArrayParameterType|int|string\n    {\n        if (is_int($value)) {\n            return Types::INTEGER;\n        }\n\n        if (is_bool($value)) {\n            return Types::BOOLEAN;\n        }\n\n        if ($value instanceof DateTimeImmutable) {\n            return Types::DATETIME_IMMUTABLE;\n        }\n\n        if ($value instanceof DateTimeInterface) {\n            return Types::DATETIME_MUTABLE;\n        }\n\n        if ($value instanceof DateInterval) {\n            return Types::DATEINTERVAL;\n        }\n\n        if ($value instanceof BackedEnum) {\n            return is_int($value->value)\n                ? Types::INTEGER\n                : Types::STRING;\n        }\n\n        if (is_array($value)) {\n            $firstValue = current($value);\n            if ($firstValue instanceof BackedEnum) {\n                $firstValue = $firstValue->value;\n            }\n\n            return is_int($firstValue)\n                ? ArrayParameterType::INTEGER\n                : ArrayParameterType::STRING;\n        }\n\n        return ParameterType::STRING;\n    }\n\n    private function __construct()\n    {\n    }\n}\n"
  },
  {
    "path": "src/Query/Parser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\Common\\Lexer\\Token;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\DuplicateFieldException;\nuse Doctrine\\ORM\\Exception\\NoMatchingPropertyException;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\Functions;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse LogicException;\nuse ReflectionClass;\n\nuse function array_intersect;\nuse function array_key_exists;\nuse function array_search;\nuse function assert;\nuse function class_exists;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function interface_exists;\nuse function is_string;\nuse function sprintf;\nuse function str_contains;\nuse function strlen;\nuse function strpos;\nuse function strrpos;\nuse function strtolower;\nuse function substr;\nuse function trim;\n\n/**\n * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.\n * Parses a DQL query, reports any errors in it, and generates an AST.\n *\n * @phpstan-type DqlToken = Token<TokenType, string>\n * @phpstan-type QueryComponent = array{\n *                 metadata?: ClassMetadata<object>,\n *                 parent?: string|null,\n *                 relation?: AssociationMapping|null,\n *                 map?: string|null,\n *                 resultVariable?: AST\\Node|string,\n *                 nestingLevel: int,\n *                 token: DqlToken,\n *             }\n */\nfinal class Parser\n{\n    /**\n     * @readonly Maps BUILT-IN string function names to AST class names.\n     * @var array<string, class-string<Functions\\FunctionNode>>\n     */\n    private static array $stringFunctions = [\n        'concat'    => Functions\\ConcatFunction::class,\n        'substring' => Functions\\SubstringFunction::class,\n        'trim'      => Functions\\TrimFunction::class,\n        'lower'     => Functions\\LowerFunction::class,\n        'upper'     => Functions\\UpperFunction::class,\n        'identity'  => Functions\\IdentityFunction::class,\n    ];\n\n    /**\n     * @readonly Maps BUILT-IN numeric function names to AST class names.\n     * @var array<string, class-string<Functions\\FunctionNode>>\n     */\n    private static array $numericFunctions = [\n        'length'    => Functions\\LengthFunction::class,\n        'locate'    => Functions\\LocateFunction::class,\n        'abs'       => Functions\\AbsFunction::class,\n        'sqrt'      => Functions\\SqrtFunction::class,\n        'mod'       => Functions\\ModFunction::class,\n        'size'      => Functions\\SizeFunction::class,\n        'date_diff' => Functions\\DateDiffFunction::class,\n        'bit_and'   => Functions\\BitAndFunction::class,\n        'bit_or'    => Functions\\BitOrFunction::class,\n\n        // Aggregate functions\n        'min'       => Functions\\MinFunction::class,\n        'max'       => Functions\\MaxFunction::class,\n        'avg'       => Functions\\AvgFunction::class,\n        'sum'       => Functions\\SumFunction::class,\n        'count'     => Functions\\CountFunction::class,\n    ];\n\n    /**\n     * @readonly Maps BUILT-IN datetime function names to AST class names.\n     * @var array<string, class-string<Functions\\FunctionNode>>\n     */\n    private static array $datetimeFunctions = [\n        'current_date'      => Functions\\CurrentDateFunction::class,\n        'current_time'      => Functions\\CurrentTimeFunction::class,\n        'current_timestamp' => Functions\\CurrentTimestampFunction::class,\n        'date_add'          => Functions\\DateAddFunction::class,\n        'date_sub'          => Functions\\DateSubFunction::class,\n    ];\n\n    /*\n     * Expressions that were encountered during parsing of identifiers and expressions\n     * and still need to be validated.\n     */\n\n    /** @phpstan-var list<array{token: DqlToken|null, expression: mixed, nestingLevel: int}> */\n    private array $deferredIdentificationVariables = [];\n\n    /** @phpstan-var list<array{token: DqlToken|null, expression: AST\\PartialObjectExpression, nestingLevel: int}> */\n    private array $deferredPartialObjectExpressions = [];\n\n    /** @phpstan-var list<array{token: DqlToken|null, expression: AST\\PathExpression, nestingLevel: int}> */\n    private array $deferredPathExpressions = [];\n\n    /** @phpstan-var list<array{token: DqlToken|null, expression: mixed, nestingLevel: int}> */\n    private array $deferredResultVariables = [];\n\n    /** @phpstan-var list<array{token: DqlToken|null, expression: AST\\NewObjectExpression, nestingLevel: int}> */\n    private array $deferredNewObjectExpressions = [];\n\n    /**\n     * The lexer.\n     */\n    private readonly Lexer $lexer;\n\n    /**\n     * The parser result.\n     */\n    private readonly ParserResult $parserResult;\n\n    /**\n     * The EntityManager.\n     */\n    private readonly EntityManagerInterface $em;\n\n    /**\n     * Map of declared query components in the parsed query.\n     *\n     * @phpstan-var array<string, QueryComponent>\n     */\n    private array $queryComponents = [];\n\n    /**\n     * Keeps the nesting level of defined ResultVariables.\n     */\n    private int $nestingLevel = 0;\n\n    /**\n     * Any additional custom tree walkers that modify the AST.\n     *\n     * @var list<class-string<TreeWalker>>\n     */\n    private array $customTreeWalkers = [];\n\n    /**\n     * The custom last tree walker, if any, that is responsible for producing the output.\n     *\n     * @var class-string<SqlWalker>|null\n     */\n    private $customOutputWalker;\n\n    /** @phpstan-var array<string, AST\\SelectExpression> */\n    private array $identVariableExpressions = [];\n\n    /**\n     * Creates a new query parser object.\n     *\n     * @param Query $query The Query to parse.\n     */\n    public function __construct(private readonly Query $query)\n    {\n        $this->em           = $query->getEntityManager();\n        $this->lexer        = new Lexer((string) $query->getDQL());\n        $this->parserResult = new ParserResult();\n    }\n\n    /**\n     * Sets a custom tree walker that produces output.\n     * This tree walker will be run last over the AST, after any other walkers.\n     *\n     * @param class-string<SqlWalker> $className\n     */\n    public function setCustomOutputTreeWalker(string $className): void\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11641',\n            '%s is deprecated, set the output walker class with the \\Doctrine\\ORM\\Query::HINT_CUSTOM_OUTPUT_WALKER query hint instead',\n            __METHOD__,\n        );\n\n        $this->customOutputWalker = $className;\n    }\n\n    /**\n     * Adds a custom tree walker for modifying the AST.\n     *\n     * @param class-string<TreeWalker> $className\n     */\n    public function addCustomTreeWalker(string $className): void\n    {\n        $this->customTreeWalkers[] = $className;\n    }\n\n    /**\n     * Gets the lexer used by the parser.\n     */\n    public function getLexer(): Lexer\n    {\n        return $this->lexer;\n    }\n\n    /**\n     * Gets the ParserResult that is being filled with information during parsing.\n     */\n    public function getParserResult(): ParserResult\n    {\n        return $this->parserResult;\n    }\n\n    /**\n     * Gets the EntityManager used by the parser.\n     */\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    /**\n     * Parses and builds AST for the given Query.\n     */\n    public function getAST(): AST\\SelectStatement|AST\\UpdateStatement|AST\\DeleteStatement\n    {\n        // Parse & build AST\n        $AST = $this->QueryLanguage();\n\n        // Process any deferred validations of some nodes in the AST.\n        // This also allows post-processing of the AST for modification purposes.\n        $this->processDeferredIdentificationVariables();\n\n        if ($this->deferredPartialObjectExpressions) {\n            $this->processDeferredPartialObjectExpressions();\n        }\n\n        if ($this->deferredPathExpressions) {\n            $this->processDeferredPathExpressions();\n        }\n\n        if ($this->deferredResultVariables) {\n            $this->processDeferredResultVariables();\n        }\n\n        if ($this->deferredNewObjectExpressions) {\n            $this->processDeferredNewObjectExpressions($AST);\n        }\n\n        $this->processRootEntityAliasSelected();\n\n        // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!\n        $this->fixIdentificationVariableOrder($AST);\n\n        return $AST;\n    }\n\n    /**\n     * Attempts to match the given token with the current lookahead token.\n     *\n     * If they match, updates the lookahead token; otherwise raises a syntax\n     * error.\n     *\n     * @throws QueryException If the tokens don't match.\n     */\n    public function match(TokenType $token): void\n    {\n        $lookaheadType = $this->lexer->lookahead->type ?? null;\n\n        // Short-circuit on first condition, usually types match\n        if ($lookaheadType === $token) {\n            $this->lexer->moveNext();\n\n            return;\n        }\n\n        // If parameter is not identifier (1-99) must be exact match\n        if ($token->value < TokenType::T_IDENTIFIER->value) {\n            $this->syntaxError($this->lexer->getLiteral($token));\n        }\n\n        // If parameter is keyword (200+) must be exact match\n        if ($token->value > TokenType::T_IDENTIFIER->value) {\n            $this->syntaxError($this->lexer->getLiteral($token));\n        }\n\n        // If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)\n        if ($token->value === TokenType::T_IDENTIFIER->value && $lookaheadType->value < TokenType::T_IDENTIFIER->value) {\n            $this->syntaxError($this->lexer->getLiteral($token));\n        }\n\n        $this->lexer->moveNext();\n    }\n\n    /**\n     * Frees this parser, enabling it to be reused.\n     *\n     * @param bool $deep     Whether to clean peek and reset errors.\n     * @param int  $position Position to reset.\n     */\n    public function free(bool $deep = false, int $position = 0): void\n    {\n        // WARNING! Use this method with care. It resets the scanner!\n        $this->lexer->resetPosition($position);\n\n        // Deep = true cleans peek and also any previously defined errors\n        if ($deep) {\n            $this->lexer->resetPeek();\n        }\n\n        $this->lexer->token     = null;\n        $this->lexer->lookahead = null;\n    }\n\n    /**\n     * Parses a query string.\n     */\n    public function parse(): ParserResult\n    {\n        $AST = $this->getAST();\n\n        $customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);\n        if ($customWalkers !== false) {\n            $this->customTreeWalkers = $customWalkers;\n        }\n\n        $customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER);\n        if ($customOutputWalker !== false) {\n            $this->customOutputWalker = $customOutputWalker;\n        }\n\n        // Run any custom tree walkers over the AST\n        if ($this->customTreeWalkers) {\n            $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents);\n\n            foreach ($this->customTreeWalkers as $walker) {\n                $treeWalkerChain->addTreeWalker($walker);\n            }\n\n            match (true) {\n                $AST instanceof AST\\UpdateStatement => $treeWalkerChain->walkUpdateStatement($AST),\n                $AST instanceof AST\\DeleteStatement => $treeWalkerChain->walkDeleteStatement($AST),\n                $AST instanceof AST\\SelectStatement => $treeWalkerChain->walkSelectStatement($AST),\n            };\n\n            $this->queryComponents = $treeWalkerChain->getQueryComponents();\n        }\n\n        $outputWalkerClass = $this->customOutputWalker ?: SqlOutputWalker::class;\n        $outputWalker      = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents);\n\n        if ($outputWalker instanceof OutputWalker) {\n            $finalizer = $outputWalker->getFinalizer($AST);\n            $this->parserResult->setSqlFinalizer($finalizer);\n        } else {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/11188/',\n                'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.',\n                $outputWalkerClass,\n                OutputWalker::class,\n                SqlFinalizer::class,\n            );\n            // @phpstan-ignore method.deprecated\n            $executor = $outputWalker->getExecutor($AST);\n            // @phpstan-ignore method.deprecated\n            $this->parserResult->setSqlExecutor($executor);\n        }\n\n        return $this->parserResult;\n    }\n\n    /**\n     * Fixes order of identification variables.\n     *\n     * They have to appear in the select clause in the same order as the\n     * declarations (from ... x join ... y join ... z ...) appear in the query\n     * as the hydration process relies on that order for proper operation.\n     */\n    private function fixIdentificationVariableOrder(AST\\SelectStatement|AST\\DeleteStatement|AST\\UpdateStatement $AST): void\n    {\n        if (count($this->identVariableExpressions) <= 1) {\n            return;\n        }\n\n        assert($AST instanceof AST\\SelectStatement);\n\n        foreach ($this->queryComponents as $dqlAlias => $qComp) {\n            if (! isset($this->identVariableExpressions[$dqlAlias])) {\n                continue;\n            }\n\n            $expr = $this->identVariableExpressions[$dqlAlias];\n            $key  = array_search($expr, $AST->selectClause->selectExpressions, true);\n\n            unset($AST->selectClause->selectExpressions[$key]);\n\n            $AST->selectClause->selectExpressions[] = $expr;\n        }\n    }\n\n    /**\n     * Generates a new syntax error.\n     *\n     * @param string        $expected Expected string.\n     * @param DqlToken|null $token    Got token.\n     *\n     * @throws QueryException\n     */\n    public function syntaxError(string $expected = '', Token|null $token = null): never\n    {\n        if ($token === null) {\n            $token = $this->lexer->lookahead;\n        }\n\n        $tokenPos = $token->position ?? '-1';\n\n        $message  = sprintf('line 0, col %d: Error: ', $tokenPos);\n        $message .= $expected !== '' ? sprintf('Expected %s, got ', $expected) : 'Unexpected ';\n        $message .= $this->lexer->lookahead === null ? 'end of string.' : sprintf(\"'%s'\", $token->value);\n\n        throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL() ?? ''));\n    }\n\n    /**\n     * Generates a new semantical error.\n     *\n     * @param string $message Optional message.\n     * @phpstan-param DqlToken|null $token\n     *\n     * @throws QueryException\n     */\n    public function semanticalError(string $message = '', Token|null $token = null): never\n    {\n        if ($token === null) {\n            $token = $this->lexer->lookahead ?? new Token('fake token', 42, 0);\n        }\n\n        // Minimum exposed chars ahead of token\n        $distance = 12;\n\n        // Find a position of a final word to display in error string\n        $dql    = $this->query->getDQL();\n        $length = strlen($dql);\n        $pos    = $token->position + $distance;\n        $pos    = strpos($dql, ' ', $length > $pos ? $pos : $length);\n        $length = $pos !== false ? $pos - $token->position : $distance;\n\n        $tokenPos = $token->position > 0 ? $token->position : '-1';\n        $tokenStr = substr($dql, $token->position, $length);\n\n        // Building informative message\n        $message = 'line 0, col ' . $tokenPos . \" near '\" . $tokenStr . \"': Error: \" . $message;\n\n        throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL()));\n    }\n\n    /**\n     * Peeks beyond the matched closing parenthesis and returns the first token after that one.\n     *\n     * @param bool $resetPeek Reset peek after finding the closing parenthesis.\n     *\n     * @phpstan-return DqlToken|null\n     */\n    private function peekBeyondClosingParenthesis(bool $resetPeek = true): Token|null\n    {\n        $token        = $this->lexer->peek();\n        $numUnmatched = 1;\n\n        while ($numUnmatched > 0 && $token !== null) {\n            switch ($token->type) {\n                case TokenType::T_OPEN_PARENTHESIS:\n                    ++$numUnmatched;\n                    break;\n\n                case TokenType::T_CLOSE_PARENTHESIS:\n                    --$numUnmatched;\n                    break;\n\n                default:\n                    // Do nothing\n            }\n\n            $token = $this->lexer->peek();\n        }\n\n        if ($resetPeek) {\n            $this->lexer->resetPeek();\n        }\n\n        return $token;\n    }\n\n    /**\n     * Checks if the given token indicates a mathematical operator.\n     *\n     * @phpstan-param DqlToken|null $token\n     */\n    private function isMathOperator(Token|null $token): bool\n    {\n        return $token !== null && in_array($token->type, [TokenType::T_PLUS, TokenType::T_MINUS, TokenType::T_DIVIDE, TokenType::T_MULTIPLY], true);\n    }\n\n    /**\n     * Checks if the next-next (after lookahead) token starts a function.\n     *\n     * @return bool TRUE if the next-next tokens start a function, FALSE otherwise.\n     */\n    private function isFunction(): bool\n    {\n        assert($this->lexer->lookahead !== null);\n        $lookaheadType = $this->lexer->lookahead->type;\n        $peek          = $this->lexer->peek();\n\n        $this->lexer->resetPeek();\n\n        return $lookaheadType->value >= TokenType::T_IDENTIFIER->value && $peek !== null && $peek->type === TokenType::T_OPEN_PARENTHESIS;\n    }\n\n    /**\n     * Checks whether the given token type indicates an aggregate function.\n     *\n     * @return bool TRUE if the token type is an aggregate function, FALSE otherwise.\n     */\n    private function isAggregateFunction(TokenType $tokenType): bool\n    {\n        return in_array(\n            $tokenType,\n            [TokenType::T_AVG, TokenType::T_MIN, TokenType::T_MAX, TokenType::T_SUM, TokenType::T_COUNT],\n            true,\n        );\n    }\n\n    /**\n     * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.\n     */\n    private function isNextAllAnySome(): bool\n    {\n        assert($this->lexer->lookahead !== null);\n\n        return in_array(\n            $this->lexer->lookahead->type,\n            [TokenType::T_ALL, TokenType::T_ANY, TokenType::T_SOME],\n            true,\n        );\n    }\n\n    /**\n     * Validates that the given <tt>IdentificationVariable</tt> is semantically correct.\n     * It must exist in query components list.\n     */\n    private function processDeferredIdentificationVariables(): void\n    {\n        foreach ($this->deferredIdentificationVariables as $deferredItem) {\n            $identVariable = $deferredItem['expression'];\n\n            // Check if IdentificationVariable exists in queryComponents\n            if (! isset($this->queryComponents[$identVariable])) {\n                $this->semanticalError(\n                    sprintf(\"'%s' is not defined.\", $identVariable),\n                    $deferredItem['token'],\n                );\n            }\n\n            $qComp = $this->queryComponents[$identVariable];\n\n            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable\n            if (! isset($qComp['metadata'])) {\n                $this->semanticalError(\n                    sprintf(\"'%s' does not point to a Class.\", $identVariable),\n                    $deferredItem['token'],\n                );\n            }\n\n            // Validate if identification variable nesting level is lower or equal than the current one\n            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {\n                $this->semanticalError(\n                    sprintf(\"'%s' is used outside the scope of its declaration.\", $identVariable),\n                    $deferredItem['token'],\n                );\n            }\n        }\n    }\n\n    /**\n     * Validates that the given <tt>NewObjectExpression</tt>.\n     */\n    private function processDeferredNewObjectExpressions(AST\\SelectStatement $AST): void\n    {\n        foreach ($this->deferredNewObjectExpressions as $deferredItem) {\n            $expression    = $deferredItem['expression'];\n            $token         = $deferredItem['token'];\n            $className     = $expression->className;\n            $args          = $expression->args;\n            $fromClassName = $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName ?? null;\n\n            // If the namespace is not given then assumes the first FROM entity namespace\n            if (! str_contains($className, '\\\\') && ! class_exists($className) && is_string($fromClassName) && str_contains($fromClassName, '\\\\')) {\n                $namespace = substr($fromClassName, 0, strrpos($fromClassName, '\\\\'));\n                $fqcn      = $namespace . '\\\\' . $className;\n\n                if (class_exists($fqcn)) {\n                    $expression->className = $fqcn;\n                    $className             = $fqcn;\n                }\n            }\n\n            if (! class_exists($className)) {\n                $this->semanticalError(sprintf('Class \"%s\" is not defined.', $className), $token);\n            }\n\n            $class = new ReflectionClass($className);\n\n            if (! $class->isInstantiable()) {\n                $this->semanticalError(sprintf('Class \"%s\" can not be instantiated.', $className), $token);\n            }\n\n            if ($class->getConstructor() === null) {\n                $this->semanticalError(sprintf('Class \"%s\" has not a valid constructor.', $className), $token);\n            }\n\n            if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) {\n                $this->semanticalError(sprintf('Number of arguments does not match with \"%s\" constructor declaration.', $className), $token);\n            }\n        }\n    }\n\n    /**\n     * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.\n     * It must exist in query components list.\n     */\n    private function processDeferredPartialObjectExpressions(): void\n    {\n        foreach ($this->deferredPartialObjectExpressions as $deferredItem) {\n            $expr  = $deferredItem['expression'];\n            $class = $this->getMetadataForDqlAlias($expr->identificationVariable);\n\n            foreach ($expr->partialFieldSet as $field) {\n                if (isset($class->fieldMappings[$field])) {\n                    continue;\n                }\n\n                if (\n                    isset($class->associationMappings[$field]) &&\n                    $class->associationMappings[$field]->isToOneOwningSide()\n                ) {\n                    continue;\n                }\n\n                $this->semanticalError(sprintf(\n                    \"There is no mapped field named '%s' on class %s.\",\n                    $field,\n                    $class->name,\n                ), $deferredItem['token']);\n            }\n\n            if (array_intersect($class->identifier, $expr->partialFieldSet) !== $class->identifier) {\n                $this->semanticalError(\n                    'The partial field selection of class ' . $class->name . ' must contain the identifier.',\n                    $deferredItem['token'],\n                );\n            }\n        }\n    }\n\n    /**\n     * Validates that the given <tt>ResultVariable</tt> is semantically correct.\n     * It must exist in query components list.\n     */\n    private function processDeferredResultVariables(): void\n    {\n        foreach ($this->deferredResultVariables as $deferredItem) {\n            $resultVariable = $deferredItem['expression'];\n\n            // Check if ResultVariable exists in queryComponents\n            if (! isset($this->queryComponents[$resultVariable])) {\n                $this->semanticalError(\n                    sprintf(\"'%s' is not defined.\", $resultVariable),\n                    $deferredItem['token'],\n                );\n            }\n\n            $qComp = $this->queryComponents[$resultVariable];\n\n            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable\n            if (! isset($qComp['resultVariable'])) {\n                $this->semanticalError(\n                    sprintf(\"'%s' does not point to a ResultVariable.\", $resultVariable),\n                    $deferredItem['token'],\n                );\n            }\n\n            // Validate if identification variable nesting level is lower or equal than the current one\n            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {\n                $this->semanticalError(\n                    sprintf(\"'%s' is used outside the scope of its declaration.\", $resultVariable),\n                    $deferredItem['token'],\n                );\n            }\n        }\n    }\n\n    /**\n     * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:\n     *\n     * AssociationPathExpression             ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression\n     * SingleValuedPathExpression            ::= StateFieldPathExpression | SingleValuedAssociationPathExpression\n     * StateFieldPathExpression              ::= IdentificationVariable \".\" StateField\n     * SingleValuedAssociationPathExpression ::= IdentificationVariable \".\" SingleValuedAssociationField\n     * CollectionValuedPathExpression        ::= IdentificationVariable \".\" CollectionValuedAssociationField\n     */\n    private function processDeferredPathExpressions(): void\n    {\n        foreach ($this->deferredPathExpressions as $deferredItem) {\n            $pathExpression = $deferredItem['expression'];\n\n            $class = $this->getMetadataForDqlAlias($pathExpression->identificationVariable);\n\n            $field = $pathExpression->field;\n            if ($field === null) {\n                $field = $pathExpression->field = $class->identifier[0];\n            }\n\n            // Check if field or association exists\n            if (! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {\n                $this->semanticalError(\n                    'Class ' . $class->name . ' has no field or association named ' . $field,\n                    $deferredItem['token'],\n                );\n            }\n\n            $fieldType = AST\\PathExpression::TYPE_STATE_FIELD;\n\n            if (isset($class->associationMappings[$field])) {\n                $assoc = $class->associationMappings[$field];\n\n                $fieldType = $assoc->isToOne()\n                    ? AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION\n                    : AST\\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;\n            }\n\n            // Validate if PathExpression is one of the expected types\n            $expectedType = $pathExpression->expectedType;\n\n            if (! ($expectedType & $fieldType)) {\n                // We need to recognize which was expected type(s)\n                $expectedStringTypes = [];\n\n                // Validate state field type\n                if ($expectedType & AST\\PathExpression::TYPE_STATE_FIELD) {\n                    $expectedStringTypes[] = 'StateFieldPathExpression';\n                }\n\n                // Validate single valued association (*-to-one)\n                if ($expectedType & AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {\n                    $expectedStringTypes[] = 'SingleValuedAssociationField';\n                }\n\n                // Validate single valued association (*-to-many)\n                if ($expectedType & AST\\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {\n                    $expectedStringTypes[] = 'CollectionValuedAssociationField';\n                }\n\n                // Build the error message\n                $semanticalError  = 'Invalid PathExpression. ';\n                $semanticalError .= count($expectedStringTypes) === 1\n                    ? 'Must be a ' . $expectedStringTypes[0] . '.'\n                    : implode(' or ', $expectedStringTypes) . ' expected.';\n\n                $this->semanticalError($semanticalError, $deferredItem['token']);\n            }\n\n            // We need to force the type in PathExpression\n            $pathExpression->type = $fieldType;\n        }\n    }\n\n    private function processRootEntityAliasSelected(): void\n    {\n        if (! count($this->identVariableExpressions)) {\n            return;\n        }\n\n        foreach ($this->identVariableExpressions as $dqlAlias => $expr) {\n            if (isset($this->queryComponents[$dqlAlias]) && ! isset($this->queryComponents[$dqlAlias]['parent'])) {\n                return;\n            }\n        }\n\n        $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');\n    }\n\n    /**\n     * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement\n     */\n    public function QueryLanguage(): AST\\SelectStatement|AST\\UpdateStatement|AST\\DeleteStatement\n    {\n        $statement = null;\n\n        $this->lexer->moveNext();\n\n        switch ($this->lexer->lookahead->type ?? null) {\n            case TokenType::T_SELECT:\n                $statement = $this->SelectStatement();\n                break;\n\n            case TokenType::T_UPDATE:\n                $statement = $this->UpdateStatement();\n                break;\n\n            case TokenType::T_DELETE:\n                $statement = $this->DeleteStatement();\n                break;\n\n            default:\n                $this->syntaxError('SELECT, UPDATE or DELETE');\n                break;\n        }\n\n        // Check for end of string\n        if ($this->lexer->lookahead !== null) {\n            $this->syntaxError('end of string');\n        }\n\n        return $statement;\n    }\n\n    /**\n     * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n     */\n    public function SelectStatement(): AST\\SelectStatement\n    {\n        $selectStatement = new AST\\SelectStatement($this->SelectClause(), $this->FromClause());\n\n        $selectStatement->whereClause   = $this->lexer->isNextToken(TokenType::T_WHERE) ? $this->WhereClause() : null;\n        $selectStatement->groupByClause = $this->lexer->isNextToken(TokenType::T_GROUP) ? $this->GroupByClause() : null;\n        $selectStatement->havingClause  = $this->lexer->isNextToken(TokenType::T_HAVING) ? $this->HavingClause() : null;\n        $selectStatement->orderByClause = $this->lexer->isNextToken(TokenType::T_ORDER) ? $this->OrderByClause() : null;\n\n        return $selectStatement;\n    }\n\n    /**\n     * UpdateStatement ::= UpdateClause [WhereClause]\n     */\n    public function UpdateStatement(): AST\\UpdateStatement\n    {\n        $updateStatement = new AST\\UpdateStatement($this->UpdateClause());\n\n        $updateStatement->whereClause = $this->lexer->isNextToken(TokenType::T_WHERE) ? $this->WhereClause() : null;\n\n        return $updateStatement;\n    }\n\n    /**\n     * DeleteStatement ::= DeleteClause [WhereClause]\n     */\n    public function DeleteStatement(): AST\\DeleteStatement\n    {\n        $deleteStatement = new AST\\DeleteStatement($this->DeleteClause());\n\n        $deleteStatement->whereClause = $this->lexer->isNextToken(TokenType::T_WHERE) ? $this->WhereClause() : null;\n\n        return $deleteStatement;\n    }\n\n    /**\n     * IdentificationVariable ::= identifier\n     */\n    public function IdentificationVariable(): string\n    {\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $identVariable = $this->lexer->token->value;\n\n        $this->deferredIdentificationVariables[] = [\n            'expression'   => $identVariable,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $this->lexer->token,\n        ];\n\n        return $identVariable;\n    }\n\n    /**\n     * AliasIdentificationVariable = identifier\n     */\n    public function AliasIdentificationVariable(): string\n    {\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $aliasIdentVariable = $this->lexer->token->value;\n        $exists             = isset($this->queryComponents[$aliasIdentVariable]);\n\n        if ($exists) {\n            $this->semanticalError(\n                sprintf(\"'%s' is already defined.\", $aliasIdentVariable),\n                $this->lexer->token,\n            );\n        }\n\n        return $aliasIdentVariable;\n    }\n\n    /**\n     * AbstractSchemaName ::= fully_qualified_name | identifier\n     */\n    public function AbstractSchemaName(): string\n    {\n        if ($this->lexer->isNextToken(TokenType::T_FULLY_QUALIFIED_NAME)) {\n            $this->match(TokenType::T_FULLY_QUALIFIED_NAME);\n            assert($this->lexer->token !== null);\n\n            return $this->lexer->token->value;\n        }\n\n        $this->match(TokenType::T_IDENTIFIER);\n        assert($this->lexer->token !== null);\n\n        return $this->lexer->token->value;\n    }\n\n    /**\n     * Validates an AbstractSchemaName, making sure the class exists.\n     *\n     * @param string $schemaName The name to validate.\n     *\n     * @throws QueryException if the name does not exist.\n     */\n    private function validateAbstractSchemaName(string $schemaName): void\n    {\n        assert($this->lexer->token !== null);\n        if (! (class_exists($schemaName, true) || interface_exists($schemaName, true))) {\n            $this->semanticalError(\n                sprintf(\"Class '%s' is not defined.\", $schemaName),\n                $this->lexer->token,\n            );\n        }\n    }\n\n    /**\n     * AliasResultVariable ::= identifier\n     */\n    public function AliasResultVariable(): string\n    {\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $resultVariable = $this->lexer->token->value;\n        $exists         = isset($this->queryComponents[$resultVariable]);\n\n        if ($exists) {\n            $this->semanticalError(\n                sprintf(\"'%s' is already defined.\", $resultVariable),\n                $this->lexer->token,\n            );\n        }\n\n        return $resultVariable;\n    }\n\n    /**\n     * ResultVariable ::= identifier\n     */\n    public function ResultVariable(): string\n    {\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $resultVariable = $this->lexer->token->value;\n\n        // Defer ResultVariable validation\n        $this->deferredResultVariables[] = [\n            'expression'   => $resultVariable,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $this->lexer->token,\n        ];\n\n        return $resultVariable;\n    }\n\n    /**\n     * JoinAssociationPathExpression ::= IdentificationVariable \".\" (CollectionValuedAssociationField | SingleValuedAssociationField)\n     */\n    public function JoinAssociationPathExpression(): AST\\JoinAssociationPathExpression\n    {\n        $identVariable = $this->IdentificationVariable();\n\n        if (! isset($this->queryComponents[$identVariable])) {\n            $this->semanticalError(\n                'Identification Variable ' . $identVariable . ' used in join path expression but was not defined before.',\n            );\n        }\n\n        $this->match(TokenType::T_DOT);\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $field = $this->lexer->token->value;\n\n        // Validate association field\n        $class = $this->getMetadataForDqlAlias($identVariable);\n\n        if (! $class->hasAssociation($field)) {\n            $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);\n        }\n\n        return new AST\\JoinAssociationPathExpression($identVariable, $field);\n    }\n\n    /**\n     * Parses an arbitrary path expression and defers semantical validation\n     * based on expected types.\n     *\n     * PathExpression ::= IdentificationVariable {\".\" identifier}*\n     *\n     * @phpstan-param int-mask-of<AST\\PathExpression::TYPE_*> $expectedTypes\n     */\n    public function PathExpression(int $expectedTypes): AST\\PathExpression\n    {\n        $identVariable = $this->IdentificationVariable();\n        $field         = null;\n\n        assert($this->lexer->token !== null);\n        if ($this->lexer->isNextToken(TokenType::T_DOT)) {\n            $this->match(TokenType::T_DOT);\n            $this->match(TokenType::T_IDENTIFIER);\n\n            $field = $this->lexer->token->value;\n\n            while ($this->lexer->isNextToken(TokenType::T_DOT)) {\n                $this->match(TokenType::T_DOT);\n                $this->match(TokenType::T_IDENTIFIER);\n                $field .= '.' . $this->lexer->token->value;\n            }\n        }\n\n        // Creating AST node\n        $pathExpr = new AST\\PathExpression($expectedTypes, $identVariable, $field);\n\n        // Defer PathExpression validation if requested to be deferred\n        $this->deferredPathExpressions[] = [\n            'expression'   => $pathExpr,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $this->lexer->token,\n        ];\n\n        return $pathExpr;\n    }\n\n    /**\n     * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression\n     */\n    public function AssociationPathExpression(): AST\\PathExpression\n    {\n        return $this->PathExpression(\n            AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |\n            AST\\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION,\n        );\n    }\n\n    /**\n     * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression\n     */\n    public function SingleValuedPathExpression(): AST\\PathExpression\n    {\n        return $this->PathExpression(\n            AST\\PathExpression::TYPE_STATE_FIELD |\n            AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,\n        );\n    }\n\n    /**\n     * StateFieldPathExpression ::= IdentificationVariable \".\" StateField\n     */\n    public function StateFieldPathExpression(): AST\\PathExpression\n    {\n        return $this->PathExpression(AST\\PathExpression::TYPE_STATE_FIELD);\n    }\n\n    /**\n     * SingleValuedAssociationPathExpression ::= IdentificationVariable \".\" SingleValuedAssociationField\n     */\n    public function SingleValuedAssociationPathExpression(): AST\\PathExpression\n    {\n        return $this->PathExpression(AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);\n    }\n\n    /**\n     * CollectionValuedPathExpression ::= IdentificationVariable \".\" CollectionValuedAssociationField\n     */\n    public function CollectionValuedPathExpression(): AST\\PathExpression\n    {\n        return $this->PathExpression(AST\\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);\n    }\n\n    /**\n     * EntityAsDtoArgumentExpression ::= IdentificationVariable\n     */\n    public function EntityAsDtoArgumentExpression(): AST\\EntityAsDtoArgumentExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        $expression    = null;\n        $identVariable = null;\n        $peek          = $this->lexer->glimpse();\n        $lookaheadType = $this->lexer->lookahead->type;\n        assert($peek !== null);\n\n        assert($lookaheadType === TokenType::T_IDENTIFIER);\n        assert($peek->type !== TokenType::T_DOT);\n        assert($peek->type !== TokenType::T_OPEN_PARENTHESIS);\n\n        $expression = $identVariable = $this->IdentificationVariable();\n\n        // [[\"AS\"] AliasResultVariable]\n        $mustHaveAliasResultVariable = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n\n            $mustHaveAliasResultVariable = true;\n        }\n\n        $aliasResultVariable = null;\n\n        if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(TokenType::T_IDENTIFIER)) {\n            $token               = $this->lexer->lookahead;\n            $aliasResultVariable = $this->AliasResultVariable();\n\n            // Include AliasResultVariable in query components.\n            $this->queryComponents[$aliasResultVariable] = [\n                'resultVariable' => $expression,\n                'nestingLevel'   => $this->nestingLevel,\n                'token'          => $token,\n            ];\n        }\n\n        return new AST\\EntityAsDtoArgumentExpression($expression, $identVariable, $aliasResultVariable);\n    }\n\n    /**\n     * SelectClause ::= \"SELECT\" [\"DISTINCT\"] SelectExpression {\",\" SelectExpression}\n     */\n    public function SelectClause(): AST\\SelectClause\n    {\n        $isDistinct = false;\n        $this->match(TokenType::T_SELECT);\n\n        // Check for DISTINCT\n        if ($this->lexer->isNextToken(TokenType::T_DISTINCT)) {\n            $this->match(TokenType::T_DISTINCT);\n\n            $isDistinct = true;\n        }\n\n        // Process SelectExpressions (1..N)\n        $selectExpressions   = [];\n        $selectExpressions[] = $this->SelectExpression();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $selectExpressions[] = $this->SelectExpression();\n        }\n\n        return new AST\\SelectClause($selectExpressions, $isDistinct);\n    }\n\n    /**\n     * SimpleSelectClause ::= \"SELECT\" [\"DISTINCT\"] SimpleSelectExpression\n     */\n    public function SimpleSelectClause(): AST\\SimpleSelectClause\n    {\n        $isDistinct = false;\n        $this->match(TokenType::T_SELECT);\n\n        if ($this->lexer->isNextToken(TokenType::T_DISTINCT)) {\n            $this->match(TokenType::T_DISTINCT);\n\n            $isDistinct = true;\n        }\n\n        return new AST\\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);\n    }\n\n    /**\n     * UpdateClause ::= \"UPDATE\" AbstractSchemaName [\"AS\"] AliasIdentificationVariable \"SET\" UpdateItem {\",\" UpdateItem}*\n     */\n    public function UpdateClause(): AST\\UpdateClause\n    {\n        $this->match(TokenType::T_UPDATE);\n        assert($this->lexer->lookahead !== null);\n\n        $token              = $this->lexer->lookahead;\n        $abstractSchemaName = $this->AbstractSchemaName();\n\n        $this->validateAbstractSchemaName($abstractSchemaName);\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n        }\n\n        $aliasIdentificationVariable = $this->AliasIdentificationVariable();\n\n        $class = $this->em->getClassMetadata($abstractSchemaName);\n\n        // Building queryComponent\n        $queryComponent = [\n            'metadata'     => $class,\n            'parent'       => null,\n            'relation'     => null,\n            'map'          => null,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $token,\n        ];\n\n        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;\n\n        $this->match(TokenType::T_SET);\n\n        $updateItems   = [];\n        $updateItems[] = $this->UpdateItem();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $updateItems[] = $this->UpdateItem();\n        }\n\n        $updateClause                              = new AST\\UpdateClause($abstractSchemaName, $updateItems);\n        $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;\n\n        return $updateClause;\n    }\n\n    /**\n     * DeleteClause ::= \"DELETE\" [\"FROM\"] AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n     */\n    public function DeleteClause(): AST\\DeleteClause\n    {\n        $this->match(TokenType::T_DELETE);\n\n        if ($this->lexer->isNextToken(TokenType::T_FROM)) {\n            $this->match(TokenType::T_FROM);\n        }\n\n        assert($this->lexer->lookahead !== null);\n        $token              = $this->lexer->lookahead;\n        $abstractSchemaName = $this->AbstractSchemaName();\n\n        $this->validateAbstractSchemaName($abstractSchemaName);\n\n        $deleteClause = new AST\\DeleteClause($abstractSchemaName);\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n        }\n\n        $aliasIdentificationVariable = $this->lexer->isNextToken(TokenType::T_IDENTIFIER)\n            ? $this->AliasIdentificationVariable()\n            : 'alias_should_have_been_set';\n\n        $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;\n        $class                                     = $this->em->getClassMetadata($deleteClause->abstractSchemaName);\n\n        // Building queryComponent\n        $queryComponent = [\n            'metadata'     => $class,\n            'parent'       => null,\n            'relation'     => null,\n            'map'          => null,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $token,\n        ];\n\n        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;\n\n        return $deleteClause;\n    }\n\n    /**\n     * FromClause ::= \"FROM\" IdentificationVariableDeclaration {\",\" IdentificationVariableDeclaration}*\n     */\n    public function FromClause(): AST\\FromClause\n    {\n        $this->match(TokenType::T_FROM);\n\n        $identificationVariableDeclarations   = [];\n        $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();\n        }\n\n        return new AST\\FromClause($identificationVariableDeclarations);\n    }\n\n    /**\n     * SubselectFromClause ::= \"FROM\" SubselectIdentificationVariableDeclaration {\",\" SubselectIdentificationVariableDeclaration}*\n     */\n    public function SubselectFromClause(): AST\\SubselectFromClause\n    {\n        $this->match(TokenType::T_FROM);\n\n        $identificationVariables   = [];\n        $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();\n        }\n\n        return new AST\\SubselectFromClause($identificationVariables);\n    }\n\n    /**\n     * WhereClause ::= \"WHERE\" ConditionalExpression\n     */\n    public function WhereClause(): AST\\WhereClause\n    {\n        $this->match(TokenType::T_WHERE);\n\n        return new AST\\WhereClause($this->ConditionalExpression());\n    }\n\n    /**\n     * HavingClause ::= \"HAVING\" ConditionalExpression\n     */\n    public function HavingClause(): AST\\HavingClause\n    {\n        $this->match(TokenType::T_HAVING);\n\n        return new AST\\HavingClause($this->ConditionalExpression());\n    }\n\n    /**\n     * GroupByClause ::= \"GROUP\" \"BY\" GroupByItem {\",\" GroupByItem}*\n     */\n    public function GroupByClause(): AST\\GroupByClause\n    {\n        $this->match(TokenType::T_GROUP);\n        $this->match(TokenType::T_BY);\n\n        $groupByItems = [$this->GroupByItem()];\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $groupByItems[] = $this->GroupByItem();\n        }\n\n        return new AST\\GroupByClause($groupByItems);\n    }\n\n    /**\n     * OrderByClause ::= \"ORDER\" \"BY\" OrderByItem {\",\" OrderByItem}*\n     */\n    public function OrderByClause(): AST\\OrderByClause\n    {\n        $this->match(TokenType::T_ORDER);\n        $this->match(TokenType::T_BY);\n\n        $orderByItems   = [];\n        $orderByItems[] = $this->OrderByItem();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $orderByItems[] = $this->OrderByItem();\n        }\n\n        return new AST\\OrderByClause($orderByItems);\n    }\n\n    /**\n     * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]\n     */\n    public function Subselect(): AST\\Subselect\n    {\n        // Increase query nesting level\n        $this->nestingLevel++;\n\n        $subselect = new AST\\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());\n\n        $subselect->whereClause   = $this->lexer->isNextToken(TokenType::T_WHERE) ? $this->WhereClause() : null;\n        $subselect->groupByClause = $this->lexer->isNextToken(TokenType::T_GROUP) ? $this->GroupByClause() : null;\n        $subselect->havingClause  = $this->lexer->isNextToken(TokenType::T_HAVING) ? $this->HavingClause() : null;\n        $subselect->orderByClause = $this->lexer->isNextToken(TokenType::T_ORDER) ? $this->OrderByClause() : null;\n\n        // Decrease query nesting level\n        $this->nestingLevel--;\n\n        return $subselect;\n    }\n\n    /**\n     * UpdateItem ::= SingleValuedPathExpression \"=\" NewValue\n     */\n    public function UpdateItem(): AST\\UpdateItem\n    {\n        $pathExpr = $this->SingleValuedPathExpression();\n\n        $this->match(TokenType::T_EQUALS);\n\n        return new AST\\UpdateItem($pathExpr, $this->NewValue());\n    }\n\n    /**\n     * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression\n     */\n    public function GroupByItem(): string|AST\\PathExpression\n    {\n        // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression\n        $glimpse = $this->lexer->glimpse();\n\n        if ($glimpse !== null && $glimpse->type === TokenType::T_DOT) {\n            return $this->SingleValuedPathExpression();\n        }\n\n        assert($this->lexer->lookahead !== null);\n        // Still need to decide between IdentificationVariable or ResultVariable\n        $lookaheadValue = $this->lexer->lookahead->value;\n\n        if (! isset($this->queryComponents[$lookaheadValue])) {\n            $this->semanticalError('Cannot group by undefined identification or result variable.');\n        }\n\n        return isset($this->queryComponents[$lookaheadValue]['metadata'])\n            ? $this->IdentificationVariable()\n            : $this->ResultVariable();\n    }\n\n    /**\n     * OrderByItem ::= (\n     *      SimpleArithmeticExpression | SingleValuedPathExpression | CaseExpression |\n     *      ScalarExpression | ResultVariable | FunctionDeclaration\n     * ) [\"ASC\" | \"DESC\"]\n     */\n    public function OrderByItem(): AST\\OrderByItem\n    {\n        $this->lexer->peek(); // lookahead => '.'\n        $this->lexer->peek(); // lookahead => token after '.'\n\n        $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'\n\n        $this->lexer->resetPeek();\n\n        $glimpse = $this->lexer->glimpse();\n\n        assert($this->lexer->lookahead !== null);\n        $expr = match (true) {\n            $this->isMathOperator($peek) || $this->isMathOperator($glimpse) => $this->SimpleArithmeticExpression(),\n            $glimpse !== null && $glimpse->type === TokenType::T_DOT => $this->SingleValuedPathExpression(),\n            $this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->ScalarExpression(),\n            $this->lexer->lookahead->type === TokenType::T_CASE => $this->CaseExpression(),\n            $this->isFunction() => $this->FunctionDeclaration(),\n            default => $this->ResultVariable(),\n        };\n\n        $type = 'ASC';\n        $item = new AST\\OrderByItem($expr);\n\n        switch (true) {\n            case $this->lexer->isNextToken(TokenType::T_DESC):\n                $this->match(TokenType::T_DESC);\n                $type = 'DESC';\n                break;\n\n            case $this->lexer->isNextToken(TokenType::T_ASC):\n                $this->match(TokenType::T_ASC);\n                break;\n\n            default:\n                // Do nothing\n        }\n\n        $item->type = $type;\n\n        return $item;\n    }\n\n    /**\n     * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |\n     *      EnumPrimary | SimpleEntityExpression | \"NULL\"\n     *\n     * NOTE: Since it is not possible to correctly recognize individual types, here is the full\n     * grammar that needs to be supported:\n     *\n     * NewValue ::= SimpleArithmeticExpression | \"NULL\"\n     *\n     * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression\n     */\n    public function NewValue(): AST\\ArithmeticExpression|AST\\InputParameter|null\n    {\n        if ($this->lexer->isNextToken(TokenType::T_NULL)) {\n            $this->match(TokenType::T_NULL);\n\n            return null;\n        }\n\n        if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) {\n            $this->match(TokenType::T_INPUT_PARAMETER);\n            assert($this->lexer->token !== null);\n\n            return new AST\\InputParameter($this->lexer->token->value);\n        }\n\n        return $this->ArithmeticExpression();\n    }\n\n    /**\n     * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*\n     */\n    public function IdentificationVariableDeclaration(): AST\\IdentificationVariableDeclaration\n    {\n        $joins                    = [];\n        $rangeVariableDeclaration = $this->RangeVariableDeclaration();\n        $indexBy                  = $this->lexer->isNextToken(TokenType::T_INDEX)\n            ? $this->IndexBy()\n            : null;\n\n        $rangeVariableDeclaration->isRoot = true;\n\n        while (\n            $this->lexer->isNextToken(TokenType::T_LEFT) ||\n            $this->lexer->isNextToken(TokenType::T_INNER) ||\n            $this->lexer->isNextToken(TokenType::T_JOIN)\n        ) {\n            $joins[] = $this->Join();\n        }\n\n        return new AST\\IdentificationVariableDeclaration(\n            $rangeVariableDeclaration,\n            $indexBy,\n            $joins,\n        );\n    }\n\n    /**\n     * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration\n     *\n     * {Internal note: WARNING: Solution is harder than a bare implementation.\n     * Desired EBNF support:\n     *\n     * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression [\"AS\"] AliasIdentificationVariable)\n     *\n     * It demands that entire SQL generation to become programmatical. This is\n     * needed because association based subselect requires \"WHERE\" conditional\n     * expressions to be injected, but there is no scope to do that. Only scope\n     * accessible is \"FROM\", prohibiting an easy implementation without larger\n     * changes.}\n     */\n    public function SubselectIdentificationVariableDeclaration(): AST\\IdentificationVariableDeclaration\n    {\n        /*\n        NOT YET IMPLEMENTED!\n\n        $glimpse = $this->lexer->glimpse();\n\n        if ($glimpse->type == TokenType::T_DOT) {\n            $associationPathExpression = $this->AssociationPathExpression();\n\n            if ($this->lexer->isNextToken(TokenType::T_AS)) {\n                $this->match(TokenType::T_AS);\n            }\n\n            $aliasIdentificationVariable = $this->AliasIdentificationVariable();\n            $identificationVariable      = $associationPathExpression->identificationVariable;\n            $field                       = $associationPathExpression->associationField;\n\n            $class       = $this->queryComponents[$identificationVariable]['metadata'];\n            $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']);\n\n            // Building queryComponent\n            $joinQueryComponent = array(\n                'metadata'     => $targetClass,\n                'parent'       => $identificationVariable,\n                'relation'     => $class->getAssociationMapping($field),\n                'map'          => null,\n                'nestingLevel' => $this->nestingLevel,\n                'token'        => $this->lexer->lookahead\n            );\n\n            $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;\n\n            return new AST\\SubselectIdentificationVariableDeclaration(\n                $associationPathExpression, $aliasIdentificationVariable\n            );\n        }\n        */\n\n        return $this->IdentificationVariableDeclaration();\n    }\n\n    /**\n     * Join ::= [\"LEFT\" [\"OUTER\"] | \"INNER\"] \"JOIN\"\n     *          (JoinAssociationDeclaration [\"WITH\" ConditionalExpression] | RangeVariableDeclaration [(\"ON\" | \"WITH\") ConditionalExpression])\n     */\n    public function Join(): AST\\Join\n    {\n        // Check Join type\n        $joinType = AST\\Join::JOIN_TYPE_INNER;\n\n        switch (true) {\n            case $this->lexer->isNextToken(TokenType::T_LEFT):\n                $this->match(TokenType::T_LEFT);\n\n                $joinType = AST\\Join::JOIN_TYPE_LEFT;\n\n                // Possible LEFT OUTER join\n                if ($this->lexer->isNextToken(TokenType::T_OUTER)) {\n                    $this->match(TokenType::T_OUTER);\n\n                    $joinType = AST\\Join::JOIN_TYPE_LEFTOUTER;\n                }\n\n                break;\n\n            case $this->lexer->isNextToken(TokenType::T_INNER):\n                $this->match(TokenType::T_INNER);\n                break;\n\n            default:\n                // Do nothing\n        }\n\n        $this->match(TokenType::T_JOIN);\n\n        $next = $this->lexer->glimpse();\n        assert($next !== null);\n        $conditionalExpression = null;\n\n        if ($next->type === TokenType::T_DOT) {\n            $joinDeclaration = $this->JoinAssociationDeclaration();\n\n            if ($this->lexer->isNextToken(TokenType::T_WITH)) {\n                $this->match(TokenType::T_WITH);\n                $conditionalExpression = $this->ConditionalExpression();\n            }\n        } else {\n            $joinDeclaration         = $this->RangeVariableDeclaration();\n            $joinDeclaration->isRoot = false;\n\n            if ($this->lexer->isNextToken(TokenType::T_ON)) {\n                $this->match(TokenType::T_ON);\n                $conditionalExpression = $this->ConditionalExpression();\n            } elseif ($this->lexer->isNextToken(TokenType::T_WITH)) {\n                $this->match(TokenType::T_WITH);\n                $conditionalExpression = $this->ConditionalExpression();\n                Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/12192', 'Using WITH for the join condition of arbitrary joins is deprecated. Use ON instead.');\n            }\n        }\n\n        $join                        = new AST\\Join($joinType, $joinDeclaration);\n        $join->conditionalExpression = $conditionalExpression;\n\n        return $join;\n    }\n\n    /**\n     * RangeVariableDeclaration ::= AbstractSchemaName [\"AS\"] AliasIdentificationVariable\n     *\n     * @throws QueryException\n     */\n    public function RangeVariableDeclaration(): AST\\RangeVariableDeclaration\n    {\n        if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS) && $this->lexer->glimpse()->type === TokenType::T_SELECT) {\n            $this->semanticalError('Subquery is not supported here', $this->lexer->token);\n        }\n\n        $abstractSchemaName = $this->AbstractSchemaName();\n\n        $this->validateAbstractSchemaName($abstractSchemaName);\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n        }\n\n        assert($this->lexer->lookahead !== null);\n        $token                       = $this->lexer->lookahead;\n        $aliasIdentificationVariable = $this->AliasIdentificationVariable();\n        $classMetadata               = $this->em->getClassMetadata($abstractSchemaName);\n\n        // Building queryComponent\n        $queryComponent = [\n            'metadata'     => $classMetadata,\n            'parent'       => null,\n            'relation'     => null,\n            'map'          => null,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $token,\n        ];\n\n        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;\n\n        return new AST\\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);\n    }\n\n    /**\n     * JoinAssociationDeclaration ::= JoinAssociationPathExpression [\"AS\"] AliasIdentificationVariable [IndexBy]\n     */\n    public function JoinAssociationDeclaration(): AST\\JoinAssociationDeclaration\n    {\n        $joinAssociationPathExpression = $this->JoinAssociationPathExpression();\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n        }\n\n        assert($this->lexer->lookahead !== null);\n\n        $aliasIdentificationVariable = $this->AliasIdentificationVariable();\n        $indexBy                     = $this->lexer->isNextToken(TokenType::T_INDEX) ? $this->IndexBy() : null;\n\n        $identificationVariable = $joinAssociationPathExpression->identificationVariable;\n        $field                  = $joinAssociationPathExpression->associationField;\n\n        $class       = $this->getMetadataForDqlAlias($identificationVariable);\n        $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]->targetEntity);\n\n        // Building queryComponent\n        $joinQueryComponent = [\n            'metadata'     => $targetClass,\n            'parent'       => $joinAssociationPathExpression->identificationVariable,\n            'relation'     => $class->getAssociationMapping($field),\n            'map'          => null,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $this->lexer->lookahead,\n        ];\n\n        $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;\n\n        return new AST\\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);\n    }\n\n    /**\n     * PartialObjectExpression ::= \"PARTIAL\" IdentificationVariable \".\" PartialFieldSet\n     * PartialFieldSet ::= \"{\" SimpleStateField {\",\" SimpleStateField}* \"}\"\n     */\n    public function PartialObjectExpression(): AST\\PartialObjectExpression\n    {\n        $this->match(TokenType::T_PARTIAL);\n\n        $partialFieldSet = [];\n\n        $identificationVariable = $this->IdentificationVariable();\n\n        $this->match(TokenType::T_DOT);\n        $this->match(TokenType::T_OPEN_CURLY_BRACE);\n        $this->match(TokenType::T_IDENTIFIER);\n\n        assert($this->lexer->token !== null);\n        $field = $this->lexer->token->value;\n\n        // First field in partial expression might be embeddable property\n        while ($this->lexer->isNextToken(TokenType::T_DOT)) {\n            $this->match(TokenType::T_DOT);\n            $this->match(TokenType::T_IDENTIFIER);\n            $field .= '.' . $this->lexer->token->value;\n        }\n\n        $partialFieldSet[] = $field;\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n            $this->match(TokenType::T_IDENTIFIER);\n\n            $field = $this->lexer->token->value;\n\n            while ($this->lexer->isNextToken(TokenType::T_DOT)) {\n                $this->match(TokenType::T_DOT);\n                $this->match(TokenType::T_IDENTIFIER);\n                $field .= '.' . $this->lexer->token->value;\n            }\n\n            $partialFieldSet[] = $field;\n        }\n\n        $this->match(TokenType::T_CLOSE_CURLY_BRACE);\n\n        $partialObjectExpression = new AST\\PartialObjectExpression($identificationVariable, $partialFieldSet);\n\n        // Defer PartialObjectExpression validation\n        $this->deferredPartialObjectExpressions[] = [\n            'expression'   => $partialObjectExpression,\n            'nestingLevel' => $this->nestingLevel,\n            'token'        => $this->lexer->token,\n        ];\n\n        return $partialObjectExpression;\n    }\n\n    /**\n     * NewObjectExpression ::= \"NEW\" AbstractSchemaName \"(\" NewObjectArg {\",\" NewObjectArg}* \")\"\n     */\n    public function NewObjectExpression(): AST\\NewObjectExpression\n    {\n        $useNamedArguments =  false;\n        $args              = [];\n        $argFieldAlias     = [];\n        $this->match(TokenType::T_NEW);\n\n        if ($this->lexer->isNextToken(TokenType::T_NAMED)) {\n            $this->match(TokenType::T_NAMED);\n            $useNamedArguments = true;\n        }\n\n        /** @var class-string $className */\n        $className = $this->AbstractSchemaName(); // note that this is not yet validated\n        $token     = $this->lexer->token;\n\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->addArgument($args, $useNamedArguments);\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n            $this->addArgument($args, $useNamedArguments);\n        }\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        $expression = new AST\\NewObjectExpression($className, $args);\n\n        // Defer NewObjectExpression validation\n        $this->deferredNewObjectExpressions[] = [\n            'token'        => $token,\n            'expression'   => $expression,\n            'nestingLevel' => $this->nestingLevel,\n        ];\n\n        return $expression;\n    }\n\n    /** @param array<mixed> $args */\n    public function addArgument(array &$args, bool $useNamedArguments): void\n    {\n        $fieldAlias = null;\n\n        if ($useNamedArguments) {\n            $startToken = $this->lexer->lookahead?->position ?? 0;\n\n            $newArg = $this->NewObjectArg($fieldAlias);\n\n            $key = $fieldAlias ?? $newArg->field ?? null;\n\n            if ($key === null) {\n                throw NoMatchingPropertyException::create(trim(substr(\n                    ($this->query->getDQL() ?? ''),\n                    $startToken,\n                    ($this->lexer->lookahead->position ?? 0) - $startToken,\n                )));\n            }\n\n            if (array_key_exists($key, $args)) {\n                throw DuplicateFieldException::create($key, trim(substr(\n                    ($this->query->getDQL() ?? ''),\n                    $startToken,\n                    ($this->lexer->lookahead->position ?? 0) - $startToken,\n                )));\n            }\n\n            $args[$key] = $newArg;\n        } else {\n            $args[] = $this->NewObjectArg($fieldAlias);\n        }\n    }\n\n    /**\n     * NewObjectArg ::= (ScalarExpression | \"(\" Subselect \")\" | NewObjectExpression) [\"AS\" AliasResultVariable]\n     */\n    public function NewObjectArg(string|null &$fieldAlias = null): mixed\n    {\n        $fieldAlias = null;\n\n        assert($this->lexer->lookahead !== null);\n        $token = $this->lexer->lookahead;\n        $peek  = $this->lexer->glimpse();\n\n        assert($peek !== null);\n\n        $expression = null;\n\n        if ($token->type === TokenType::T_OPEN_PARENTHESIS && $peek->type === TokenType::T_SELECT) {\n            $this->match(TokenType::T_OPEN_PARENTHESIS);\n            $expression = $this->Subselect();\n            $this->match(TokenType::T_CLOSE_PARENTHESIS);\n        } elseif ($token->type === TokenType::T_NEW) {\n            $expression = $this->NewObjectExpression();\n        } elseif ($token->type === TokenType::T_IDENTIFIER && $peek->type !== TokenType::T_DOT && $peek->type !== TokenType::T_OPEN_PARENTHESIS) {\n            $expression = $this->EntityAsDtoArgumentExpression();\n            $fieldAlias = $expression->aliasVariable;\n        } else {\n            $expression = $this->ScalarExpression();\n        }\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n            $this->match(TokenType::T_IDENTIFIER);\n\n            assert($this->lexer->token !== null);\n\n            $fieldAlias = $this->lexer->token->value;\n        }\n\n        return $expression;\n    }\n\n    /**\n     * IndexBy ::= \"INDEX\" \"BY\" SingleValuedPathExpression\n     */\n    public function IndexBy(): AST\\IndexBy\n    {\n        $this->match(TokenType::T_INDEX);\n        $this->match(TokenType::T_BY);\n        $pathExpr = $this->SingleValuedPathExpression();\n\n        // Add the INDEX BY info to the query component\n        $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;\n\n        return new AST\\IndexBy($pathExpr);\n    }\n\n    /**\n     * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |\n     *                      StateFieldPathExpression | BooleanPrimary | CaseExpression |\n     *                      InstanceOfExpression\n     *\n     * @return mixed One of the possible expressions or subexpressions.\n     */\n    public function ScalarExpression(): mixed\n    {\n        assert($this->lexer->token !== null);\n        assert($this->lexer->lookahead !== null);\n        $lookahead = $this->lexer->lookahead->type;\n        $peek      = $this->lexer->glimpse();\n\n        switch (true) {\n            case $lookahead === TokenType::T_INTEGER:\n            case $lookahead === TokenType::T_FLOAT:\n            // SimpleArithmeticExpression : (- u.value ) or ( + u.value )  or ( - 1 ) or ( + 1 )\n            case $lookahead === TokenType::T_MINUS:\n            case $lookahead === TokenType::T_PLUS:\n                return $this->SimpleArithmeticExpression();\n\n            case $lookahead === TokenType::T_STRING:\n                return $this->StringPrimary();\n\n            case $lookahead === TokenType::T_TRUE:\n            case $lookahead === TokenType::T_FALSE:\n                $this->match($lookahead);\n\n                return new AST\\Literal(AST\\Literal::BOOLEAN, $this->lexer->token->value);\n\n            case $lookahead === TokenType::T_INPUT_PARAMETER:\n                return match (true) {\n                    $this->isMathOperator($peek) => $this->SimpleArithmeticExpression(),\n                    default => $this->InputParameter(),\n                };\n\n            case $lookahead === TokenType::T_CASE:\n            case $lookahead === TokenType::T_COALESCE:\n            case $lookahead === TokenType::T_NULLIF:\n                // Since NULLIF and COALESCE can be identified as a function,\n                // we need to check these before checking for FunctionDeclaration\n                return $this->CaseExpression();\n\n            case $lookahead === TokenType::T_OPEN_PARENTHESIS:\n                return $this->SimpleArithmeticExpression();\n\n            // this check must be done before checking for a filed path expression\n            case $this->isFunction():\n                $this->lexer->peek();\n\n                return match (true) {\n                    $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->SimpleArithmeticExpression(),\n                    default => $this->FunctionDeclaration(),\n                };\n\n            // it is no function, so it must be a field path\n            case $lookahead === TokenType::T_IDENTIFIER:\n                $this->lexer->peek(); // lookahead => '.'\n                $this->lexer->peek(); // lookahead => token after '.'\n                $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'\n                $this->lexer->resetPeek();\n\n                if ($this->isMathOperator($peek)) {\n                    return $this->SimpleArithmeticExpression();\n                }\n\n                return $this->StateFieldPathExpression();\n\n            default:\n                $this->syntaxError();\n        }\n    }\n\n    /**\n     * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression\n     * GeneralCaseExpression ::= \"CASE\" WhenClause {WhenClause}* \"ELSE\" ScalarExpression \"END\"\n     * WhenClause ::= \"WHEN\" ConditionalExpression \"THEN\" ScalarExpression\n     * SimpleCaseExpression ::= \"CASE\" CaseOperand SimpleWhenClause {SimpleWhenClause}* \"ELSE\" ScalarExpression \"END\"\n     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator\n     * SimpleWhenClause ::= \"WHEN\" ScalarExpression \"THEN\" ScalarExpression\n     * CoalesceExpression ::= \"COALESCE\" \"(\" ScalarExpression {\",\" ScalarExpression}* \")\"\n     * NullifExpression ::= \"NULLIF\" \"(\" ScalarExpression \",\" ScalarExpression \")\"\n     *\n     * @return mixed One of the possible expressions or subexpressions.\n     */\n    public function CaseExpression(): mixed\n    {\n        assert($this->lexer->lookahead !== null);\n        $lookahead = $this->lexer->lookahead->type;\n\n        switch ($lookahead) {\n            case TokenType::T_NULLIF:\n                return $this->NullIfExpression();\n\n            case TokenType::T_COALESCE:\n                return $this->CoalesceExpression();\n\n            case TokenType::T_CASE:\n                $this->lexer->resetPeek();\n                $peek = $this->lexer->peek();\n\n                assert($peek !== null);\n                if ($peek->type === TokenType::T_WHEN) {\n                    return $this->GeneralCaseExpression();\n                }\n\n                return $this->SimpleCaseExpression();\n\n            default:\n                // Do nothing\n                break;\n        }\n\n        $this->syntaxError();\n    }\n\n    /**\n     * CoalesceExpression ::= \"COALESCE\" \"(\" ScalarExpression {\",\" ScalarExpression}* \")\"\n     */\n    public function CoalesceExpression(): AST\\CoalesceExpression\n    {\n        $this->match(TokenType::T_COALESCE);\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        // Process ScalarExpressions (1..N)\n        $scalarExpressions   = [];\n        $scalarExpressions[] = $this->ScalarExpression();\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $scalarExpressions[] = $this->ScalarExpression();\n        }\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return new AST\\CoalesceExpression($scalarExpressions);\n    }\n\n    /**\n     * NullIfExpression ::= \"NULLIF\" \"(\" ScalarExpression \",\" ScalarExpression \")\"\n     */\n    public function NullIfExpression(): AST\\NullIfExpression\n    {\n        $this->match(TokenType::T_NULLIF);\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $firstExpression = $this->ScalarExpression();\n        $this->match(TokenType::T_COMMA);\n        $secondExpression = $this->ScalarExpression();\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return new AST\\NullIfExpression($firstExpression, $secondExpression);\n    }\n\n    /**\n     * GeneralCaseExpression ::= \"CASE\" WhenClause {WhenClause}* \"ELSE\" ScalarExpression \"END\"\n     */\n    public function GeneralCaseExpression(): AST\\GeneralCaseExpression\n    {\n        $this->match(TokenType::T_CASE);\n\n        // Process WhenClause (1..N)\n        $whenClauses = [];\n\n        do {\n            $whenClauses[] = $this->WhenClause();\n        } while ($this->lexer->isNextToken(TokenType::T_WHEN));\n\n        $this->match(TokenType::T_ELSE);\n        $scalarExpression = $this->ScalarExpression();\n        $this->match(TokenType::T_END);\n\n        return new AST\\GeneralCaseExpression($whenClauses, $scalarExpression);\n    }\n\n    /**\n     * SimpleCaseExpression ::= \"CASE\" CaseOperand SimpleWhenClause {SimpleWhenClause}* \"ELSE\" ScalarExpression \"END\"\n     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator\n     */\n    public function SimpleCaseExpression(): AST\\SimpleCaseExpression\n    {\n        $this->match(TokenType::T_CASE);\n        $caseOperand = $this->StateFieldPathExpression();\n\n        // Process SimpleWhenClause (1..N)\n        $simpleWhenClauses = [];\n\n        do {\n            $simpleWhenClauses[] = $this->SimpleWhenClause();\n        } while ($this->lexer->isNextToken(TokenType::T_WHEN));\n\n        $this->match(TokenType::T_ELSE);\n        $scalarExpression = $this->ScalarExpression();\n        $this->match(TokenType::T_END);\n\n        return new AST\\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);\n    }\n\n    /**\n     * WhenClause ::= \"WHEN\" ConditionalExpression \"THEN\" ScalarExpression\n     */\n    public function WhenClause(): AST\\WhenClause\n    {\n        $this->match(TokenType::T_WHEN);\n        $conditionalExpression = $this->ConditionalExpression();\n        $this->match(TokenType::T_THEN);\n\n        return new AST\\WhenClause($conditionalExpression, $this->ScalarExpression());\n    }\n\n    /**\n     * SimpleWhenClause ::= \"WHEN\" ScalarExpression \"THEN\" ScalarExpression\n     */\n    public function SimpleWhenClause(): AST\\SimpleWhenClause\n    {\n        $this->match(TokenType::T_WHEN);\n        $conditionalExpression = $this->ScalarExpression();\n        $this->match(TokenType::T_THEN);\n\n        return new AST\\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());\n    }\n\n    /**\n     * SelectExpression ::= (\n     *     IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |\n     *     PartialObjectExpression | \"(\" Subselect \")\" | CaseExpression | NewObjectExpression\n     * ) [[\"AS\"] [\"HIDDEN\"] AliasResultVariable]\n     */\n    public function SelectExpression(): AST\\SelectExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        $expression    = null;\n        $identVariable = null;\n        $peek          = $this->lexer->glimpse();\n        $lookaheadType = $this->lexer->lookahead->type;\n        assert($peek !== null);\n\n        switch (true) {\n            // ScalarExpression (u.name)\n            case $lookaheadType === TokenType::T_IDENTIFIER && $peek->type === TokenType::T_DOT:\n                $expression = $this->ScalarExpression();\n                break;\n\n            // IdentificationVariable (u)\n            case $lookaheadType === TokenType::T_IDENTIFIER && $peek->type !== TokenType::T_OPEN_PARENTHESIS:\n                $expression = $identVariable = $this->IdentificationVariable();\n                break;\n\n            // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))\n            case $lookaheadType === TokenType::T_CASE:\n            case $lookaheadType === TokenType::T_COALESCE:\n            case $lookaheadType === TokenType::T_NULLIF:\n                $expression = $this->CaseExpression();\n                break;\n\n            // DQL Function (SUM(u.value) or SUM(u.value) + 1)\n            case $this->isFunction():\n                $this->lexer->peek(); // \"(\"\n\n                $expression = match (true) {\n                    $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->ScalarExpression(),\n                    default => $this->FunctionDeclaration(),\n                };\n\n                break;\n\n            // PartialObjectExpression (PARTIAL u.{id, name})\n            case $lookaheadType === TokenType::T_PARTIAL:\n                $expression    = $this->PartialObjectExpression();\n                $identVariable = $expression->identificationVariable;\n                break;\n\n            // Subselect\n            case $lookaheadType === TokenType::T_OPEN_PARENTHESIS && $peek->type === TokenType::T_SELECT:\n                $this->match(TokenType::T_OPEN_PARENTHESIS);\n                $expression = $this->Subselect();\n                $this->match(TokenType::T_CLOSE_PARENTHESIS);\n                break;\n\n            // Shortcut: ScalarExpression => SimpleArithmeticExpression\n            case $lookaheadType === TokenType::T_OPEN_PARENTHESIS:\n            case $lookaheadType === TokenType::T_INTEGER:\n            case $lookaheadType === TokenType::T_STRING:\n            case $lookaheadType === TokenType::T_FLOAT:\n            // SimpleArithmeticExpression : (- u.value ) or ( + u.value )\n            case $lookaheadType === TokenType::T_MINUS:\n            case $lookaheadType === TokenType::T_PLUS:\n                $expression = $this->SimpleArithmeticExpression();\n                break;\n\n            // NewObjectExpression (New ClassName(id, name))\n            case $lookaheadType === TokenType::T_NEW:\n                $expression = $this->NewObjectExpression();\n                break;\n\n            default:\n                $this->syntaxError(\n                    'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | \"(\" Subselect \")\" | CaseExpression',\n                    $this->lexer->lookahead,\n                );\n        }\n\n        // [[\"AS\"] [\"HIDDEN\"] AliasResultVariable]\n        $mustHaveAliasResultVariable = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n\n            $mustHaveAliasResultVariable = true;\n        }\n\n        $hiddenAliasResultVariable = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_HIDDEN)) {\n            $this->match(TokenType::T_HIDDEN);\n\n            $hiddenAliasResultVariable = true;\n        }\n\n        $aliasResultVariable = null;\n\n        if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(TokenType::T_IDENTIFIER)) {\n            assert($expression instanceof AST\\Node || is_string($expression));\n            $token               = $this->lexer->lookahead;\n            $aliasResultVariable = $this->AliasResultVariable();\n\n            // Include AliasResultVariable in query components.\n            $this->queryComponents[$aliasResultVariable] = [\n                'resultVariable' => $expression,\n                'nestingLevel'   => $this->nestingLevel,\n                'token'          => $token,\n            ];\n        }\n\n        // AST\n\n        $expr = new AST\\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);\n\n        if ($identVariable) {\n            $this->identVariableExpressions[$identVariable] = $expr;\n        }\n\n        return $expr;\n    }\n\n    /**\n     * SimpleSelectExpression ::= (\n     *      StateFieldPathExpression | IdentificationVariable | FunctionDeclaration |\n     *      AggregateExpression | \"(\" Subselect \")\" | ScalarExpression\n     * ) [[\"AS\"] AliasResultVariable]\n     */\n    public function SimpleSelectExpression(): AST\\SimpleSelectExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        $peek = $this->lexer->glimpse();\n        assert($peek !== null);\n\n        switch ($this->lexer->lookahead->type) {\n            case TokenType::T_IDENTIFIER:\n                switch (true) {\n                    case $peek->type === TokenType::T_DOT:\n                        $expression = $this->StateFieldPathExpression();\n\n                        return new AST\\SimpleSelectExpression($expression);\n\n                    case $peek->type !== TokenType::T_OPEN_PARENTHESIS:\n                        $expression = $this->IdentificationVariable();\n\n                        return new AST\\SimpleSelectExpression($expression);\n\n                    case $this->isFunction():\n                        // SUM(u.id) + COUNT(u.id)\n                        if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) {\n                            return new AST\\SimpleSelectExpression($this->ScalarExpression());\n                        }\n\n                        // COUNT(u.id)\n                        if ($this->isAggregateFunction($this->lexer->lookahead->type)) {\n                            return new AST\\SimpleSelectExpression($this->AggregateExpression());\n                        }\n\n                        // IDENTITY(u)\n                        return new AST\\SimpleSelectExpression($this->FunctionDeclaration());\n\n                    default:\n                        // Do nothing\n                }\n\n                break;\n\n            case TokenType::T_OPEN_PARENTHESIS:\n                if ($peek->type !== TokenType::T_SELECT) {\n                    // Shortcut: ScalarExpression => SimpleArithmeticExpression\n                    $expression = $this->SimpleArithmeticExpression();\n\n                    return new AST\\SimpleSelectExpression($expression);\n                }\n\n                // Subselect\n                $this->match(TokenType::T_OPEN_PARENTHESIS);\n                $expression = $this->Subselect();\n                $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n                return new AST\\SimpleSelectExpression($expression);\n\n            default:\n                // Do nothing\n        }\n\n        $this->lexer->peek();\n\n        $expression = $this->ScalarExpression();\n        $expr       = new AST\\SimpleSelectExpression($expression);\n\n        if ($this->lexer->isNextToken(TokenType::T_AS)) {\n            $this->match(TokenType::T_AS);\n        }\n\n        if ($this->lexer->isNextToken(TokenType::T_IDENTIFIER)) {\n            $token                             = $this->lexer->lookahead;\n            $resultVariable                    = $this->AliasResultVariable();\n            $expr->fieldIdentificationVariable = $resultVariable;\n\n            // Include AliasResultVariable in query components.\n            $this->queryComponents[$resultVariable] = [\n                'resultvariable' => $expr,\n                'nestingLevel'   => $this->nestingLevel,\n                'token'          => $token,\n            ];\n        }\n\n        return $expr;\n    }\n\n    /**\n     * ConditionalExpression ::= ConditionalTerm {\"OR\" ConditionalTerm}*\n     */\n    public function ConditionalExpression(): AST\\ConditionalExpression|AST\\ConditionalFactor|AST\\ConditionalPrimary|AST\\ConditionalTerm\n    {\n        $conditionalTerms   = [];\n        $conditionalTerms[] = $this->ConditionalTerm();\n\n        while ($this->lexer->isNextToken(TokenType::T_OR)) {\n            $this->match(TokenType::T_OR);\n\n            $conditionalTerms[] = $this->ConditionalTerm();\n        }\n\n        // Phase 1 AST optimization: Prevent AST\\ConditionalExpression\n        // if only one AST\\ConditionalTerm is defined\n        if (count($conditionalTerms) === 1) {\n            return $conditionalTerms[0];\n        }\n\n        return new AST\\ConditionalExpression($conditionalTerms);\n    }\n\n    /**\n     * ConditionalTerm ::= ConditionalFactor {\"AND\" ConditionalFactor}*\n     */\n    public function ConditionalTerm(): AST\\ConditionalFactor|AST\\ConditionalPrimary|AST\\ConditionalTerm\n    {\n        $conditionalFactors   = [];\n        $conditionalFactors[] = $this->ConditionalFactor();\n\n        while ($this->lexer->isNextToken(TokenType::T_AND)) {\n            $this->match(TokenType::T_AND);\n\n            $conditionalFactors[] = $this->ConditionalFactor();\n        }\n\n        // Phase 1 AST optimization: Prevent AST\\ConditionalTerm\n        // if only one AST\\ConditionalFactor is defined\n        if (count($conditionalFactors) === 1) {\n            return $conditionalFactors[0];\n        }\n\n        return new AST\\ConditionalTerm($conditionalFactors);\n    }\n\n    /**\n     * ConditionalFactor ::= [\"NOT\"] ConditionalPrimary\n     */\n    public function ConditionalFactor(): AST\\ConditionalFactor|AST\\ConditionalPrimary\n    {\n        $not = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n\n            $not = true;\n        }\n\n        $conditionalPrimary = $this->ConditionalPrimary();\n\n        // Phase 1 AST optimization: Prevent AST\\ConditionalFactor\n        // if only one AST\\ConditionalPrimary is defined\n        if (! $not) {\n            return $conditionalPrimary;\n        }\n\n        return new AST\\ConditionalFactor($conditionalPrimary, $not);\n    }\n\n    /**\n     * ConditionalPrimary ::= SimpleConditionalExpression | \"(\" ConditionalExpression \")\"\n     */\n    public function ConditionalPrimary(): AST\\ConditionalPrimary\n    {\n        $condPrimary = new AST\\ConditionalPrimary();\n\n        if (! $this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS)) {\n            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();\n\n            return $condPrimary;\n        }\n\n        // Peek beyond the matching closing parenthesis ')'\n        $peek = $this->peekBeyondClosingParenthesis();\n\n        if (\n            $peek !== null && (\n            in_array($peek->value, ['=', '<', '<=', '<>', '>', '>=', '!='], true) ||\n            in_array($peek->type, [TokenType::T_NOT, TokenType::T_BETWEEN, TokenType::T_LIKE, TokenType::T_IN, TokenType::T_IS, TokenType::T_EXISTS], true) ||\n            $this->isMathOperator($peek)\n            )\n        ) {\n            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();\n\n            return $condPrimary;\n        }\n\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n        $condPrimary->conditionalExpression = $this->ConditionalExpression();\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return $condPrimary;\n    }\n\n    /**\n     * SimpleConditionalExpression ::=\n     *      ComparisonExpression | BetweenExpression | LikeExpression |\n     *      InExpression | NullComparisonExpression | ExistsExpression |\n     *      EmptyCollectionComparisonExpression | CollectionMemberExpression |\n     *      InstanceOfExpression\n     */\n    public function SimpleConditionalExpression(): AST\\ExistsExpression|AST\\BetweenExpression|AST\\LikeExpression|AST\\InListExpression|AST\\InSubselectExpression|AST\\InstanceOfExpression|AST\\CollectionMemberExpression|AST\\NullComparisonExpression|AST\\EmptyCollectionComparisonExpression|AST\\ComparisonExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        if ($this->lexer->isNextToken(TokenType::T_EXISTS)) {\n            return $this->ExistsExpression();\n        }\n\n        $token     = $this->lexer->lookahead;\n        $peek      = $this->lexer->glimpse();\n        $lookahead = $token;\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $token = $this->lexer->glimpse();\n        }\n\n        assert($token !== null);\n        assert($peek !== null);\n        if ($token->type === TokenType::T_IDENTIFIER || $token->type === TokenType::T_INPUT_PARAMETER || $this->isFunction()) {\n            // Peek beyond the matching closing parenthesis.\n            $beyond = $this->lexer->peek();\n\n            switch ($peek->value) {\n                case '(':\n                    // Peeks beyond the matched closing parenthesis.\n                    $token = $this->peekBeyondClosingParenthesis(false);\n                    assert($token !== null);\n\n                    if ($token->type === TokenType::T_NOT) {\n                        $token = $this->lexer->peek();\n                        assert($token !== null);\n                    }\n\n                    if ($token->type === TokenType::T_IS) {\n                        $lookahead = $this->lexer->peek();\n                    }\n\n                    break;\n\n                default:\n                    // Peek beyond the PathExpression or InputParameter.\n                    $token = $beyond;\n\n                    while ($token->value === '.') {\n                        $this->lexer->peek();\n\n                        $token = $this->lexer->peek();\n                        assert($token !== null);\n                    }\n\n                    // Also peek beyond a NOT if there is one.\n                    assert($token !== null);\n                    if ($token->type === TokenType::T_NOT) {\n                        $token = $this->lexer->peek();\n                        assert($token !== null);\n                    }\n\n                    // We need to go even further in case of IS (differentiate between NULL and EMPTY)\n                    $lookahead = $this->lexer->peek();\n            }\n\n            assert($lookahead !== null);\n            // Also peek beyond a NOT if there is one.\n            if ($lookahead->type === TokenType::T_NOT) {\n                $lookahead = $this->lexer->peek();\n            }\n\n            $this->lexer->resetPeek();\n        }\n\n        if ($token->type === TokenType::T_BETWEEN) {\n            return $this->BetweenExpression();\n        }\n\n        if ($token->type === TokenType::T_LIKE) {\n            return $this->LikeExpression();\n        }\n\n        if ($token->type === TokenType::T_IN) {\n            return $this->InExpression();\n        }\n\n        if ($token->type === TokenType::T_INSTANCE) {\n            return $this->InstanceOfExpression();\n        }\n\n        if ($token->type === TokenType::T_MEMBER) {\n            return $this->CollectionMemberExpression();\n        }\n\n        assert($lookahead !== null);\n        if ($token->type === TokenType::T_IS && $lookahead->type === TokenType::T_NULL) {\n            return $this->NullComparisonExpression();\n        }\n\n        if ($token->type === TokenType::T_IS && $lookahead->type === TokenType::T_EMPTY) {\n            return $this->EmptyCollectionComparisonExpression();\n        }\n\n        return $this->ComparisonExpression();\n    }\n\n    /**\n     * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression \"IS\" [\"NOT\"] \"EMPTY\"\n     */\n    public function EmptyCollectionComparisonExpression(): AST\\EmptyCollectionComparisonExpression\n    {\n        $pathExpression = $this->CollectionValuedPathExpression();\n        $this->match(TokenType::T_IS);\n\n        $not = false;\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_EMPTY);\n\n        return new AST\\EmptyCollectionComparisonExpression(\n            $pathExpression,\n            $not,\n        );\n    }\n\n    /**\n     * CollectionMemberExpression ::= EntityExpression [\"NOT\"] \"MEMBER\" [\"OF\"] CollectionValuedPathExpression\n     *\n     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression\n     * SimpleEntityExpression ::= IdentificationVariable | InputParameter\n     */\n    public function CollectionMemberExpression(): AST\\CollectionMemberExpression\n    {\n        $not        = false;\n        $entityExpr = $this->EntityExpression();\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n\n            $not = true;\n        }\n\n        $this->match(TokenType::T_MEMBER);\n\n        if ($this->lexer->isNextToken(TokenType::T_OF)) {\n            $this->match(TokenType::T_OF);\n        }\n\n        return new AST\\CollectionMemberExpression(\n            $entityExpr,\n            $this->CollectionValuedPathExpression(),\n            $not,\n        );\n    }\n\n    /**\n     * Literal ::= string | char | integer | float | boolean\n     */\n    public function Literal(): AST\\Literal\n    {\n        assert($this->lexer->lookahead !== null);\n        assert($this->lexer->token !== null);\n        switch ($this->lexer->lookahead->type) {\n            case TokenType::T_STRING:\n                $this->match(TokenType::T_STRING);\n\n                return new AST\\Literal(AST\\Literal::STRING, $this->lexer->token->value);\n\n            case TokenType::T_INTEGER:\n            case TokenType::T_FLOAT:\n                $this->match(\n                    $this->lexer->isNextToken(TokenType::T_INTEGER) ? TokenType::T_INTEGER : TokenType::T_FLOAT,\n                );\n\n                return new AST\\Literal(AST\\Literal::NUMERIC, $this->lexer->token->value);\n\n            case TokenType::T_TRUE:\n            case TokenType::T_FALSE:\n                $this->match(\n                    $this->lexer->isNextToken(TokenType::T_TRUE) ? TokenType::T_TRUE : TokenType::T_FALSE,\n                );\n\n                return new AST\\Literal(AST\\Literal::BOOLEAN, $this->lexer->token->value);\n\n            default:\n                $this->syntaxError('Literal');\n        }\n    }\n\n    /**\n     * InParameter ::= ArithmeticExpression | InputParameter\n     */\n    public function InParameter(): AST\\InputParameter|AST\\ArithmeticExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        if ($this->lexer->lookahead->type === TokenType::T_INPUT_PARAMETER) {\n            return $this->InputParameter();\n        }\n\n        return $this->ArithmeticExpression();\n    }\n\n    /**\n     * InputParameter ::= PositionalParameter | NamedParameter\n     */\n    public function InputParameter(): AST\\InputParameter\n    {\n        $this->match(TokenType::T_INPUT_PARAMETER);\n        assert($this->lexer->token !== null);\n\n        return new AST\\InputParameter($this->lexer->token->value);\n    }\n\n    /**\n     * ArithmeticExpression ::= SimpleArithmeticExpression | \"(\" Subselect \")\"\n     */\n    public function ArithmeticExpression(): AST\\ArithmeticExpression\n    {\n        $expr = new AST\\ArithmeticExpression();\n\n        if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS)) {\n            $peek = $this->lexer->glimpse();\n            assert($peek !== null);\n\n            if ($peek->type === TokenType::T_SELECT) {\n                $this->match(TokenType::T_OPEN_PARENTHESIS);\n                $expr->subselect = $this->Subselect();\n                $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n                return $expr;\n            }\n        }\n\n        $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();\n\n        return $expr;\n    }\n\n    /**\n     * SimpleArithmeticExpression ::= ArithmeticTerm {(\"+\" | \"-\") ArithmeticTerm}*\n     */\n    public function SimpleArithmeticExpression(): AST\\Node|string\n    {\n        $terms   = [];\n        $terms[] = $this->ArithmeticTerm();\n\n        while (($isPlus = $this->lexer->isNextToken(TokenType::T_PLUS)) || $this->lexer->isNextToken(TokenType::T_MINUS)) {\n            $this->match($isPlus ? TokenType::T_PLUS : TokenType::T_MINUS);\n\n            assert($this->lexer->token !== null);\n            $terms[] = $this->lexer->token->value;\n            $terms[] = $this->ArithmeticTerm();\n        }\n\n        // Phase 1 AST optimization: Prevent AST\\SimpleArithmeticExpression\n        // if only one AST\\ArithmeticTerm is defined\n        if (count($terms) === 1) {\n            return $terms[0];\n        }\n\n        return new AST\\SimpleArithmeticExpression($terms);\n    }\n\n    /**\n     * ArithmeticTerm ::= ArithmeticFactor {(\"*\" | \"/\") ArithmeticFactor}*\n     */\n    public function ArithmeticTerm(): AST\\Node|string\n    {\n        $factors   = [];\n        $factors[] = $this->ArithmeticFactor();\n\n        while (($isMult = $this->lexer->isNextToken(TokenType::T_MULTIPLY)) || $this->lexer->isNextToken(TokenType::T_DIVIDE)) {\n            $this->match($isMult ? TokenType::T_MULTIPLY : TokenType::T_DIVIDE);\n\n            assert($this->lexer->token !== null);\n            $factors[] = $this->lexer->token->value;\n            $factors[] = $this->ArithmeticFactor();\n        }\n\n        // Phase 1 AST optimization: Prevent AST\\ArithmeticTerm\n        // if only one AST\\ArithmeticFactor is defined\n        if (count($factors) === 1) {\n            return $factors[0];\n        }\n\n        return new AST\\ArithmeticTerm($factors);\n    }\n\n    /**\n     * ArithmeticFactor ::= [(\"+\" | \"-\")] ArithmeticPrimary\n     */\n    public function ArithmeticFactor(): AST\\Node|string|AST\\ArithmeticFactor\n    {\n        $sign = null;\n\n        $isPlus = $this->lexer->isNextToken(TokenType::T_PLUS);\n        if ($isPlus || $this->lexer->isNextToken(TokenType::T_MINUS)) {\n            $this->match($isPlus ? TokenType::T_PLUS : TokenType::T_MINUS);\n            $sign = $isPlus;\n        }\n\n        $primary = $this->ArithmeticPrimary();\n\n        // Phase 1 AST optimization: Prevent AST\\ArithmeticFactor\n        // if only one AST\\ArithmeticPrimary is defined\n        if ($sign === null) {\n            return $primary;\n        }\n\n        return new AST\\ArithmeticFactor($primary, $sign);\n    }\n\n    /**\n     * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression\n     *          | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings\n     *          | FunctionsReturningDatetime | IdentificationVariable | ResultVariable\n     *          | InputParameter | CaseExpression\n     */\n    public function ArithmeticPrimary(): AST\\Node|string\n    {\n        if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS)) {\n            $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n            $expr = $this->SimpleArithmeticExpression();\n\n            $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n            return new AST\\ParenthesisExpression($expr);\n        }\n\n        if ($this->lexer->lookahead === null) {\n            $this->syntaxError('ArithmeticPrimary');\n        }\n\n        switch ($this->lexer->lookahead->type) {\n            case TokenType::T_COALESCE:\n            case TokenType::T_NULLIF:\n            case TokenType::T_CASE:\n                return $this->CaseExpression();\n\n            case TokenType::T_IDENTIFIER:\n                $peek = $this->lexer->glimpse();\n\n                if ($peek !== null && $peek->value === '(') {\n                    return $this->FunctionDeclaration();\n                }\n\n                if ($peek !== null && $peek->value === '.') {\n                    return $this->SingleValuedPathExpression();\n                }\n\n                if (isset($this->queryComponents[$this->lexer->lookahead->value]['resultVariable'])) {\n                    return $this->ResultVariable();\n                }\n\n                return $this->StateFieldPathExpression();\n\n            case TokenType::T_INPUT_PARAMETER:\n                return $this->InputParameter();\n\n            default:\n                $peek = $this->lexer->glimpse();\n\n                if ($peek !== null && $peek->value === '(') {\n                    return $this->FunctionDeclaration();\n                }\n\n                return $this->Literal();\n        }\n    }\n\n    /**\n     * StringExpression ::= StringPrimary | ResultVariable | \"(\" Subselect \")\"\n     */\n    public function StringExpression(): AST\\Subselect|AST\\Node|string\n    {\n        $peek = $this->lexer->glimpse();\n        assert($peek !== null);\n\n        // Subselect\n        if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS) && $peek->type === TokenType::T_SELECT) {\n            $this->match(TokenType::T_OPEN_PARENTHESIS);\n            $expr = $this->Subselect();\n            $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n            return $expr;\n        }\n\n        assert($this->lexer->lookahead !== null);\n        // ResultVariable (string)\n        if (\n            $this->lexer->isNextToken(TokenType::T_IDENTIFIER) &&\n            isset($this->queryComponents[$this->lexer->lookahead->value]['resultVariable'])\n        ) {\n            return $this->ResultVariable();\n        }\n\n        return $this->StringPrimary();\n    }\n\n    /**\n     * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression\n     */\n    public function StringPrimary(): AST\\Node\n    {\n        assert($this->lexer->lookahead !== null);\n        $lookaheadType = $this->lexer->lookahead->type;\n\n        switch ($lookaheadType) {\n            case TokenType::T_IDENTIFIER:\n                $peek = $this->lexer->glimpse();\n                assert($peek !== null);\n\n                if ($peek->value === '.') {\n                    return $this->StateFieldPathExpression();\n                }\n\n                if ($peek->value === '(') {\n                    // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions.\n                    return $this->FunctionDeclaration();\n                }\n\n                $this->syntaxError(\"'.' or '('\");\n                break;\n\n            case TokenType::T_STRING:\n                $this->match(TokenType::T_STRING);\n                assert($this->lexer->token !== null);\n\n                return new AST\\Literal(AST\\Literal::STRING, $this->lexer->token->value);\n\n            case TokenType::T_INPUT_PARAMETER:\n                return $this->InputParameter();\n\n            case TokenType::T_CASE:\n            case TokenType::T_COALESCE:\n            case TokenType::T_NULLIF:\n                return $this->CaseExpression();\n\n            default:\n                assert($lookaheadType !== null);\n                if ($this->isAggregateFunction($lookaheadType)) {\n                    return $this->AggregateExpression();\n                }\n        }\n\n        $this->syntaxError(\n            'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression',\n        );\n    }\n\n    /**\n     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression\n     */\n    public function EntityExpression(): AST\\InputParameter|AST\\PathExpression\n    {\n        $glimpse = $this->lexer->glimpse();\n        assert($glimpse !== null);\n\n        if ($this->lexer->isNextToken(TokenType::T_IDENTIFIER) && $glimpse->value === '.') {\n            return $this->SingleValuedAssociationPathExpression();\n        }\n\n        return $this->SimpleEntityExpression();\n    }\n\n    /**\n     * SimpleEntityExpression ::= IdentificationVariable | InputParameter\n     */\n    public function SimpleEntityExpression(): AST\\InputParameter|AST\\PathExpression\n    {\n        if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) {\n            return $this->InputParameter();\n        }\n\n        return $this->StateFieldPathExpression();\n    }\n\n    /**\n     * AggregateExpression ::=\n     *  (\"AVG\" | \"MAX\" | \"MIN\" | \"SUM\" | \"COUNT\") \"(\" [\"DISTINCT\"] SimpleArithmeticExpression \")\"\n     */\n    public function AggregateExpression(): AST\\AggregateExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        $lookaheadType = $this->lexer->lookahead->type;\n        $isDistinct    = false;\n\n        if (! in_array($lookaheadType, [TokenType::T_COUNT, TokenType::T_AVG, TokenType::T_MAX, TokenType::T_MIN, TokenType::T_SUM], true)) {\n            $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');\n        }\n\n        $this->match($lookaheadType);\n        assert($this->lexer->token !== null);\n        $functionName = $this->lexer->token->value;\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        if ($this->lexer->isNextToken(TokenType::T_DISTINCT)) {\n            $this->match(TokenType::T_DISTINCT);\n            $isDistinct = true;\n        }\n\n        $pathExp = $this->SimpleArithmeticExpression();\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return new AST\\AggregateExpression($functionName, $pathExp, $isDistinct);\n    }\n\n    /**\n     * QuantifiedExpression ::= (\"ALL\" | \"ANY\" | \"SOME\") \"(\" Subselect \")\"\n     */\n    public function QuantifiedExpression(): AST\\QuantifiedExpression\n    {\n        assert($this->lexer->lookahead !== null);\n        $lookaheadType = $this->lexer->lookahead->type;\n        $value         = $this->lexer->lookahead->value;\n\n        if (! in_array($lookaheadType, [TokenType::T_ALL, TokenType::T_ANY, TokenType::T_SOME], true)) {\n            $this->syntaxError('ALL, ANY or SOME');\n        }\n\n        $this->match($lookaheadType);\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $qExpr       = new AST\\QuantifiedExpression($this->Subselect());\n        $qExpr->type = $value;\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return $qExpr;\n    }\n\n    /**\n     * BetweenExpression ::= ArithmeticExpression [\"NOT\"] \"BETWEEN\" ArithmeticExpression \"AND\" ArithmeticExpression\n     */\n    public function BetweenExpression(): AST\\BetweenExpression\n    {\n        $not        = false;\n        $arithExpr1 = $this->ArithmeticExpression();\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_BETWEEN);\n        $arithExpr2 = $this->ArithmeticExpression();\n        $this->match(TokenType::T_AND);\n        $arithExpr3 = $this->ArithmeticExpression();\n\n        return new AST\\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3, $not);\n    }\n\n    /**\n     * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )\n     */\n    public function ComparisonExpression(): AST\\ComparisonExpression\n    {\n        $this->lexer->glimpse();\n\n        $leftExpr  = $this->ArithmeticExpression();\n        $operator  = $this->ComparisonOperator();\n        $rightExpr = $this->isNextAllAnySome()\n            ? $this->QuantifiedExpression()\n            : $this->ArithmeticExpression();\n\n        return new AST\\ComparisonExpression($leftExpr, $operator, $rightExpr);\n    }\n\n    /**\n     * InExpression ::= SingleValuedPathExpression [\"NOT\"] \"IN\" \"(\" (InParameter {\",\" InParameter}* | Subselect) \")\"\n     */\n    public function InExpression(): AST\\InListExpression|AST\\InSubselectExpression\n    {\n        $expression = $this->ArithmeticExpression();\n\n        $not = false;\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_IN);\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        if ($this->lexer->isNextToken(TokenType::T_SELECT)) {\n            $inExpression = new AST\\InSubselectExpression(\n                $expression,\n                $this->Subselect(),\n                $not,\n            );\n        } else {\n            $literals = [$this->InParameter()];\n\n            while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n                $this->match(TokenType::T_COMMA);\n                $literals[] = $this->InParameter();\n            }\n\n            $inExpression = new AST\\InListExpression(\n                $expression,\n                $literals,\n                $not,\n            );\n        }\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return $inExpression;\n    }\n\n    /**\n     * InstanceOfExpression ::= IdentificationVariable [\"NOT\"] \"INSTANCE\" [\"OF\"] (InstanceOfParameter | \"(\" InstanceOfParameter {\",\" InstanceOfParameter}* \")\")\n     */\n    public function InstanceOfExpression(): AST\\InstanceOfExpression\n    {\n        $identificationVariable = $this->IdentificationVariable();\n\n        $not = false;\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_INSTANCE);\n        $this->match(TokenType::T_OF);\n\n        $exprValues = $this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS)\n            ? $this->InstanceOfParameterList()\n            : [$this->InstanceOfParameter()];\n\n        return new AST\\InstanceOfExpression(\n            $identificationVariable,\n            $exprValues,\n            $not,\n        );\n    }\n\n    /** @return non-empty-list<AST\\InputParameter|string> */\n    public function InstanceOfParameterList(): array\n    {\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $exprValues = [$this->InstanceOfParameter()];\n\n        while ($this->lexer->isNextToken(TokenType::T_COMMA)) {\n            $this->match(TokenType::T_COMMA);\n\n            $exprValues[] = $this->InstanceOfParameter();\n        }\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return $exprValues;\n    }\n\n    /**\n     * InstanceOfParameter ::= AbstractSchemaName | InputParameter\n     */\n    public function InstanceOfParameter(): AST\\InputParameter|string\n    {\n        if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) {\n            $this->match(TokenType::T_INPUT_PARAMETER);\n            assert($this->lexer->token !== null);\n\n            return new AST\\InputParameter($this->lexer->token->value);\n        }\n\n        $abstractSchemaName = $this->AbstractSchemaName();\n\n        $this->validateAbstractSchemaName($abstractSchemaName);\n\n        return $abstractSchemaName;\n    }\n\n    /**\n     * LikeExpression ::= StringExpression [\"NOT\"] \"LIKE\" StringPrimary [\"ESCAPE\" char]\n     */\n    public function LikeExpression(): AST\\LikeExpression\n    {\n        $stringExpr = $this->StringExpression();\n        $not        = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_LIKE);\n\n        if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) {\n            $this->match(TokenType::T_INPUT_PARAMETER);\n            assert($this->lexer->token !== null);\n            $stringPattern = new AST\\InputParameter($this->lexer->token->value);\n        } else {\n            $stringPattern = $this->StringPrimary();\n        }\n\n        $escapeChar = null;\n\n        if ($this->lexer->lookahead !== null && $this->lexer->lookahead->type === TokenType::T_ESCAPE) {\n            $this->match(TokenType::T_ESCAPE);\n            $this->match(TokenType::T_STRING);\n            assert($this->lexer->token !== null);\n\n            $escapeChar = new AST\\Literal(AST\\Literal::STRING, $this->lexer->token->value);\n        }\n\n        return new AST\\LikeExpression($stringExpr, $stringPattern, $escapeChar, $not);\n    }\n\n    /**\n     * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) \"IS\" [\"NOT\"] \"NULL\"\n     */\n    public function NullComparisonExpression(): AST\\NullComparisonExpression\n    {\n        switch (true) {\n            case $this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER):\n                $this->match(TokenType::T_INPUT_PARAMETER);\n                assert($this->lexer->token !== null);\n\n                $expr = new AST\\InputParameter($this->lexer->token->value);\n                break;\n\n            case $this->lexer->isNextToken(TokenType::T_NULLIF):\n                $expr = $this->NullIfExpression();\n                break;\n\n            case $this->lexer->isNextToken(TokenType::T_COALESCE):\n                $expr = $this->CoalesceExpression();\n                break;\n\n            case $this->isFunction():\n                $expr = $this->FunctionDeclaration();\n                break;\n\n            default:\n                // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression\n                $glimpse = $this->lexer->glimpse();\n                assert($glimpse !== null);\n\n                if ($glimpse->type === TokenType::T_DOT) {\n                    $expr = $this->SingleValuedPathExpression();\n\n                    // Leave switch statement\n                    break;\n                }\n\n                assert($this->lexer->lookahead !== null);\n                $lookaheadValue = $this->lexer->lookahead->value;\n\n                // Validate existing component\n                if (! isset($this->queryComponents[$lookaheadValue])) {\n                    $this->semanticalError('Cannot add having condition on undefined result variable.');\n                }\n\n                // Validate SingleValuedPathExpression (ie.: \"product\")\n                if (isset($this->queryComponents[$lookaheadValue]['metadata'])) {\n                    $expr = $this->SingleValuedPathExpression();\n                    break;\n                }\n\n                // Validating ResultVariable\n                if (! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) {\n                    $this->semanticalError('Cannot add having condition on a non result variable.');\n                }\n\n                $expr = $this->ResultVariable();\n                break;\n        }\n\n        $this->match(TokenType::T_IS);\n\n        $not = false;\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n\n            $not = true;\n        }\n\n        $this->match(TokenType::T_NULL);\n\n        return new AST\\NullComparisonExpression($expr, $not);\n    }\n\n    /**\n     * ExistsExpression ::= [\"NOT\"] \"EXISTS\" \"(\" Subselect \")\"\n     */\n    public function ExistsExpression(): AST\\ExistsExpression\n    {\n        $not = false;\n\n        if ($this->lexer->isNextToken(TokenType::T_NOT)) {\n            $this->match(TokenType::T_NOT);\n            $not = true;\n        }\n\n        $this->match(TokenType::T_EXISTS);\n        $this->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $subselect = $this->Subselect();\n\n        $this->match(TokenType::T_CLOSE_PARENTHESIS);\n\n        return new AST\\ExistsExpression($subselect, $not);\n    }\n\n    /**\n     * ComparisonOperator ::= \"=\" | \"<\" | \"<=\" | \"<>\" | \">\" | \">=\" | \"!=\"\n     */\n    public function ComparisonOperator(): string\n    {\n        assert($this->lexer->lookahead !== null);\n        switch ($this->lexer->lookahead->value) {\n            case '=':\n                $this->match(TokenType::T_EQUALS);\n\n                return '=';\n\n            case '<':\n                $this->match(TokenType::T_LOWER_THAN);\n                $operator = '<';\n\n                if ($this->lexer->isNextToken(TokenType::T_EQUALS)) {\n                    $this->match(TokenType::T_EQUALS);\n                    $operator .= '=';\n                } elseif ($this->lexer->isNextToken(TokenType::T_GREATER_THAN)) {\n                    $this->match(TokenType::T_GREATER_THAN);\n                    $operator .= '>';\n                }\n\n                return $operator;\n\n            case '>':\n                $this->match(TokenType::T_GREATER_THAN);\n                $operator = '>';\n\n                if ($this->lexer->isNextToken(TokenType::T_EQUALS)) {\n                    $this->match(TokenType::T_EQUALS);\n                    $operator .= '=';\n                }\n\n                return $operator;\n\n            case '!':\n                $this->match(TokenType::T_NEGATE);\n                $this->match(TokenType::T_EQUALS);\n\n                return '<>';\n\n            default:\n                $this->syntaxError('=, <, <=, <>, >, >=, !=');\n        }\n    }\n\n    /**\n     * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime\n     */\n    public function FunctionDeclaration(): Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        $token    = $this->lexer->lookahead;\n        $funcName = strtolower($token->value);\n\n        $customFunctionDeclaration = $this->CustomFunctionDeclaration();\n\n        // Check for custom functions functions first!\n        switch (true) {\n            case $customFunctionDeclaration !== null:\n                return $customFunctionDeclaration;\n\n            case isset(self::$stringFunctions[$funcName]):\n                return $this->FunctionsReturningStrings();\n\n            case isset(self::$numericFunctions[$funcName]):\n                return $this->FunctionsReturningNumerics();\n\n            case isset(self::$datetimeFunctions[$funcName]):\n                return $this->FunctionsReturningDatetime();\n\n            default:\n                $this->syntaxError('known function', $token);\n        }\n    }\n\n    /**\n     * Helper function for FunctionDeclaration grammar rule.\n     */\n    private function CustomFunctionDeclaration(): Functions\\FunctionNode|null\n    {\n        assert($this->lexer->lookahead !== null);\n        $token    = $this->lexer->lookahead;\n        $funcName = strtolower($token->value);\n\n        // Check for custom functions afterwards\n        $config = $this->em->getConfiguration();\n\n        return match (true) {\n            $config->getCustomStringFunction($funcName) !== null => $this->CustomFunctionsReturningStrings(),\n            $config->getCustomNumericFunction($funcName) !== null => $this->CustomFunctionsReturningNumerics(),\n            $config->getCustomDatetimeFunction($funcName) !== null => $this->CustomFunctionsReturningDatetime(),\n            default => null,\n        };\n    }\n\n    /**\n     * FunctionsReturningNumerics ::=\n     *      \"LENGTH\" \"(\" StringPrimary \")\" |\n     *      \"LOCATE\" \"(\" StringPrimary \",\" StringPrimary [\",\" SimpleArithmeticExpression]\")\" |\n     *      \"ABS\" \"(\" SimpleArithmeticExpression \")\" |\n     *      \"SQRT\" \"(\" SimpleArithmeticExpression \")\" |\n     *      \"MOD\" \"(\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\" |\n     *      \"SIZE\" \"(\" CollectionValuedPathExpression \")\" |\n     *      \"DATE_DIFF\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\" |\n     *      \"BIT_AND\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\" |\n     *      \"BIT_OR\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \")\"\n     */\n    public function FunctionsReturningNumerics(): AST\\Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        $funcNameLower = strtolower($this->lexer->lookahead->value);\n        $funcClass     = self::$numericFunctions[$funcNameLower];\n\n        $function = new $funcClass($funcNameLower);\n        $function->parse($this);\n\n        return $function;\n    }\n\n    public function CustomFunctionsReturningNumerics(): AST\\Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        // getCustomNumericFunction is case-insensitive\n        $functionName  = strtolower($this->lexer->lookahead->value);\n        $functionClass = $this->em->getConfiguration()->getCustomNumericFunction($functionName);\n\n        assert($functionClass !== null);\n\n        if (is_string($functionClass)) {\n            $function = new $functionClass($functionName);\n            assert($function instanceof AST\\Functions\\FunctionNode);\n        } else {\n            $function = $functionClass($functionName);\n        }\n\n        $function->parse($this);\n\n        return $function;\n    }\n\n    /**\n     * FunctionsReturningDateTime ::=\n     *     \"CURRENT_DATE\" |\n     *     \"CURRENT_TIME\" |\n     *     \"CURRENT_TIMESTAMP\" |\n     *     \"DATE_ADD\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \",\" StringPrimary \")\" |\n     *     \"DATE_SUB\" \"(\" ArithmeticPrimary \",\" ArithmeticPrimary \",\" StringPrimary \")\"\n     */\n    public function FunctionsReturningDatetime(): AST\\Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        $funcNameLower = strtolower($this->lexer->lookahead->value);\n        $funcClass     = self::$datetimeFunctions[$funcNameLower];\n\n        $function = new $funcClass($funcNameLower);\n        $function->parse($this);\n\n        return $function;\n    }\n\n    public function CustomFunctionsReturningDatetime(): AST\\Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        // getCustomDatetimeFunction is case-insensitive\n        $functionName  = $this->lexer->lookahead->value;\n        $functionClass = $this->em->getConfiguration()->getCustomDatetimeFunction($functionName);\n\n        assert($functionClass !== null);\n\n        $function = is_string($functionClass)\n            ? new $functionClass($functionName)\n            : $functionClass($functionName);\n\n        $function->parse($this);\n\n        return $function;\n    }\n\n    /**\n     * FunctionsReturningStrings ::=\n     *   \"CONCAT\" \"(\" StringPrimary \",\" StringPrimary {\",\" StringPrimary}* \")\" |\n     *   \"SUBSTRING\" \"(\" StringPrimary \",\" SimpleArithmeticExpression \",\" SimpleArithmeticExpression \")\" |\n     *   \"TRIM\" \"(\" [[\"LEADING\" | \"TRAILING\" | \"BOTH\"] [char] \"FROM\"] StringPrimary \")\" |\n     *   \"LOWER\" \"(\" StringPrimary \")\" |\n     *   \"UPPER\" \"(\" StringPrimary \")\" |\n     *   \"IDENTITY\" \"(\" SingleValuedAssociationPathExpression {\",\" string} \")\"\n     */\n    public function FunctionsReturningStrings(): AST\\Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        $funcNameLower = strtolower($this->lexer->lookahead->value);\n        $funcClass     = self::$stringFunctions[$funcNameLower];\n\n        $function = new $funcClass($funcNameLower);\n        $function->parse($this);\n\n        return $function;\n    }\n\n    public function CustomFunctionsReturningStrings(): Functions\\FunctionNode\n    {\n        assert($this->lexer->lookahead !== null);\n        // getCustomStringFunction is case-insensitive\n        $functionName  = $this->lexer->lookahead->value;\n        $functionClass = $this->em->getConfiguration()->getCustomStringFunction($functionName);\n\n        assert($functionClass !== null);\n\n        $function = is_string($functionClass)\n            ? new $functionClass($functionName)\n            : $functionClass($functionName);\n\n        $function->parse($this);\n\n        return $function;\n    }\n\n    private function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata\n    {\n        if (! isset($this->queryComponents[$dqlAlias]['metadata'])) {\n            throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias));\n        }\n\n        return $this->queryComponents[$dqlAlias]['metadata'];\n    }\n}\n"
  },
  {
    "path": "src/Query/ParserResult.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse LogicException;\n\nuse function sprintf;\n\n/**\n * Encapsulates the resulting components from a DQL query parsing process that\n * can be serialized.\n *\n * @link        http://www.doctrine-project.org\n */\nclass ParserResult\n{\n    /**\n     * The SQL executor used for executing the SQL.\n     */\n    private AbstractSqlExecutor|null $sqlExecutor = null;\n\n    /**\n     * The SQL executor used for executing the SQL.\n     */\n    private SqlFinalizer|null $sqlFinalizer = null;\n\n    /**\n     * The ResultSetMapping that describes how to map the SQL result set.\n     */\n    private ResultSetMapping $resultSetMapping;\n\n    /**\n     * The mappings of DQL parameter names/positions to SQL parameter positions.\n     *\n     * @phpstan-var array<string|int, list<int>>\n     */\n    private array $parameterMappings = [];\n\n    /**\n     * Initializes a new instance of the <tt>ParserResult</tt> class.\n     * The new instance is initialized with an empty <tt>ResultSetMapping</tt>.\n     */\n    public function __construct()\n    {\n        $this->resultSetMapping = new ResultSetMapping();\n    }\n\n    /**\n     * Gets the ResultSetMapping for the parsed query.\n     *\n     * @return ResultSetMapping The result set mapping of the parsed query\n     */\n    public function getResultSetMapping(): ResultSetMapping\n    {\n        return $this->resultSetMapping;\n    }\n\n    /**\n     * Sets the ResultSetMapping of the parsed query.\n     */\n    public function setResultSetMapping(ResultSetMapping $rsm): void\n    {\n        $this->resultSetMapping = $rsm;\n    }\n\n    /**\n     * Sets the SQL executor that should be used for this ParserResult.\n     *\n     * @deprecated The SqlExecutor will be removed from ParserResult in 4.0. Provide a SqlFinalizer instead that can create the executor.\n     */\n    public function setSqlExecutor(AbstractSqlExecutor $executor): void\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11188',\n            'The SqlExecutor will be removed from %s in 4.0. Provide a %s instead that can create the executor.',\n            self::class,\n            SqlFinalizer::class,\n        );\n\n        $this->sqlExecutor = $executor;\n    }\n\n    /**\n     * Gets the SQL executor used by this ParserResult.\n     *\n     * @deprecated The SqlExecutor will be removed from ParserResult in 4.0. Provide a SqlFinalizer instead that can create the executor.\n     */\n    public function getSqlExecutor(): AbstractSqlExecutor\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11188',\n            'The SqlExecutor will be removed from %s in 4.0. Provide a %s instead that can create the executor.',\n            self::class,\n            SqlFinalizer::class,\n        );\n\n        if ($this->sqlExecutor === null) {\n            throw new LogicException(sprintf(\n                'Executor not set yet. Call %s::setSqlExecutor() first.',\n                self::class,\n            ));\n        }\n\n        return $this->sqlExecutor;\n    }\n\n    public function setSqlFinalizer(SqlFinalizer $finalizer): void\n    {\n        $this->sqlFinalizer = $finalizer;\n    }\n\n    public function prepareSqlExecutor(Query $query): AbstractSqlExecutor\n    {\n        if ($this->sqlFinalizer !== null) {\n            return $this->sqlFinalizer->createExecutor($query);\n        }\n\n        if ($this->sqlExecutor !== null) {\n            return $this->sqlExecutor;\n        }\n\n        throw new LogicException('This ParserResult lacks both the SqlFinalizer as well as the (legacy) SqlExecutor');\n    }\n\n    /**\n     * Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to\n     * several SQL parameter positions.\n     */\n    public function addParameterMapping(string|int $dqlPosition, int $sqlPosition): void\n    {\n        $this->parameterMappings[$dqlPosition][] = $sqlPosition;\n    }\n\n    /**\n     * Gets all DQL to SQL parameter mappings.\n     *\n     * @phpstan-return array<int|string, list<int>> The parameter mappings.\n     */\n    public function getParameterMappings(): array\n    {\n        return $this->parameterMappings;\n    }\n\n    /**\n     * Gets the SQL parameter positions for a DQL parameter name/position.\n     *\n     * @param string|int $dqlPosition The name or position of the DQL parameter.\n     *\n     * @return int[] The positions of the corresponding SQL parameters.\n     * @phpstan-return list<int>\n     */\n    public function getSqlParameterPositions(string|int $dqlPosition): array\n    {\n        return $this->parameterMappings[$dqlPosition];\n    }\n}\n"
  },
  {
    "path": "src/Query/Printer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse function str_repeat;\n\n/**\n * A parse tree printer for Doctrine Query Language parser.\n *\n * @link        http://www.phpdoctrine.org\n */\nclass Printer\n{\n    /** Current indentation level */\n    protected int $indent = 0;\n\n    /**\n     * Constructs a new parse tree printer.\n     *\n     * @param bool $silent Parse tree will not be printed if true.\n     */\n    public function __construct(protected bool $silent = false)\n    {\n    }\n\n    /**\n     * Prints an opening parenthesis followed by production name and increases\n     * indentation level by one.\n     *\n     * This method is called before executing a production.\n     *\n     * @param string $name Production name.\n     */\n    public function startProduction(string $name): void\n    {\n        $this->println('(' . $name);\n        $this->indent++;\n    }\n\n    /**\n     * Decreases indentation level by one and prints a closing parenthesis.\n     *\n     * This method is called after executing a production.\n     */\n    public function endProduction(): void\n    {\n        $this->indent--;\n        $this->println(')');\n    }\n\n    /**\n     * Prints text indented with spaces depending on current indentation level.\n     *\n     * @param string $str The text.\n     */\n    public function println(string $str): void\n    {\n        if (! $this->silent) {\n            echo str_repeat('    ', $this->indent), $str, \"\\n\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Query/QueryException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Exception;\nuse Stringable;\nuse Throwable;\n\nclass QueryException extends Exception implements ORMException\n{\n    public static function dqlError(string $dql): self\n    {\n        return new self($dql);\n    }\n\n    public static function syntaxError(string $message, Throwable|null $previous = null): self\n    {\n        return new self('[Syntax Error] ' . $message, 0, $previous);\n    }\n\n    public static function semanticalError(string $message, Throwable|null $previous = null): self\n    {\n        return new self('[Semantical Error] ' . $message, 0, $previous);\n    }\n\n    public static function invalidLockMode(): self\n    {\n        return new self('Invalid lock mode hint provided.');\n    }\n\n    public static function invalidParameterType(string $expected, string $received): self\n    {\n        return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.');\n    }\n\n    public static function invalidParameterPosition(string $pos): self\n    {\n        return new self('Invalid parameter position: ' . $pos);\n    }\n\n    public static function tooManyParameters(int $expected, int $received): self\n    {\n        return new self('Too many parameters: the query defines ' . $expected . ' parameters and you bound ' . $received);\n    }\n\n    public static function tooFewParameters(int $expected, int $received): self\n    {\n        return new self('Too few parameters: the query defines ' . $expected . ' parameters but you only bound ' . $received);\n    }\n\n    public static function invalidParameterFormat(string $value): self\n    {\n        return new self('Invalid parameter format, ' . $value . ' given, but :<name> or ?<num> expected.');\n    }\n\n    public static function unknownParameter(string $key): self\n    {\n        return new self('Invalid parameter: token ' . $key . ' is not defined in the query.');\n    }\n\n    public static function parameterTypeMismatch(): self\n    {\n        return new self('DQL Query parameter and type numbers mismatch, but have to be exactly equal.');\n    }\n\n    public static function invalidPathExpression(PathExpression $pathExpr): self\n    {\n        return new self(\n            \"Invalid PathExpression '\" . $pathExpr->identificationVariable . '.' . $pathExpr->field . \"'.\",\n        );\n    }\n\n    public static function invalidLiteral(string|Stringable $literal): self\n    {\n        return new self(\"Invalid literal '\" . $literal . \"'\");\n    }\n\n    public static function iterateWithFetchJoinCollectionNotAllowed(AssociationMapping $assoc): self\n    {\n        return new self(\n            'Invalid query operation: Not allowed to iterate over fetch join collections ' .\n            'in class ' . $assoc->sourceEntity . ' association ' . $assoc->fieldName,\n        );\n    }\n\n    public static function partialObjectsAreDangerous(): self\n    {\n        return new self(\n            'Loading partial objects is dangerous. Fetch full objects or consider ' .\n            'using a different fetch mode. If you really want partial objects, ' .\n            'set the doctrine.forcePartialLoad query hint to TRUE.',\n        );\n    }\n\n    /**\n     * @param string[] $assoc\n     * @phpstan-param array<string, string> $assoc\n     */\n    public static function overwritingJoinConditionsNotYetSupported(array $assoc): self\n    {\n        return new self(\n            'Unsupported query operation: It is not yet possible to overwrite the join ' .\n            'conditions in class ' . $assoc['sourceEntityName'] . ' association ' . $assoc['fieldName'] . '. ' .\n            'Use WITH to append additional join conditions to the association.',\n        );\n    }\n\n    public static function associationPathInverseSideNotSupported(PathExpression $pathExpr): self\n    {\n        return new self(\n            'A single-valued association path expression to an inverse side is not supported in DQL queries. ' .\n            'Instead of \"' . $pathExpr->identificationVariable . '.' . $pathExpr->field . '\" use an explicit join.',\n        );\n    }\n\n    public static function iterateWithFetchJoinNotAllowed(AssociationMapping $assoc): self\n    {\n        return new self(\n            'Iterate with fetch join in class ' . $assoc->sourceEntity .\n            ' using association ' . $assoc->fieldName . ' not allowed.',\n        );\n    }\n\n    public static function eagerFetchJoinWithNotAllowed(string $sourceEntity, string $fieldName): self\n    {\n        return new self(\n            'Associations with fetch-mode=EAGER may not be using WITH conditions in\n             \"' . $sourceEntity . '#' . $fieldName . '\".',\n        );\n    }\n\n    public static function iterateWithMixedResultNotAllowed(): self\n    {\n        return new self('Iterating a query with mixed results (using scalars) is not supported.');\n    }\n\n    public static function associationPathCompositeKeyNotSupported(): self\n    {\n        return new self(\n            'A single-valued association path expression to an entity with a composite primary ' .\n            'key is not supported. Explicitly name the components of the composite primary key ' .\n            'in the query.',\n        );\n    }\n\n    public static function instanceOfUnrelatedClass(string $className, string $rootClass): self\n    {\n        return new self(\"Cannot check if a child of '\" . $rootClass . \"' is instanceof '\" . $className . \"', \" .\n            'inheritance hierarchy does not exists between these two classes.');\n    }\n\n    public static function invalidQueryComponent(string $dqlAlias): self\n    {\n        return new self(\n            \"Invalid query component given for DQL alias '\" . $dqlAlias . \"', \" .\n            \"requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys.\",\n        );\n    }\n}\n"
  },
  {
    "path": "src/Query/QueryExpressionVisitor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Common\\Collections\\Expr\\CompositeExpression;\nuse Doctrine\\Common\\Collections\\Expr\\ExpressionVisitor;\nuse Doctrine\\Common\\Collections\\Expr\\Value;\nuse RuntimeException;\n\nuse function count;\nuse function str_replace;\nuse function str_starts_with;\n\n/**\n * Converts Collection expressions to Query expressions.\n */\nclass QueryExpressionVisitor extends ExpressionVisitor\n{\n    private const OPERATOR_MAP = [\n        Comparison::GT => Expr\\Comparison::GT,\n        Comparison::GTE => Expr\\Comparison::GTE,\n        Comparison::LT  => Expr\\Comparison::LT,\n        Comparison::LTE => Expr\\Comparison::LTE,\n    ];\n\n    private readonly Expr $expr;\n\n    /** @var list<mixed> */\n    private array $parameters = [];\n\n    /** @param mixed[] $queryAliases */\n    public function __construct(\n        private readonly array $queryAliases,\n    ) {\n        $this->expr = new Expr();\n    }\n\n    /**\n     * Gets bound parameters.\n     * Filled after {@link dispach()}.\n     *\n     * @return ArrayCollection<int, mixed>\n     */\n    public function getParameters(): ArrayCollection\n    {\n        return new ArrayCollection($this->parameters);\n    }\n\n    public function clearParameters(): void\n    {\n        $this->parameters = [];\n    }\n\n    /**\n     * Converts Criteria expression to Query one based on static map.\n     */\n    private static function convertComparisonOperator(string $criteriaOperator): string|null\n    {\n        return self::OPERATOR_MAP[$criteriaOperator] ?? null;\n    }\n\n    public function walkCompositeExpression(CompositeExpression $expr): mixed\n    {\n        $expressionList = [];\n\n        foreach ($expr->getExpressionList() as $child) {\n            $expressionList[] = $this->dispatch($child);\n        }\n\n        return match ($expr->getType()) {\n            CompositeExpression::TYPE_AND => new Expr\\Andx($expressionList),\n            CompositeExpression::TYPE_OR => new Expr\\Orx($expressionList),\n            CompositeExpression::TYPE_NOT => $this->expr->not($expressionList[0]),\n            default => throw new RuntimeException('Unknown composite ' . $expr->getType()),\n        };\n    }\n\n    public function walkComparison(Comparison $comparison): mixed\n    {\n        if (! isset($this->queryAliases[0])) {\n            throw new QueryException('No aliases are set before invoking walkComparison().');\n        }\n\n        $field = $this->queryAliases[0] . '.' . $comparison->getField();\n\n        foreach ($this->queryAliases as $alias) {\n            if (str_starts_with($comparison->getField() . '.', $alias . '.')) {\n                $field = $comparison->getField();\n                break;\n            }\n        }\n\n        $parameterName = str_replace('.', '_', $comparison->getField());\n\n        foreach ($this->parameters as $parameter) {\n            if ($parameter->getName() === $parameterName) {\n                $parameterName .= '_' . count($this->parameters);\n                break;\n            }\n        }\n\n        $parameter   = new Parameter($parameterName, $this->walkValue($comparison->getValue()));\n        $placeholder = ':' . $parameterName;\n\n        switch ($comparison->getOperator()) {\n            case Comparison::IN:\n                $this->parameters[] = $parameter;\n\n                return $this->expr->in($field, $placeholder);\n\n            case Comparison::NIN:\n                $this->parameters[] = $parameter;\n\n                return $this->expr->notIn($field, $placeholder);\n\n            case Comparison::EQ:\n            case Comparison::IS:\n                if ($this->walkValue($comparison->getValue()) === null) {\n                    return $this->expr->isNull($field);\n                }\n\n                $this->parameters[] = $parameter;\n\n                return $this->expr->eq($field, $placeholder);\n\n            case Comparison::NEQ:\n                if ($this->walkValue($comparison->getValue()) === null) {\n                    return $this->expr->isNotNull($field);\n                }\n\n                $this->parameters[] = $parameter;\n\n                return $this->expr->neq($field, $placeholder);\n\n            case Comparison::CONTAINS:\n                $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType());\n                $this->parameters[] = $parameter;\n\n                return $this->expr->like($field, $placeholder);\n\n            case Comparison::MEMBER_OF:\n                return $this->expr->isMemberOf($comparison->getField(), $comparison->getValue()->getValue());\n\n            case Comparison::STARTS_WITH:\n                $parameter->setValue($parameter->getValue() . '%', $parameter->getType());\n                $this->parameters[] = $parameter;\n\n                return $this->expr->like($field, $placeholder);\n\n            case Comparison::ENDS_WITH:\n                $parameter->setValue('%' . $parameter->getValue(), $parameter->getType());\n                $this->parameters[] = $parameter;\n\n                return $this->expr->like($field, $placeholder);\n\n            default:\n                $operator = self::convertComparisonOperator($comparison->getOperator());\n                if ($operator) {\n                    $this->parameters[] = $parameter;\n\n                    return new Expr\\Comparison(\n                        $field,\n                        $operator,\n                        $placeholder,\n                    );\n                }\n\n                throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator());\n        }\n    }\n\n    public function walkValue(Value $value): mixed\n    {\n        return $value->getValue();\n    }\n}\n"
  },
  {
    "path": "src/Query/ResultSetMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse function count;\n\n/**\n * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.\n *\n * IMPORTANT NOTE:\n * The properties of this class are only public for fast internal READ access and to (drastically)\n * reduce the size of serialized instances for more effective caching due to better (un-)serialization\n * performance.\n *\n * <b>Users should use the public methods.</b>\n *\n * @todo Think about whether the number of lookup maps can be reduced.\n */\nclass ResultSetMapping\n{\n    /**\n     * Whether the result is mixed (contains scalar values together with field values).\n     *\n     * @ignore\n     */\n    public bool $isMixed = false;\n\n    /**\n     * Whether the result is a select statement.\n     *\n     * @ignore\n     */\n    public bool $isSelect = true;\n\n    /**\n     * Maps alias names to class names.\n     *\n     * @ignore\n     * @var array<string, class-string>\n     */\n    public array $aliasMap = [];\n\n    /**\n     * Maps alias names to related association field names.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $relationMap = [];\n\n    /**\n     * Maps alias names to parent alias names.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $parentAliasMap = [];\n\n    /**\n     * Maps column names in the result set to field names for each class.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $fieldMappings = [];\n\n    /**\n     * Map field names for each class to alias\n     *\n     * @var array<class-string, array<string, array<string, string>>>\n     */\n    public array $columnAliasMappings = [];\n\n    /**\n     * Maps column names in the result set to the alias/field name to use in the mapped result.\n     *\n     * @ignore\n     * @phpstan-var array<string, string|int>\n     */\n    public array $scalarMappings = [];\n\n    /**\n     * Maps scalar columns to enums\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public $enumMappings = [];\n\n    /**\n     * Maps column names in the result set to the alias/field type to use in the mapped result.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $typeMappings = [];\n\n    /**\n     * Maps entities in the result set to the alias name to use in the mapped result.\n     *\n     * @ignore\n     * @phpstan-var array<string, string|null>\n     */\n    public array $entityMappings = [];\n\n    /**\n     * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $metaMappings = [];\n\n    /**\n     * Maps column names in the result set to the alias they belong to.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $columnOwnerMap = [];\n\n    /**\n     * List of columns in the result set that are used as discriminator columns.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $discriminatorColumns = [];\n\n    /**\n     * Maps alias names to field names that should be used for indexing.\n     *\n     * @ignore\n     * @phpstan-var array<string, string>\n     */\n    public array $indexByMap = [];\n\n    /**\n     * Map from column names to class names that declare the field the column is mapped to.\n     *\n     * @ignore\n     * @var array<string, class-string>\n     */\n    public array $declaringClasses = [];\n\n    /**\n     * This is necessary to hydrate derivate foreign keys correctly.\n     *\n     * @phpstan-var array<string, array<string, bool>>\n     */\n    public array $isIdentifierColumn = [];\n\n    /**\n     * Maps column names in the result set to field names for each new object expression.\n     *\n     * @phpstan-var array<string, array<string, mixed>>\n     */\n    public array $newObjectMappings = [];\n\n    /**\n     * Maps object Ids in the result set to classnames.\n     *\n     * @phpstan-var array<string|int, class-string>\n     */\n    public array $newObject = [];\n\n    /**\n     * Maps last argument for new objects in order to initiate object construction\n     *\n     * @phpstan-var array<int|string, array{ownerIndex: string|int, argIndex: int|string, argAlias: string}>\n     */\n    public array $nestedNewObjectArguments = [];\n\n    /**\n     * Maps metadata parameter names to the metadata attribute.\n     *\n     * @phpstan-var array<int|string, string>\n     */\n    public array $metadataParameterMapping = [];\n\n    /**\n     * Contains query parameter names to be resolved as discriminator values\n     *\n     * @phpstan-var array<string, string>\n     */\n    public array $discriminatorParameters = [];\n\n    /**\n     * Entities nested in Dto's\n     *\n     * @phpstan-var array<string, array<string, (int|string)>>\n     */\n    public array $nestedEntities = [];\n\n    /**\n     * Adds an entity result to this ResultSetMapping.\n     *\n     * @param class-string $class       The class name of the entity.\n     * @param string       $alias       The alias for the class. The alias must be unique among all entity\n     *                                  results or joined entity results within this ResultSetMapping.\n     * @param string|null  $resultAlias The result alias with which the entity result should be\n     *                                  placed in the result structure.\n     *\n     * @return $this\n     *\n     * @todo Rename: addRootEntity\n     */\n    public function addEntityResult(string $class, string $alias, string|null $resultAlias = null): static\n    {\n        $this->aliasMap[$alias]       = $class;\n        $this->entityMappings[$alias] = $resultAlias;\n\n        if ($resultAlias !== null) {\n            $this->isMixed = true;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets a discriminator column for an entity result or joined entity result.\n     * The discriminator column will be used to determine the concrete class name to\n     * instantiate.\n     *\n     * @param string $alias       The alias of the entity result or joined entity result the discriminator\n     *                            column should be used for.\n     * @param string $discrColumn The name of the discriminator column in the SQL result set.\n     *\n     * @return $this\n     *\n     * @todo Rename: addDiscriminatorColumn\n     */\n    public function setDiscriminatorColumn(string $alias, string $discrColumn): static\n    {\n        $this->discriminatorColumns[$alias] = $discrColumn;\n        $this->columnOwnerMap[$discrColumn] = $alias;\n\n        return $this;\n    }\n\n    /**\n     * Sets a field to use for indexing an entity result or joined entity result.\n     *\n     * @param string $alias     The alias of an entity result or joined entity result.\n     * @param string $fieldName The name of the field to use for indexing.\n     *\n     * @return $this\n     */\n    public function addIndexBy(string $alias, string $fieldName): static\n    {\n        $found = false;\n\n        foreach ([...$this->metaMappings, ...$this->fieldMappings] as $columnName => $columnFieldName) {\n            if (! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) {\n                continue;\n            }\n\n            $this->addIndexByColumn($alias, $columnName);\n            $found = true;\n\n            break;\n        }\n\n        /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals\n        if ( ! $found) {\n            $message = sprintf(\n                'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',\n                $alias,\n                $fieldName\n            );\n\n            throw new \\LogicException($message);\n        }\n        */\n\n        return $this;\n    }\n\n    /**\n     * Sets to index by a scalar result column name.\n     *\n     * @return $this\n     */\n    public function addIndexByScalar(string $resultColumnName): static\n    {\n        $this->indexByMap['scalars'] = $resultColumnName;\n\n        return $this;\n    }\n\n    /**\n     * Sets a column to use for indexing an entity or joined entity result by the given alias name.\n     *\n     * @return $this\n     */\n    public function addIndexByColumn(string $alias, string $resultColumnName): static\n    {\n        $this->indexByMap[$alias] = $resultColumnName;\n\n        return $this;\n    }\n\n    /**\n     * Checks whether an entity result or joined entity result with a given alias has\n     * a field set for indexing.\n     *\n     * @todo Rename: isIndexed($alias)\n     */\n    public function hasIndexBy(string $alias): bool\n    {\n        return isset($this->indexByMap[$alias]);\n    }\n\n    /**\n     * Checks whether the column with the given name is mapped as a field result\n     * as part of an entity result or joined entity result.\n     *\n     * @param string $columnName The name of the column in the SQL result set.\n     *\n     * @todo Rename: isField\n     */\n    public function isFieldResult(string $columnName): bool\n    {\n        return isset($this->fieldMappings[$columnName]);\n    }\n\n    /**\n     * Adds a field to the result that belongs to an entity or joined entity.\n     *\n     * @param string            $alias          The alias of the root entity or joined entity to which the field belongs.\n     * @param string            $columnName     The name of the column in the SQL result set.\n     * @param string            $fieldName      The name of the field on the declaring class.\n     * @param class-string|null $declaringClass The name of the class that declares/owns the specified field.\n     *                                          When $alias refers to a superclass in a mapped hierarchy but\n     *                                          the field $fieldName is defined on a subclass, specify that here.\n     *                                          If not specified, the field is assumed to belong to the class\n     *                                          designated by $alias.\n     *\n     * @return $this\n     *\n     * @todo Rename: addField\n     */\n    public function addFieldResult(string $alias, string $columnName, string $fieldName, string|null $declaringClass = null): static\n    {\n        // column name (in result set) => field name\n        $this->fieldMappings[$columnName] = $fieldName;\n        // column name => alias of owner\n        $this->columnOwnerMap[$columnName] = $alias;\n        // field name => class name of declaring class\n        $declaringClass                      = $declaringClass ?: $this->aliasMap[$alias];\n        $this->declaringClasses[$columnName] = $declaringClass;\n\n        $this->columnAliasMappings[$declaringClass][$alias][$fieldName] = $columnName;\n\n        if (! $this->isMixed && $this->scalarMappings) {\n            $this->isMixed = true;\n        }\n\n        return $this;\n    }\n\n    public function hasColumnAliasByField(string $alias, string $fieldName): bool\n    {\n        $declaringClass = $this->aliasMap[$alias];\n\n        return isset($this->columnAliasMappings[$declaringClass][$alias][$fieldName]);\n    }\n\n    public function getColumnAliasByField(string $alias, string $fieldName): string\n    {\n        $declaringClass = $this->aliasMap[$alias];\n\n        return $this->columnAliasMappings[$declaringClass][$alias][$fieldName];\n    }\n\n    /**\n     * Adds a joined entity result.\n     *\n     * @param class-string $class       The class name of the joined entity.\n     * @param string       $alias       The unique alias to use for the joined entity.\n     * @param string       $parentAlias The alias of the entity result that is the parent of this joined result.\n     * @param string       $relation    The association field that connects the parent entity result\n     *                                  with the joined entity result.\n     *\n     * @return $this\n     *\n     * @todo Rename: addJoinedEntity\n     */\n    public function addJoinedEntityResult(string $class, string $alias, string $parentAlias, string $relation): static\n    {\n        $this->aliasMap[$alias]       = $class;\n        $this->parentAliasMap[$alias] = $parentAlias;\n        $this->relationMap[$alias]    = $relation;\n\n        return $this;\n    }\n\n    /**\n     * Adds a scalar result mapping.\n     *\n     * @param string     $columnName The name of the column in the SQL result set.\n     * @param string|int $alias      The result alias with which the scalar result should be placed in the result structure.\n     * @param string     $type       The column type\n     *\n     * @return $this\n     *\n     * @todo Rename: addScalar\n     */\n    public function addScalarResult(string $columnName, string|int $alias, string $type = 'string'): static\n    {\n        $this->scalarMappings[$columnName] = $alias;\n        $this->typeMappings[$columnName]   = $type;\n\n        if (! $this->isMixed && $this->fieldMappings) {\n            $this->isMixed = true;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds a scalar result mapping.\n     *\n     * @param string $columnName The name of the column in the SQL result set.\n     * @param string $enumType   The enum type\n     *\n     * @return $this\n     */\n    public function addEnumResult(string $columnName, string $enumType): static\n    {\n        $this->enumMappings[$columnName] = $enumType;\n\n        return $this;\n    }\n\n    /**\n     * Adds a metadata parameter mappings.\n     */\n    public function addMetadataParameterMapping(string|int $parameter, string $attribute): void\n    {\n        $this->metadataParameterMapping[$parameter] = $attribute;\n    }\n\n    /**\n     * Checks whether a column with a given name is mapped as a scalar result.\n     *\n     * @todo Rename: isScalar\n     */\n    public function isScalarResult(string $columnName): bool\n    {\n        return isset($this->scalarMappings[$columnName]);\n    }\n\n    /**\n     * Gets the name of the class of an entity result or joined entity result,\n     * identified by the given unique alias.\n     *\n     * @phpstan-return class-string\n     */\n    public function getClassName(string $alias): string\n    {\n        return $this->aliasMap[$alias];\n    }\n\n    /**\n     * Gets the field alias for a column that is mapped as a scalar value.\n     *\n     * @param string $columnName The name of the column in the SQL result set.\n     */\n    public function getScalarAlias(string $columnName): string|int\n    {\n        return $this->scalarMappings[$columnName];\n    }\n\n    /**\n     * Gets the name of the class that owns a field mapping for the specified column.\n     *\n     * @phpstan-return class-string\n     */\n    public function getDeclaringClass(string $columnName): string\n    {\n        return $this->declaringClasses[$columnName];\n    }\n\n    public function getRelation(string $alias): string\n    {\n        return $this->relationMap[$alias];\n    }\n\n    public function isRelation(string $alias): bool\n    {\n        return isset($this->relationMap[$alias]);\n    }\n\n    /**\n     * Gets the alias of the class that owns a field mapping for the specified column.\n     */\n    public function getEntityAlias(string $columnName): string\n    {\n        return $this->columnOwnerMap[$columnName];\n    }\n\n    /**\n     * Gets the parent alias of the given alias.\n     */\n    public function getParentAlias(string $alias): string\n    {\n        return $this->parentAliasMap[$alias];\n    }\n\n    /**\n     * Checks whether the given alias has a parent alias.\n     */\n    public function hasParentAlias(string $alias): bool\n    {\n        return isset($this->parentAliasMap[$alias]);\n    }\n\n    /**\n     * Gets the field name for a column name.\n     */\n    public function getFieldName(string $columnName): string\n    {\n        return $this->fieldMappings[$columnName];\n    }\n\n    /** @return array<string, class-string> */\n    public function getAliasMap(): array\n    {\n        return $this->aliasMap;\n    }\n\n    /**\n     * Gets the number of different entities that appear in the mapped result.\n     *\n     * @phpstan-return 0|positive-int\n     */\n    public function getEntityResultCount(): int\n    {\n        return count($this->aliasMap);\n    }\n\n    /**\n     * Checks whether this ResultSetMapping defines a mixed result.\n     *\n     * Mixed results can only occur in object and array (graph) hydration. In such a\n     * case a mixed result means that scalar values are mixed with objects/array in\n     * the result.\n     */\n    public function isMixedResult(): bool\n    {\n        return $this->isMixed;\n    }\n\n    /**\n     * Adds a meta column (foreign key or discriminator column) to the result set.\n     *\n     * @param string      $alias      The result alias with which the meta result should be placed in the result structure.\n     * @param string      $columnName The name of the column in the SQL result set.\n     * @param string      $fieldName  The name of the field on the declaring class.\n     * @param string|null $type       The column type\n     *\n     * @return $this\n     *\n     * @todo Make all methods of this class require all parameters and not infer anything\n     */\n    public function addMetaResult(\n        string $alias,\n        string $columnName,\n        string $fieldName,\n        bool $isIdentifierColumn = false,\n        string|null $type = null,\n    ): static {\n        $this->metaMappings[$columnName]   = $fieldName;\n        $this->columnOwnerMap[$columnName] = $alias;\n\n        if ($isIdentifierColumn) {\n            $this->isIdentifierColumn[$alias][$columnName] = true;\n        }\n\n        if ($type) {\n            $this->typeMappings[$columnName] = $type;\n        }\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "src/Query/ResultSetMappingBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse InvalidArgumentException;\nuse Stringable;\n\nuse function in_array;\nuse function sprintf;\n\n/**\n * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields.\n */\nclass ResultSetMappingBuilder extends ResultSetMapping implements Stringable\n{\n    use SQLResultCasing;\n\n    /**\n     * Picking this rename mode will register entity columns as is,\n     * as they are in the database. This can cause clashes when multiple\n     * entities are fetched that have columns with the same name.\n     */\n    public const COLUMN_RENAMING_NONE = 1;\n\n    /**\n     * Picking custom renaming allows the user to define the renaming\n     * of specific columns with a rename array that contains column names as\n     * keys and result alias as values.\n     */\n    public const COLUMN_RENAMING_CUSTOM = 2;\n\n    /**\n     * Incremental renaming uses a result set mapping internal counter to add a\n     * number to each column result, leading to uniqueness. This only works if\n     * you use {@see generateSelectClause()} to generate the SELECT clause for\n     * you.\n     */\n    public const COLUMN_RENAMING_INCREMENT = 3;\n\n    private int $sqlCounter = 0;\n\n    /** @phpstan-param self::COLUMN_RENAMING_* $defaultRenameMode */\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        private readonly int $defaultRenameMode = self::COLUMN_RENAMING_NONE,\n    ) {\n    }\n\n    /**\n     * Adds a root entity and all of its fields to the result set.\n     *\n     * @param class-string          $class          The class name of the root entity.\n     * @param string                $alias          The unique alias to use for the root entity.\n     * @param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).\n     * @phpstan-param self::COLUMN_RENAMING_*|null $renameMode\n     */\n    public function addRootEntityFromClassMetadata(\n        string $class,\n        string $alias,\n        array $renamedColumns = [],\n        int|null $renameMode = null,\n    ): void {\n        $renameMode     = $renameMode ?: $this->defaultRenameMode;\n        $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);\n\n        $this->addEntityResult($class, $alias);\n        $this->addAllClassFields($class, $alias, $columnAliasMap);\n    }\n\n    /**\n     * Adds a joined entity and all of its fields to the result set.\n     *\n     * @param class-string          $class          The class name of the joined entity.\n     * @param string                $alias          The unique alias to use for the joined entity.\n     * @param string                $parentAlias    The alias of the entity result that is the parent of this joined result.\n     * @param string                $relation       The association field that connects the parent entity result\n     *                                              with the joined entity result.\n     * @param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).\n     * @phpstan-param self::COLUMN_RENAMING_*|null $renameMode\n     */\n    public function addJoinedEntityFromClassMetadata(\n        string $class,\n        string $alias,\n        string $parentAlias,\n        string $relation,\n        array $renamedColumns = [],\n        int|null $renameMode = null,\n    ): void {\n        $renameMode     = $renameMode ?: $this->defaultRenameMode;\n        $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);\n\n        $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);\n        $this->addAllClassFields($class, $alias, $columnAliasMap);\n    }\n\n    /**\n     * Adds all fields of the given class to the result set mapping (columns and meta fields).\n     *\n     * @param string[] $columnAliasMap\n     * @phpstan-param array<string, string> $columnAliasMap\n     *\n     * @throws InvalidArgumentException\n     */\n    protected function addAllClassFields(string $class, string $alias, array $columnAliasMap = []): void\n    {\n        $classMetadata = $this->em->getClassMetadata($class);\n        $platform      = $this->em->getConnection()->getDatabasePlatform();\n\n        if (! $this->isInheritanceSupported($classMetadata)) {\n            throw new InvalidArgumentException('ResultSetMapping builder does not currently support your inheritance scheme.');\n        }\n\n        foreach ($classMetadata->getColumnNames() as $columnName) {\n            $propertyName = $classMetadata->getFieldName($columnName);\n            $columnAlias  = $this->getSQLResultCasing($platform, $columnAliasMap[$columnName]);\n\n            if (isset($this->fieldMappings[$columnAlias])) {\n                throw new InvalidArgumentException(sprintf(\n                    \"The column '%s' conflicts with another column in the mapper.\",\n                    $columnName,\n                ));\n            }\n\n            $this->addFieldResult($alias, $columnAlias, $propertyName);\n\n            $enumType = $classMetadata->getFieldMapping($propertyName)->enumType ?? null;\n            if (! empty($enumType)) {\n                $this->addEnumResult($columnAlias, $enumType);\n            }\n        }\n\n        foreach ($classMetadata->associationMappings as $associationMapping) {\n            if ($associationMapping->isToOneOwningSide()) {\n                $targetClass  = $this->em->getClassMetadata($associationMapping->targetEntity);\n                $isIdentifier = isset($associationMapping->id) && $associationMapping->id === true;\n\n                foreach ($associationMapping->joinColumns as $joinColumn) {\n                    $columnName  = $joinColumn->name;\n                    $columnAlias = $this->getSQLResultCasing($platform, $columnAliasMap[$columnName]);\n                    $columnType  = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n\n                    if (isset($this->metaMappings[$columnAlias])) {\n                        throw new InvalidArgumentException(sprintf(\n                            \"The column '%s' conflicts with another column in the mapper.\",\n                            $columnAlias,\n                        ));\n                    }\n\n                    $this->addMetaResult($alias, $columnAlias, $columnName, $isIdentifier, $columnType);\n                }\n            }\n        }\n    }\n\n    private function isInheritanceSupported(ClassMetadata $classMetadata): bool\n    {\n        if (\n            $classMetadata->isInheritanceTypeSingleTable()\n            && in_array($classMetadata->name, $classMetadata->discriminatorMap, true)\n        ) {\n            return true;\n        }\n\n        return ! ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined());\n    }\n\n    /**\n     * Gets column alias for a given column.\n     *\n     * @phpstan-param array<string, string>  $customRenameColumns\n     *\n     * @phpstan-assert self::COLUMN_RENAMING_* $mode\n     */\n    private function getColumnAlias(string $columnName, int $mode, array $customRenameColumns): string\n    {\n        return match ($mode) {\n            self::COLUMN_RENAMING_INCREMENT => $columnName . $this->sqlCounter++,\n            self::COLUMN_RENAMING_CUSTOM => $customRenameColumns[$columnName] ?? $columnName,\n            self::COLUMN_RENAMING_NONE => $columnName,\n            default => throw new InvalidArgumentException(sprintf('%d is not a valid value for $mode', $mode)),\n        };\n    }\n\n    /**\n     * Retrieves a class columns and join columns aliases that are used in the SELECT clause.\n     *\n     * This depends on the renaming mode selected by the user.\n     *\n     * @param class-string $className\n     * @phpstan-param self::COLUMN_RENAMING_* $mode\n     * @phpstan-param array<string, string> $customRenameColumns\n     *\n     * @return string[]\n     */\n    private function getColumnAliasMap(\n        string $className,\n        int $mode,\n        array $customRenameColumns,\n    ): array {\n        if ($customRenameColumns) { // for BC with 2.2-2.3 API\n            $mode = self::COLUMN_RENAMING_CUSTOM;\n        }\n\n        $columnAlias = [];\n        $class       = $this->em->getClassMetadata($className);\n\n        foreach ($class->getColumnNames() as $columnName) {\n            $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);\n        }\n\n        foreach ($class->associationMappings as $associationMapping) {\n            if ($associationMapping->isToOneOwningSide()) {\n                foreach ($associationMapping->joinColumns as $joinColumn) {\n                    $columnName               = $joinColumn->name;\n                    $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);\n                }\n            }\n        }\n\n        return $columnAlias;\n    }\n\n    /**\n     * Generates the Select clause from this ResultSetMappingBuilder.\n     *\n     * Works only for all the entity results. The select parts for scalar\n     * expressions have to be written manually.\n     *\n     * @param string[] $tableAliases\n     * @phpstan-param array<string, string> $tableAliases\n     */\n    public function generateSelectClause(array $tableAliases = []): string\n    {\n        $sql = '';\n\n        foreach ($this->columnOwnerMap as $columnName => $dqlAlias) {\n            $tableAlias = $tableAliases[$dqlAlias] ?? $dqlAlias;\n\n            if ($sql !== '') {\n                $sql .= ', ';\n            }\n\n            if (isset($this->fieldMappings[$columnName])) {\n                $class             = $this->em->getClassMetadata($this->declaringClasses[$columnName]);\n                $fieldName         = $this->fieldMappings[$columnName];\n                $classFieldMapping = $class->fieldMappings[$fieldName];\n                $columnSql         = $tableAlias . '.' . $classFieldMapping->columnName;\n\n                $type      = Type::getType($classFieldMapping->type);\n                $columnSql = $type->convertToPHPValueSQL($columnSql, $this->em->getConnection()->getDatabasePlatform());\n\n                $sql .= $columnSql;\n            } elseif (isset($this->metaMappings[$columnName])) {\n                $sql .= $tableAlias . '.' . $this->metaMappings[$columnName];\n            } elseif (isset($this->discriminatorColumns[$dqlAlias])) {\n                $sql .= $tableAlias . '.' . $this->discriminatorColumns[$dqlAlias];\n            }\n\n            $sql .= ' AS ' . $columnName;\n        }\n\n        return $sql;\n    }\n\n    public function __toString(): string\n    {\n        return $this->generateSelectClause([]);\n    }\n}\n"
  },
  {
    "path": "src/Query/SqlOutputWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\Query\\Exec\\PreparedExecutorFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SingleSelectSqlFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse LogicException;\n\nclass SqlOutputWalker extends SqlWalker implements OutputWalker\n{\n    public function getFinalizer(AST\\DeleteStatement|AST\\UpdateStatement|AST\\SelectStatement $AST): SqlFinalizer\n    {\n        switch (true) {\n            case $AST instanceof AST\\SelectStatement:\n                return new SingleSelectSqlFinalizer($this->createSqlForFinalizer($AST));\n\n            case $AST instanceof AST\\UpdateStatement:\n                return new PreparedExecutorFinalizer($this->createUpdateStatementExecutor($AST));\n\n            case $AST instanceof AST\\DeleteStatement:\n                return new PreparedExecutorFinalizer($this->createDeleteStatementExecutor($AST));\n        }\n\n        throw new LogicException('Unexpected AST node type');\n    }\n}\n"
  },
  {
    "path": "src/Query/SqlWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse BadMethodCallException;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Utility\\HierarchyDiscriminatorResolver;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse InvalidArgumentException;\nuse LogicException;\n\nuse function array_diff;\nuse function array_filter;\nuse function array_keys;\nuse function array_map;\nuse function array_merge;\nuse function assert;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function is_array;\nuse function is_float;\nuse function is_int;\nuse function is_numeric;\nuse function is_string;\nuse function preg_match;\nuse function reset;\nuse function sprintf;\nuse function strtolower;\nuse function strtoupper;\nuse function trim;\n\n/**\n * The SqlWalker walks over a DQL AST and constructs the corresponding SQL.\n *\n * @phpstan-import-type QueryComponent from Parser\n * @phpstan-consistent-constructor\n */\nclass SqlWalker\n{\n    public const HINT_DISTINCT = 'doctrine.distinct';\n\n    /**\n     * Used to mark a query as containing a PARTIAL expression, which needs to be known by SLC.\n     */\n    public const HINT_PARTIAL = 'doctrine.partial';\n\n    private readonly ResultSetMapping $rsm;\n\n    /**\n     * Counter for generating unique column aliases.\n     */\n    private int $aliasCounter = 0;\n\n    /**\n     * Counter for generating unique table aliases.\n     */\n    private int $tableAliasCounter = 0;\n\n    /**\n     * Counter for generating unique scalar result.\n     */\n    private int $scalarResultCounter = 1;\n\n    /**\n     * Counter for generating unique parameter indexes.\n     */\n    private int $sqlParamIndex = 0;\n\n    /**\n     * Counter for generating indexes.\n     */\n    private int $newObjectCounter = 0;\n\n    private readonly EntityManagerInterface $em;\n    private readonly Connection $conn;\n\n    /** @var mixed[] */\n    private array $tableAliasMap = [];\n\n    /**\n     * Map from result variable names to their SQL column alias names.\n     *\n     * @phpstan-var array<string|int, string|list<string>>\n     */\n    private array $scalarResultAliasMap = [];\n\n    /**\n     * Map from Table-Alias + Column-Name to OrderBy-Direction.\n     *\n     * @var array<string, string>\n     */\n    private array $orderedColumnsMap = [];\n\n    /**\n     * Map from DQL-Alias + Field-Name to SQL Column Alias.\n     *\n     * @var array<string, array<string, string>>\n     */\n    private array $scalarFields = [];\n\n    /**\n     * A list of classes that appear in non-scalar SelectExpressions.\n     *\n     * @phpstan-var array<string, array{class: ClassMetadata, dqlAlias: string, resultAlias: string|null}>\n     */\n    private array $selectedClasses = [];\n\n    /**\n     * The DQL alias of the root class of the currently traversed query.\n     *\n     * @phpstan-var list<string>\n     */\n    private array $rootAliases = [];\n\n    /**\n     * Flag that indicates whether to generate SQL table aliases in the SQL.\n     * These should only be generated for SELECT queries, not for UPDATE/DELETE.\n     */\n    private bool $useSqlTableAliases = true;\n\n    /**\n     * The database platform abstraction.\n     */\n    private readonly AbstractPlatform $platform;\n\n    /**\n     * The quote strategy.\n     */\n    private readonly QuoteStrategy $quoteStrategy;\n\n    /** @phpstan-param array<string, QueryComponent> $queryComponents The query components (symbol table). */\n    public function __construct(\n        private readonly Query $query,\n        private readonly ParserResult $parserResult,\n        private array $queryComponents,\n    ) {\n        $this->rsm           = $parserResult->getResultSetMapping();\n        $this->em            = $query->getEntityManager();\n        $this->conn          = $this->em->getConnection();\n        $this->platform      = $this->conn->getDatabasePlatform();\n        $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();\n    }\n\n    /**\n     * Gets the Query instance used by the walker.\n     */\n    public function getQuery(): Query\n    {\n        return $this->query;\n    }\n\n    /**\n     * Gets the Connection used by the walker.\n     */\n    public function getConnection(): Connection\n    {\n        return $this->conn;\n    }\n\n    /**\n     * Gets the EntityManager used by the walker.\n     */\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    /**\n     * Gets the information about a single query component.\n     *\n     * @param string $dqlAlias The DQL alias.\n     *\n     * @return mixed[]\n     * @phpstan-return QueryComponent\n     */\n    public function getQueryComponent(string $dqlAlias): array\n    {\n        return $this->queryComponents[$dqlAlias];\n    }\n\n    public function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata\n    {\n        return $this->queryComponents[$dqlAlias]['metadata']\n            ?? throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias));\n    }\n\n    /**\n     * Returns internal queryComponents array.\n     *\n     * @return array<string, QueryComponent>\n     */\n    public function getQueryComponents(): array\n    {\n        return $this->queryComponents;\n    }\n\n    /**\n     * Sets or overrides a query component for a given dql alias.\n     *\n     * @phpstan-param QueryComponent $queryComponent\n     */\n    public function setQueryComponent(string $dqlAlias, array $queryComponent): void\n    {\n        $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'];\n\n        if (array_diff($requiredKeys, array_keys($queryComponent))) {\n            throw QueryException::invalidQueryComponent($dqlAlias);\n        }\n\n        $this->queryComponents[$dqlAlias] = $queryComponent;\n    }\n\n    /**\n     * Gets an executor that can be used to execute the result of this walker.\n     *\n     * @deprecated Output walkers should no longer create the executor directly, but instead provide\n     *             a SqlFinalizer by implementing the `OutputWalker` interface. Thus, this method is\n     *             no longer needed and will be removed in 4.0.\n     */\n    public function getExecutor(AST\\SelectStatement|AST\\UpdateStatement|AST\\DeleteStatement $statement): Exec\\AbstractSqlExecutor\n    {\n        Deprecation::trigger(\n            'doctrine/orm',\n            'https://github.com/doctrine/orm/pull/11188/',\n            'Output walkers should implement %s. That way, the %s method is no longer needed and will be removed in 4.0',\n            OutputWalker::class,\n            __METHOD__,\n        );\n\n        return match (true) {\n            $statement instanceof AST\\UpdateStatement => $this->createUpdateStatementExecutor($statement),\n            $statement instanceof AST\\DeleteStatement => $this->createDeleteStatementExecutor($statement),\n            default => new Exec\\SingleSelectExecutor($statement, $this),\n        };\n    }\n\n    protected function createUpdateStatementExecutor(AST\\UpdateStatement $AST): Exec\\AbstractSqlExecutor\n    {\n        $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);\n\n        return $primaryClass->isInheritanceTypeJoined()\n            ? new Exec\\MultiTableUpdateExecutor($AST, $this)\n            : new Exec\\SingleTableDeleteUpdateExecutor($AST, $this);\n    }\n\n    protected function createDeleteStatementExecutor(AST\\DeleteStatement $AST): Exec\\AbstractSqlExecutor\n    {\n        $primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);\n\n        return $primaryClass->isInheritanceTypeJoined()\n            ? new Exec\\MultiTableDeleteExecutor($AST, $this)\n            : new Exec\\SingleTableDeleteUpdateExecutor($AST, $this);\n    }\n\n    /**\n     * Generates a unique, short SQL table alias.\n     */\n    public function getSQLTableAlias(string $tableName, string $dqlAlias = ''): string\n    {\n        $tableName .= $dqlAlias ? '@[' . $dqlAlias . ']' : '';\n\n        if (! isset($this->tableAliasMap[$tableName])) {\n            $this->tableAliasMap[$tableName] = (preg_match('/[a-z]/i', $tableName[0]) ? strtolower($tableName[0]) : 't')\n                . $this->tableAliasCounter++ . '_';\n        }\n\n        return $this->tableAliasMap[$tableName];\n    }\n\n    /**\n     * Forces the SqlWalker to use a specific alias for a table name, rather than\n     * generating an alias on its own.\n     */\n    public function setSQLTableAlias(string $tableName, string $alias, string $dqlAlias = ''): string\n    {\n        $tableName .= $dqlAlias ? '@[' . $dqlAlias . ']' : '';\n\n        $this->tableAliasMap[$tableName] = $alias;\n\n        return $alias;\n    }\n\n    /**\n     * Gets an SQL column alias for a column name.\n     */\n    public function getSQLColumnAlias(string $columnName): string\n    {\n        return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform);\n    }\n\n    /**\n     * Generates the SQL JOINs that are necessary for Class Table Inheritance\n     * for the given class.\n     */\n    private function generateClassTableInheritanceJoins(\n        ClassMetadata $class,\n        string $dqlAlias,\n    ): string {\n        $sql = '';\n\n        $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);\n\n        // INNER JOIN parent class tables\n        foreach ($class->parentClasses as $parentClassName) {\n            $parentClass = $this->em->getClassMetadata($parentClassName);\n            $tableAlias  = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);\n\n            // If this is a joined association we must use left joins to preserve the correct result.\n            $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';\n            $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';\n\n            $sqlParts = [];\n\n            foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {\n                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;\n            }\n\n            // Add filters on the root class\n            $sqlParts[] = $this->generateFilterConditionSQL($parentClass, $tableAlias);\n\n            $sql .= implode(' AND ', array_filter($sqlParts));\n        }\n\n        // Ignore subclassing inclusion if partial objects is disallowed\n        if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {\n            return $sql;\n        }\n\n        // LEFT JOIN child class tables\n        foreach ($class->subClasses as $subClassName) {\n            $subClass   = $this->em->getClassMetadata($subClassName);\n            $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);\n\n            $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';\n\n            $sqlParts = [];\n\n            foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {\n                $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;\n            }\n\n            $sql .= implode(' AND ', $sqlParts);\n        }\n\n        return $sql;\n    }\n\n    private function generateOrderedCollectionOrderByItems(): string\n    {\n        $orderedColumns = [];\n\n        foreach ($this->selectedClasses as $selectedClass) {\n            $dqlAlias = $selectedClass['dqlAlias'];\n            $qComp    = $this->queryComponents[$dqlAlias];\n\n            if (! isset($qComp['relation']->orderBy)) {\n                continue;\n            }\n\n            assert(isset($qComp['metadata']));\n            $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name);\n\n            foreach ($qComp['relation']->orderBy as $fieldName => $orientation) {\n                $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform);\n                $tableName  = $qComp['metadata']->isInheritanceTypeJoined()\n                    ? $persister->getOwningTable($fieldName)\n                    : $qComp['metadata']->getTableName();\n\n                $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName;\n\n                // OrderByClause should replace an ordered relation. see - DDC-2475\n                if (isset($this->orderedColumnsMap[$orderedColumn])) {\n                    continue;\n                }\n\n                $this->orderedColumnsMap[$orderedColumn] = $orientation;\n                $orderedColumns[]                        = $orderedColumn . ' ' . $orientation;\n            }\n        }\n\n        return implode(', ', $orderedColumns);\n    }\n\n    /**\n     * Generates a discriminator column SQL condition for the class with the given DQL alias.\n     *\n     * @phpstan-param list<string> $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.\n     */\n    private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): string\n    {\n        $sqlParts = [];\n\n        foreach ($dqlAliases as $dqlAlias) {\n            $class = $this->getMetadataForDqlAlias($dqlAlias);\n\n            if (! $class->isInheritanceTypeSingleTable()) {\n                continue;\n            }\n\n            $sqlTableAlias = $this->useSqlTableAliases\n                ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'\n                : '';\n\n            $conn   = $this->em->getConnection();\n            $values = [];\n\n            if ($class->discriminatorValue !== null) { // discriminators can be 0\n                $values[] = $class->getDiscriminatorColumn()->type === 'integer' && is_int($class->discriminatorValue)\n                    ? $class->discriminatorValue\n                    : $conn->quote((string) $class->discriminatorValue);\n            }\n\n            foreach ($class->subClasses as $subclassName) {\n                $subclassMetadata = $this->em->getClassMetadata($subclassName);\n\n                // Abstract entity classes show up in the list of subClasses, but may be omitted\n                // from the discriminator map. In that case, they have a null discriminator value.\n                if ($subclassMetadata->discriminatorValue === null) {\n                    continue;\n                }\n\n                $values[] = $subclassMetadata->getDiscriminatorColumn()->type === 'integer' && is_int($subclassMetadata->discriminatorValue)\n                    ? $subclassMetadata->discriminatorValue\n                    : $conn->quote((string) $subclassMetadata->discriminatorValue);\n            }\n\n            if ($values !== []) {\n                $sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()->name . ' IN (' . implode(', ', $values) . ')';\n            } else {\n                $sqlParts[] = '1=0'; // impossible condition\n            }\n        }\n\n        $sql = implode(' AND ', $sqlParts);\n\n        return count($sqlParts) > 1 ? '(' . $sql . ')' : $sql;\n    }\n\n    /**\n     * Generates the filter SQL for a given entity and table alias.\n     */\n    private function generateFilterConditionSQL(\n        ClassMetadata $targetEntity,\n        string $targetTableAlias,\n    ): string {\n        if (! $this->em->hasFilters()) {\n            return '';\n        }\n\n        switch ($targetEntity->inheritanceType) {\n            case ClassMetadata::INHERITANCE_TYPE_NONE:\n                break;\n            case ClassMetadata::INHERITANCE_TYPE_JOINED:\n                // The classes in the inheritance will be added to the query one by one,\n                // but only the root node is getting filtered\n                if ($targetEntity->name !== $targetEntity->rootEntityName) {\n                    return '';\n                }\n\n                break;\n            case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE:\n                // With STI the table will only be queried once, make sure that the filters\n                // are added to the root entity\n                $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);\n                break;\n            default:\n                //@todo: throw exception?\n                return '';\n        }\n\n        $filterClauses = [];\n        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {\n            $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);\n            if ($filterExpr !== '') {\n                $filterClauses[] = '(' . $filterExpr . ')';\n            }\n        }\n\n        return implode(' AND ', $filterClauses);\n    }\n\n    /**\n     * Walks down a SelectStatement AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): string\n    {\n        $sql       = $this->createSqlForFinalizer($selectStatement);\n        $finalizer = new Exec\\SingleSelectSqlFinalizer($sql);\n\n        return $finalizer->finalizeSql($this->query);\n    }\n\n    protected function createSqlForFinalizer(AST\\SelectStatement $selectStatement): string\n    {\n        $sql = $this->walkSelectClause($selectStatement->selectClause)\n            . $this->walkFromClause($selectStatement->fromClause)\n            . $this->walkWhereClause($selectStatement->whereClause);\n\n        if ($selectStatement->groupByClause) {\n            $sql .= $this->walkGroupByClause($selectStatement->groupByClause);\n        }\n\n        if ($selectStatement->havingClause) {\n            $sql .= $this->walkHavingClause($selectStatement->havingClause);\n        }\n\n        if ($selectStatement->orderByClause) {\n            $sql .= $this->walkOrderByClause($selectStatement->orderByClause);\n        }\n\n        $orderBySql = $this->generateOrderedCollectionOrderByItems();\n        if (! $selectStatement->orderByClause && $orderBySql) {\n            $sql .= ' ORDER BY ' . $orderBySql;\n        }\n\n        $this->assertOptimisticLockingHasAllClassesVersioned();\n\n        return $sql;\n    }\n\n    private function assertOptimisticLockingHasAllClassesVersioned(): void\n    {\n        $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE;\n\n        if ($lockMode === LockMode::OPTIMISTIC) {\n            foreach ($this->selectedClasses as $selectedClass) {\n                if (! $selectedClass['class']->isVersioned) {\n                    throw OptimisticLockException::lockFailed($selectedClass['class']->name);\n                }\n            }\n        }\n    }\n\n    /**\n     * Walks down a UpdateStatement AST node, thereby generating the appropriate SQL.\n     */\n    public function walkUpdateStatement(AST\\UpdateStatement $updateStatement): string\n    {\n        $this->useSqlTableAliases = false;\n        $this->rsm->isSelect      = false;\n\n        return $this->walkUpdateClause($updateStatement->updateClause)\n            . $this->walkWhereClause($updateStatement->whereClause);\n    }\n\n    /**\n     * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.\n     */\n    public function walkDeleteStatement(AST\\DeleteStatement $deleteStatement): string\n    {\n        $this->useSqlTableAliases = false;\n        $this->rsm->isSelect      = false;\n\n        return $this->walkDeleteClause($deleteStatement->deleteClause)\n            . $this->walkWhereClause($deleteStatement->whereClause);\n    }\n\n    /**\n     * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.\n     * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.\n     */\n    public function walkEntityIdentificationVariable(string $identVariable): string\n    {\n        $class      = $this->getMetadataForDqlAlias($identVariable);\n        $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);\n        $sqlParts   = [];\n\n        foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {\n            $sqlParts[] = $tableAlias . '.' . $columnName;\n        }\n\n        return implode(', ', $sqlParts);\n    }\n\n    /**\n     * Walks down an EntityAsDtoArgumentExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkEntityAsDtoArgumentExpression(AST\\EntityAsDtoArgumentExpression $expr): string\n    {\n        return implode(', ', $this->walkObjectExpression($expr->expression, [], $expr->identificationVariable ?: null));\n    }\n\n    /**\n     * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.\n     */\n    public function walkIdentificationVariable(string $identificationVariable, string|null $fieldName = null): string\n    {\n        $class = $this->getMetadataForDqlAlias($identificationVariable);\n\n        if (\n            $fieldName !== null && $class->isInheritanceTypeJoined() &&\n            isset($class->fieldMappings[$fieldName]->inherited)\n        ) {\n            $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]->inherited);\n        }\n\n        return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);\n    }\n\n    /**\n     * Walks down a PathExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkPathExpression(AST\\PathExpression $pathExpr): string\n    {\n        $sql = '';\n        assert($pathExpr->field !== null);\n\n        switch ($pathExpr->type) {\n            case AST\\PathExpression::TYPE_STATE_FIELD:\n                $fieldName = $pathExpr->field;\n                $dqlAlias  = $pathExpr->identificationVariable;\n                $class     = $this->getMetadataForDqlAlias($dqlAlias);\n\n                if ($this->useSqlTableAliases) {\n                    $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';\n                }\n\n                $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);\n                break;\n\n            case AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:\n                // 1- the owning side:\n                //    Just use the foreign key, i.e. u.group_id\n                $fieldName = $pathExpr->field;\n                $dqlAlias  = $pathExpr->identificationVariable;\n                $class     = $this->getMetadataForDqlAlias($dqlAlias);\n\n                if (isset($class->associationMappings[$fieldName]->inherited)) {\n                    $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]->inherited);\n                }\n\n                $assoc = $class->associationMappings[$fieldName];\n\n                if (! $assoc->isOwningSide()) {\n                    throw QueryException::associationPathInverseSideNotSupported($pathExpr);\n                }\n\n                assert($assoc->isToOneOwningSide());\n\n                // COMPOSITE KEYS NOT (YET?) SUPPORTED\n                if (count($assoc->sourceToTargetKeyColumns) > 1) {\n                    throw QueryException::associationPathCompositeKeyNotSupported();\n                }\n\n                if ($this->useSqlTableAliases) {\n                    $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';\n                }\n\n                $sql .= reset($assoc->targetToSourceKeyColumns);\n                break;\n\n            default:\n                throw QueryException::invalidPathExpression($pathExpr);\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a SelectClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSelectClause(AST\\SelectClause $selectClause): string\n    {\n        $sql                  = 'SELECT ' . ($selectClause->isDistinct ? 'DISTINCT ' : '');\n        $sqlSelectExpressions = array_filter(array_map($this->walkSelectExpression(...), $selectClause->selectExpressions));\n\n        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) === true && $selectClause->isDistinct) {\n            $this->query->setHint(self::HINT_DISTINCT, true);\n        }\n\n        $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&\n            $this->query->getHydrationMode() === Query::HYDRATE_OBJECT\n            || $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);\n\n        foreach ($this->selectedClasses as $selectedClass) {\n            $class       = $selectedClass['class'];\n            $dqlAlias    = $selectedClass['dqlAlias'];\n            $resultAlias = $selectedClass['resultAlias'];\n\n            // Register as entity or joined entity result\n            if (! isset($this->queryComponents[$dqlAlias]['relation'])) {\n                $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);\n            } else {\n                assert(isset($this->queryComponents[$dqlAlias]['parent']));\n\n                $this->rsm->addJoinedEntityResult(\n                    $class->name,\n                    $dqlAlias,\n                    $this->queryComponents[$dqlAlias]['parent'],\n                    $this->queryComponents[$dqlAlias]['relation']->fieldName,\n                );\n            }\n\n            if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {\n                // Add discriminator columns to SQL\n                $rootClass   = $this->em->getClassMetadata($class->rootEntityName);\n                $tblAlias    = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);\n                $discrColumn = $rootClass->getDiscriminatorColumn();\n                $columnAlias = $this->getSQLColumnAlias($discrColumn->name);\n\n                $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn->name . ' AS ' . $columnAlias;\n\n                $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);\n                $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn->fieldName, false, $discrColumn->type);\n                if (! empty($discrColumn->enumType)) {\n                    $this->rsm->addEnumResult($columnAlias, $discrColumn->enumType);\n                }\n            }\n\n            // Add foreign key columns to SQL, if necessary\n            if (! $addMetaColumns && ! $class->containsForeignIdentifier) {\n                continue;\n            }\n\n            // Add foreign key columns of class and also parent classes\n            foreach ($class->associationMappings as $assoc) {\n                if (\n                    ! $assoc->isToOneOwningSide()\n                    || ( ! $addMetaColumns && ! isset($assoc->id))\n                ) {\n                    continue;\n                }\n\n                $targetClass   = $this->em->getClassMetadata($assoc->targetEntity);\n                $isIdentifier  = (isset($assoc->id) && $assoc->id === true);\n                $owningClass   = isset($assoc->inherited) ? $this->em->getClassMetadata($assoc->inherited) : $class;\n                $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);\n\n                foreach ($assoc->joinColumns as $joinColumn) {\n                    $columnName  = $joinColumn->name;\n                    $columnAlias = $this->getSQLColumnAlias($columnName);\n                    $columnType  = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n\n                    $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n                    $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;\n\n                    $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $isIdentifier, $columnType);\n                }\n            }\n\n            // Add foreign key columns to SQL, if necessary\n            if (! $addMetaColumns) {\n                continue;\n            }\n\n            // Add foreign key columns of subclasses\n            foreach ($class->subClasses as $subClassName) {\n                $subClass      = $this->em->getClassMetadata($subClassName);\n                $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);\n\n                foreach ($subClass->associationMappings as $assoc) {\n                    // Skip if association is inherited\n                    if (isset($assoc->inherited)) {\n                        continue;\n                    }\n\n                    if ($assoc->isToOneOwningSide()) {\n                        $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n\n                        foreach ($assoc->joinColumns as $joinColumn) {\n                            $columnName  = $joinColumn->name;\n                            $columnAlias = $this->getSQLColumnAlias($columnName);\n                            $columnType  = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);\n\n                            $quotedColumnName       = $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform);\n                            $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;\n\n                            $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $subClass->isIdentifier($columnName), $columnType);\n                        }\n                    }\n                }\n            }\n        }\n\n        return $sql . implode(', ', $sqlSelectExpressions);\n    }\n\n    /**\n     * Walks down a FromClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkFromClause(AST\\FromClause $fromClause): string\n    {\n        $identificationVarDecls = $fromClause->identificationVariableDeclarations;\n        $sqlParts               = [];\n\n        foreach ($identificationVarDecls as $identificationVariableDecl) {\n            $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl);\n        }\n\n        return ' FROM ' . implode(', ', $sqlParts);\n    }\n\n    /**\n     * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL.\n     */\n    public function walkIdentificationVariableDeclaration(AST\\IdentificationVariableDeclaration $identificationVariableDecl): string\n    {\n        $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);\n\n        if ($identificationVariableDecl->indexBy) {\n            $this->walkIndexBy($identificationVariableDecl->indexBy);\n        }\n\n        foreach ($identificationVariableDecl->joins as $join) {\n            $sql .= $this->walkJoin($join);\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a IndexBy AST node.\n     */\n    public function walkIndexBy(AST\\IndexBy $indexBy): void\n    {\n        $pathExpression = $indexBy->singleValuedPathExpression;\n        $alias          = $pathExpression->identificationVariable;\n        assert($pathExpression->field !== null);\n\n        switch ($pathExpression->type) {\n            case AST\\PathExpression::TYPE_STATE_FIELD:\n                $field = $pathExpression->field;\n                break;\n\n            case AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:\n                // Just use the foreign key, i.e. u.group_id\n                $fieldName = $pathExpression->field;\n                $class     = $this->getMetadataForDqlAlias($alias);\n\n                if (isset($class->associationMappings[$fieldName]->inherited)) {\n                    $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]->inherited);\n                }\n\n                $association = $class->associationMappings[$fieldName];\n\n                if (! $association->isOwningSide()) {\n                    throw QueryException::associationPathInverseSideNotSupported($pathExpression);\n                }\n\n                assert($association->isToOneOwningSide());\n\n                if (count($association->sourceToTargetKeyColumns) > 1) {\n                    throw QueryException::associationPathCompositeKeyNotSupported();\n                }\n\n                $field = reset($association->targetToSourceKeyColumns);\n                break;\n\n            default:\n                throw QueryException::invalidPathExpression($pathExpression);\n        }\n\n        if (isset($this->scalarFields[$alias][$field])) {\n            $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);\n\n            return;\n        }\n\n        $this->rsm->addIndexBy($alias, $field);\n    }\n\n    /**\n     * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.\n     */\n    public function walkRangeVariableDeclaration(AST\\RangeVariableDeclaration $rangeVariableDeclaration): string\n    {\n        return $this->generateRangeVariableDeclarationSQL($rangeVariableDeclaration, false);\n    }\n\n    /**\n     * Generate appropriate SQL for RangeVariableDeclaration AST node\n     */\n    private function generateRangeVariableDeclarationSQL(\n        AST\\RangeVariableDeclaration $rangeVariableDeclaration,\n        bool $buildNestedJoins,\n    ): string {\n        $class    = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);\n        $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;\n\n        if ($rangeVariableDeclaration->isRoot) {\n            $this->rootAliases[] = $dqlAlias;\n        }\n\n        $sql = $this->platform->appendLockHint(\n            $this->quoteStrategy->getTableName($class, $this->platform) . ' ' .\n            $this->getSQLTableAlias($class->getTableName(), $dqlAlias),\n            $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE,\n        );\n\n        if (! $class->isInheritanceTypeJoined()) {\n            return $sql;\n        }\n\n        $classTableInheritanceJoins = $this->generateClassTableInheritanceJoins($class, $dqlAlias);\n\n        if (! $buildNestedJoins) {\n            return $sql . $classTableInheritanceJoins;\n        }\n\n        return $classTableInheritanceJoins === '' ? $sql : '(' . $sql . $classTableInheritanceJoins . ')';\n    }\n\n    /**\n     * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.\n     *\n     * @phpstan-param AST\\Join::JOIN_TYPE_* $joinType\n     *\n     * @throws QueryException\n     */\n    public function walkJoinAssociationDeclaration(\n        AST\\JoinAssociationDeclaration $joinAssociationDeclaration,\n        int $joinType = AST\\Join::JOIN_TYPE_INNER,\n        AST\\ConditionalExpression|AST\\Phase2OptimizableConditional|null $condExpr = null,\n    ): string {\n        $sql = '';\n\n        $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;\n        $joinedDqlAlias            = $joinAssociationDeclaration->aliasIdentificationVariable;\n        $indexBy                   = $joinAssociationDeclaration->indexBy;\n\n        $relation = $this->queryComponents[$joinedDqlAlias]['relation'] ?? null;\n        assert($relation !== null);\n        $targetClass     = $this->em->getClassMetadata($relation->targetEntity);\n        $sourceClass     = $this->em->getClassMetadata($relation->sourceEntity);\n        $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);\n\n        $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);\n        $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);\n\n        // Ensure we got the owning side, since it has all mapping info\n        $assoc = $this->em->getMetadataFactory()->getOwningSide($relation);\n\n        if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) === true && (! $this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {\n            if ($relation->isToMany()) {\n                throw QueryException::iterateWithFetchJoinNotAllowed($assoc);\n            }\n        }\n\n        $fetchMode = $this->query->getHint('fetchMode')[$assoc->sourceEntity][$assoc->fieldName] ?? $relation->fetch;\n\n        if ($fetchMode === ClassMetadata::FETCH_EAGER && $condExpr !== null) {\n            throw QueryException::eagerFetchJoinWithNotAllowed($assoc->sourceEntity, $assoc->fieldName);\n        }\n\n        // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot\n        // be the owning side and previously we ensured that $assoc is always the owning side of the associations.\n        // The owning side is necessary at this point because only it contains the JoinColumn information.\n        switch (true) {\n            case $assoc->isToOne():\n                assert($assoc->isToOneOwningSide());\n                $conditions = [];\n\n                foreach ($assoc->joinColumns as $joinColumn) {\n                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);\n\n                    if ($relation->isOwningSide()) {\n                        $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;\n\n                        continue;\n                    }\n\n                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;\n                }\n\n                // Apply remaining inheritance restrictions\n                $discrSql = $this->generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);\n\n                if ($discrSql) {\n                    $conditions[] = $discrSql;\n                }\n\n                // Apply the filters\n                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);\n\n                if ($filterExpr) {\n                    $conditions[] = $filterExpr;\n                }\n\n                $targetTableJoin = [\n                    'table' => $targetTableName . ' ' . $targetTableAlias,\n                    'condition' => implode(' AND ', $conditions),\n                ];\n                break;\n\n            case $assoc->isManyToMany():\n                // Join relation table\n                $joinTable      = $assoc->joinTable;\n                $joinTableAlias = $this->getSQLTableAlias($joinTable->name, $joinedDqlAlias);\n                $joinTableName  = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform);\n\n                $conditions      = [];\n                $relationColumns = $relation->isOwningSide()\n                    ? $assoc->joinTable->joinColumns\n                    : $assoc->joinTable->inverseJoinColumns;\n\n                foreach ($relationColumns as $joinColumn) {\n                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);\n\n                    $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;\n                }\n\n                $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);\n\n                // Join target table\n                $sql .= $joinType === AST\\Join::JOIN_TYPE_LEFT || $joinType === AST\\Join::JOIN_TYPE_LEFTOUTER ? ' LEFT JOIN ' : ' INNER JOIN ';\n\n                $conditions      = [];\n                $relationColumns = $relation->isOwningSide()\n                    ? $assoc->joinTable->inverseJoinColumns\n                    : $assoc->joinTable->joinColumns;\n\n                foreach ($relationColumns as $joinColumn) {\n                    $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);\n                    $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);\n\n                    $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;\n                }\n\n                // Apply remaining inheritance restrictions\n                $discrSql = $this->generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);\n\n                if ($discrSql) {\n                    $conditions[] = $discrSql;\n                }\n\n                // Apply the filters\n                $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);\n\n                if ($filterExpr) {\n                    $conditions[] = $filterExpr;\n                }\n\n                $targetTableJoin = [\n                    'table' => $targetTableName . ' ' . $targetTableAlias,\n                    'condition' => implode(' AND ', $conditions),\n                ];\n                break;\n\n            default:\n                throw new BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY');\n        }\n\n        // Handle WITH clause\n        $withCondition = $condExpr === null ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')');\n\n        if ($targetClass->isInheritanceTypeJoined()) {\n            $ctiJoins = $this->generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);\n            // If we have WITH condition, we need to build nested joins for target class table and cti joins\n            if ($withCondition && $ctiJoins) {\n                $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];\n            } else {\n                $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;\n            }\n        } else {\n            $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'];\n        }\n\n        if ($withCondition) {\n            $sql .= ' AND ' . $withCondition;\n        }\n\n        // Apply the indexes\n        if ($indexBy) {\n            // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.\n            $this->walkIndexBy($indexBy);\n        } elseif ($relation->isIndexed()) {\n            $this->rsm->addIndexBy($joinedDqlAlias, $relation->indexBy());\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a FunctionNode AST node, thereby generating the appropriate SQL.\n     */\n    public function walkFunction(AST\\Functions\\FunctionNode $function): string\n    {\n        return $function->getSql($this);\n    }\n\n    /**\n     * Walks down an OrderByClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkOrderByClause(AST\\OrderByClause $orderByClause): string\n    {\n        $orderByItems = array_map($this->walkOrderByItem(...), $orderByClause->orderByItems);\n\n        $collectionOrderByItems = $this->generateOrderedCollectionOrderByItems();\n        if ($collectionOrderByItems !== '') {\n            $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);\n        }\n\n        return ' ORDER BY ' . implode(', ', $orderByItems);\n    }\n\n    /**\n     * Walks down an OrderByItem AST node, thereby generating the appropriate SQL.\n     */\n    public function walkOrderByItem(AST\\OrderByItem $orderByItem): string\n    {\n        $type = strtoupper($orderByItem->type);\n        $expr = $orderByItem->expression;\n        $sql  = $expr instanceof AST\\Node\n            ? $expr->dispatch($this)\n            : $this->walkResultVariable($this->queryComponents[$expr]['token']->value);\n\n        $this->orderedColumnsMap[$sql] = $type;\n\n        if ($expr instanceof AST\\Subselect) {\n            return '(' . $sql . ') ' . $type;\n        }\n\n        return $sql . ' ' . $type;\n    }\n\n    /**\n     * Walks down a HavingClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkHavingClause(AST\\HavingClause $havingClause): string\n    {\n        return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);\n    }\n\n    /**\n     * Walks down a Join AST node and creates the corresponding SQL.\n     */\n    public function walkJoin(AST\\Join $join): string\n    {\n        $joinType        = $join->joinType;\n        $joinDeclaration = $join->joinAssociationDeclaration;\n\n        $sql = $joinType === AST\\Join::JOIN_TYPE_LEFT || $joinType === AST\\Join::JOIN_TYPE_LEFTOUTER\n            ? ' LEFT JOIN '\n            : ' INNER JOIN ';\n\n        switch (true) {\n            case $joinDeclaration instanceof AST\\RangeVariableDeclaration:\n                $class      = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);\n                $dqlAlias   = $joinDeclaration->aliasIdentificationVariable;\n                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);\n                $conditions = [];\n\n                if ($join->conditionalExpression) {\n                    $conditions[] = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';\n                }\n\n                $isUnconditionalJoin = $conditions === [];\n                $condExprConjunction = $class->isInheritanceTypeJoined() && $joinType !== AST\\Join::JOIN_TYPE_LEFT && $joinType !== AST\\Join::JOIN_TYPE_LEFTOUTER && $isUnconditionalJoin\n                    ? ' AND '\n                    : ' ON ';\n\n                $sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, ! $isUnconditionalJoin);\n\n                // Apply remaining inheritance restrictions\n                $discrSql = $this->generateDiscriminatorColumnConditionSQL([$dqlAlias]);\n\n                if ($discrSql) {\n                    $conditions[] = $discrSql;\n                }\n\n                // Apply the filters\n                $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);\n\n                if ($filterExpr) {\n                    $conditions[] = $filterExpr;\n                }\n\n                if ($conditions) {\n                    $sql .= $condExprConjunction . implode(' AND ', $conditions);\n                }\n\n                break;\n\n            case $joinDeclaration instanceof AST\\JoinAssociationDeclaration:\n                $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);\n                break;\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a CoalesceExpression AST node and generates the corresponding SQL.\n     */\n    public function walkCoalesceExpression(AST\\CoalesceExpression $coalesceExpression): string\n    {\n        $sql = 'COALESCE(';\n\n        $scalarExpressions = [];\n\n        foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {\n            $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);\n        }\n\n        return $sql . implode(', ', $scalarExpressions) . ')';\n    }\n\n    /**\n     * Walks down a NullIfExpression AST node and generates the corresponding SQL.\n     */\n    public function walkNullIfExpression(AST\\NullIfExpression $nullIfExpression): string\n    {\n        $firstExpression = is_string($nullIfExpression->firstExpression)\n            ? $this->conn->quote($nullIfExpression->firstExpression)\n            : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);\n\n        $secondExpression = is_string($nullIfExpression->secondExpression)\n            ? $this->conn->quote($nullIfExpression->secondExpression)\n            : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);\n\n        return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';\n    }\n\n    /**\n     * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.\n     */\n    public function walkGeneralCaseExpression(AST\\GeneralCaseExpression $generalCaseExpression): string\n    {\n        $sql = 'CASE';\n\n        foreach ($generalCaseExpression->whenClauses as $whenClause) {\n            $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression);\n            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);\n        }\n\n        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.\n     */\n    public function walkSimpleCaseExpression(AST\\SimpleCaseExpression $simpleCaseExpression): string\n    {\n        $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);\n\n        foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {\n            $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);\n            $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);\n        }\n\n        $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a SelectExpression AST node and generates the corresponding SQL.\n     */\n    public function walkSelectExpression(AST\\SelectExpression $selectExpression): string\n    {\n        $sql    = '';\n        $expr   = $selectExpression->expression;\n        $hidden = $selectExpression->hiddenAliasResultVariable;\n\n        switch (true) {\n            case $expr instanceof AST\\PathExpression:\n                if ($expr->type !== AST\\PathExpression::TYPE_STATE_FIELD) {\n                    throw QueryException::invalidPathExpression($expr);\n                }\n\n                assert($expr->field !== null);\n                $fieldName = $expr->field;\n                $dqlAlias  = $expr->identificationVariable;\n                $class     = $this->getMetadataForDqlAlias($dqlAlias);\n\n                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;\n                $tableName   = $class->isInheritanceTypeJoined()\n                    ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)\n                    : $class->getTableName();\n\n                $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);\n                $fieldMapping  = $class->fieldMappings[$fieldName];\n                $columnName    = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);\n                $columnAlias   = $this->getSQLColumnAlias($fieldMapping->columnName);\n                $col           = $sqlTableAlias . '.' . $columnName;\n\n                $type = Type::getType($fieldMapping->type);\n                $col  = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());\n\n                $sql .= $col . ' AS ' . $columnAlias;\n\n                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;\n\n                if (! $hidden) {\n                    $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldMapping->type);\n                    $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;\n\n                    if (! empty($fieldMapping->enumType)) {\n                        $this->rsm->addEnumResult($columnAlias, $fieldMapping->enumType);\n                    }\n                }\n\n                break;\n\n            case $expr instanceof AST\\AggregateExpression:\n            case $expr instanceof AST\\Functions\\FunctionNode:\n            case $expr instanceof AST\\SimpleArithmeticExpression:\n            case $expr instanceof AST\\ArithmeticTerm:\n            case $expr instanceof AST\\ArithmeticFactor:\n            case $expr instanceof AST\\ParenthesisExpression:\n            case $expr instanceof AST\\Literal:\n            case $expr instanceof AST\\NullIfExpression:\n            case $expr instanceof AST\\CoalesceExpression:\n            case $expr instanceof AST\\GeneralCaseExpression:\n            case $expr instanceof AST\\SimpleCaseExpression:\n                $columnAlias = $this->getSQLColumnAlias('sclr');\n                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;\n\n                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;\n\n                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;\n\n                if ($hidden) {\n                    break;\n                }\n\n                if (! $expr instanceof Query\\AST\\TypedExpression) {\n                    // Conceptually we could resolve field type here by traverse through AST to retrieve field type,\n                    // but this is not a feasible solution; assume 'string'.\n                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');\n\n                    break;\n                }\n\n                $this->rsm->addScalarResult($columnAlias, $resultAlias, Type::getTypeRegistry()->lookupName($expr->getReturnType()));\n\n                break;\n\n            case $expr instanceof AST\\Subselect:\n                $columnAlias = $this->getSQLColumnAlias('sclr');\n                $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;\n\n                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;\n\n                $this->scalarResultAliasMap[$resultAlias] = $columnAlias;\n\n                if (! $hidden) {\n                    // We cannot resolve field type here; assume 'string'.\n                    $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');\n                }\n\n                break;\n\n            case $expr instanceof AST\\NewObjectExpression:\n                $sql .= $this->walkNewObject($expr, $selectExpression->fieldIdentificationVariable);\n                break;\n\n            default:\n                // IdentificationVariable or PartialObjectExpression\n                if ($expr instanceof AST\\PartialObjectExpression) {\n                    $this->query->setHint(self::HINT_PARTIAL, true);\n\n                    $dqlAlias        = $expr->identificationVariable;\n                    $partialFieldSet = $expr->partialFieldSet;\n                } else {\n                    $dqlAlias        = $expr;\n                    $partialFieldSet = [];\n                }\n\n                $sql .= implode(', ', $this->walkObjectExpression($dqlAlias, $partialFieldSet, $selectExpression->fieldIdentificationVariable ?: null));\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down an Object Expression AST node and return Sql Parts\n     *\n     * @param mixed[] $partialFieldSet\n     *\n     * @return string[]\n     */\n    public function walkObjectExpression(string $dqlAlias, array $partialFieldSet, string|null $resultAlias): array\n    {\n        $class = $this->getMetadataForDqlAlias($dqlAlias);\n\n        if (! isset($this->selectedClasses[$dqlAlias])) {\n            $this->selectedClasses[$dqlAlias] = [\n                'class'       => $class,\n                'dqlAlias'    => $dqlAlias,\n                'resultAlias' => $resultAlias,\n            ];\n        }\n\n        $sqlParts = [];\n\n        // Select all fields from the queried class\n        foreach ($class->fieldMappings as $fieldName => $mapping) {\n            if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true)) {\n                continue;\n            }\n\n            $tableName = isset($mapping->inherited)\n                ? $this->em->getClassMetadata($mapping->inherited)->getTableName()\n                : $class->getTableName();\n\n            $sqlTableAlias    = $this->getSQLTableAlias($tableName, $dqlAlias);\n            $columnAlias      = $this->getSQLColumnAlias($mapping->columnName);\n            $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);\n\n            $col = $sqlTableAlias . '.' . $quotedColumnName;\n\n            $type = Type::getType($mapping->type);\n            $col  = $type->convertToPHPValueSQL($col, $this->platform);\n\n            $sqlParts[] = $col . ' AS ' . $columnAlias;\n\n            if ($resultAlias !== null) {\n                $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;\n            }\n\n            $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);\n\n            if (! empty($mapping->enumType)) {\n                $this->rsm->addEnumResult($columnAlias, $mapping->enumType);\n            }\n        }\n\n        // Add any additional fields of subclasses (excluding inherited fields)\n        // 1) on Single Table Inheritance: always, since its marginal overhead\n        // 2) on Class Table Inheritance only if partial objects are disallowed,\n        //    since it requires outer joining subtables.\n        if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {\n            foreach ($class->subClasses as $subClassName) {\n                $subClass      = $this->em->getClassMetadata($subClassName);\n                $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);\n\n                foreach ($subClass->fieldMappings as $fieldName => $mapping) {\n                    if (isset($mapping->inherited) || ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true))) {\n                        continue;\n                    }\n\n                    $columnAlias      = $this->getSQLColumnAlias($mapping->columnName);\n                    $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform);\n\n                    $col = $sqlTableAlias . '.' . $quotedColumnName;\n\n                    $type = Type::getType($mapping->type);\n                    $col  = $type->convertToPHPValueSQL($col, $this->platform);\n\n                    $sqlParts[] = $col . ' AS ' . $columnAlias;\n\n                    if ($resultAlias !== null) {\n                        $this->scalarResultAliasMap[$resultAlias][] = $columnAlias;\n                    }\n\n                    $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);\n                }\n            }\n        }\n\n        return $sqlParts;\n    }\n\n    public function walkQuantifiedExpression(AST\\QuantifiedExpression $qExpr): string\n    {\n        return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';\n    }\n\n    /**\n     * Walks down a Subselect AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSubselect(AST\\Subselect $subselect): string\n    {\n        $useAliasesBefore  = $this->useSqlTableAliases;\n        $rootAliasesBefore = $this->rootAliases;\n\n        $this->rootAliases        = []; // reset the rootAliases for the subselect\n        $this->useSqlTableAliases = true;\n\n        $sql  = $this->walkSimpleSelectClause($subselect->simpleSelectClause);\n        $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);\n        $sql .= $this->walkWhereClause($subselect->whereClause);\n\n        $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : '';\n        $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : '';\n        $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : '';\n\n        $this->rootAliases        = $rootAliasesBefore; // put the main aliases back\n        $this->useSqlTableAliases = $useAliasesBefore;\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSubselectFromClause(AST\\SubselectFromClause $subselectFromClause): string\n    {\n        $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations;\n        $sqlParts               = [];\n\n        foreach ($identificationVarDecls as $subselectIdVarDecl) {\n            $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl);\n        }\n\n        return ' FROM ' . implode(', ', $sqlParts);\n    }\n\n    /**\n     * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSimpleSelectClause(AST\\SimpleSelectClause $simpleSelectClause): string\n    {\n        return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '')\n            . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);\n    }\n\n    public function walkParenthesisExpression(AST\\ParenthesisExpression $parenthesisExpression): string\n    {\n        return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this));\n    }\n\n    public function walkNewObject(AST\\NewObjectExpression $newObjectExpression, string|null $newObjectResultAlias = null): string\n    {\n        $sqlSelectExpressions = [];\n        $objIndex             = $newObjectResultAlias ?: $this->newObjectCounter++;\n\n        foreach ($newObjectExpression->args as $argIndex => $e) {\n            $resultAlias = $this->scalarResultCounter++;\n            $columnAlias = $this->getSQLColumnAlias('sclr');\n            $fieldType   = 'string';\n\n            switch (true) {\n                case $e instanceof AST\\NewObjectExpression:\n                    $sqlSelectExpressions[]                            = $e->dispatch($this, $columnAlias);\n                    $this->rsm->nestedNewObjectArguments[$columnAlias] = ['ownerIndex' => $objIndex, 'argIndex' => $argIndex, 'argAlias' => $columnAlias];\n                    break;\n\n                case $e instanceof AST\\Subselect:\n                    $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias;\n                    break;\n\n                case $e instanceof AST\\PathExpression:\n                    assert($e->field !== null);\n                    $dqlAlias     = $e->identificationVariable;\n                    $class        = $this->getMetadataForDqlAlias($dqlAlias);\n                    $fieldName    = $e->field;\n                    $fieldMapping = $class->fieldMappings[$fieldName];\n                    $fieldType    = $fieldMapping->type;\n                    $col          = trim($e->dispatch($this));\n\n                    $type = Type::getType($fieldType);\n                    $col  = $type->convertToPHPValueSQL($col, $this->platform);\n\n                    $sqlSelectExpressions[] = $col . ' AS ' . $columnAlias;\n\n                    if (! empty($fieldMapping->enumType)) {\n                        $this->rsm->addEnumResult($columnAlias, $fieldMapping->enumType);\n                    }\n\n                    break;\n\n                case $e instanceof AST\\Literal:\n                    switch ($e->type) {\n                        case AST\\Literal::BOOLEAN:\n                            $fieldType = 'boolean';\n                            break;\n\n                        case AST\\Literal::NUMERIC:\n                            $fieldType = is_float($e->value) ? 'float' : 'integer';\n                            break;\n                    }\n\n                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;\n                    break;\n\n                case $e instanceof AST\\EntityAsDtoArgumentExpression:\n                    $alias                                             = $e->identificationVariable ?: $columnAlias;\n                    $this->rsm->nestedNewObjectArguments[$columnAlias] = ['ownerIndex' => $objIndex, 'argIndex' => $argIndex, 'argAlias' => $alias];\n                    $this->rsm->nestedEntities[$alias]                 = ['parent' => $objIndex, 'argIndex' => $argIndex, 'type' => 'entity'];\n\n                    $sqlSelectExpressions[] = trim($e->dispatch($this));\n                    break;\n\n                default:\n                    $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;\n                    break;\n            }\n\n            $this->scalarResultAliasMap[$resultAlias] = $columnAlias;\n            $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);\n\n            $this->rsm->newObjectMappings[$columnAlias] = [\n                'objIndex'  => $objIndex,\n                'argIndex'  => $argIndex,\n            ];\n        }\n\n        $this->rsm->newObject[$objIndex] = $newObjectExpression->className;\n\n        return implode(', ', $sqlSelectExpressions);\n    }\n\n    /**\n     * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSimpleSelectExpression(AST\\SimpleSelectExpression $simpleSelectExpression): string\n    {\n        $expr = $simpleSelectExpression->expression;\n        $sql  = ' ';\n\n        switch (true) {\n            case $expr instanceof AST\\PathExpression:\n                $sql .= $this->walkPathExpression($expr);\n                break;\n\n            case $expr instanceof AST\\Subselect:\n                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;\n\n                $columnAlias                        = 'sclr' . $this->aliasCounter++;\n                $this->scalarResultAliasMap[$alias] = $columnAlias;\n\n                $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;\n                break;\n\n            case $expr instanceof AST\\Functions\\FunctionNode:\n            case $expr instanceof AST\\SimpleArithmeticExpression:\n            case $expr instanceof AST\\ArithmeticTerm:\n            case $expr instanceof AST\\ArithmeticFactor:\n            case $expr instanceof AST\\Literal:\n            case $expr instanceof AST\\NullIfExpression:\n            case $expr instanceof AST\\CoalesceExpression:\n            case $expr instanceof AST\\GeneralCaseExpression:\n            case $expr instanceof AST\\SimpleCaseExpression:\n                $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;\n\n                $columnAlias                        = $this->getSQLColumnAlias('sclr');\n                $this->scalarResultAliasMap[$alias] = $columnAlias;\n\n                $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;\n                break;\n\n            case $expr instanceof AST\\ParenthesisExpression:\n                $sql .= $this->walkParenthesisExpression($expr);\n                break;\n\n            default: // IdentificationVariable\n                $sql .= $this->walkEntityIdentificationVariable($expr);\n                break;\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkAggregateExpression(AST\\AggregateExpression $aggExpression): string\n    {\n        return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '')\n            . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';\n    }\n\n    /**\n     * Walks down a GroupByClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkGroupByClause(AST\\GroupByClause $groupByClause): string\n    {\n        $sqlParts = [];\n\n        foreach ($groupByClause->groupByItems as $groupByItem) {\n            $sqlParts[] = $this->walkGroupByItem($groupByItem);\n        }\n\n        return ' GROUP BY ' . implode(', ', $sqlParts);\n    }\n\n    /**\n     * Walks down a GroupByItem AST node, thereby generating the appropriate SQL.\n     */\n    public function walkGroupByItem(AST\\PathExpression|string $groupByItem): string\n    {\n        // StateFieldPathExpression\n        if (! is_string($groupByItem)) {\n            return $this->walkPathExpression($groupByItem);\n        }\n\n        // ResultVariable\n        if (isset($this->queryComponents[$groupByItem]['resultVariable'])) {\n            $resultVariable = $this->queryComponents[$groupByItem]['resultVariable'];\n\n            if ($resultVariable instanceof AST\\PathExpression) {\n                return $this->walkPathExpression($resultVariable);\n            }\n\n            if ($resultVariable instanceof AST\\Node && isset($resultVariable->pathExpression)) {\n                return $this->walkPathExpression($resultVariable->pathExpression);\n            }\n\n            return $this->walkResultVariable($groupByItem);\n        }\n\n        // IdentificationVariable\n        $sqlParts = [];\n\n        foreach ($this->getMetadataForDqlAlias($groupByItem)->fieldNames as $field) {\n            $item       = new AST\\PathExpression(AST\\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);\n            $item->type = AST\\PathExpression::TYPE_STATE_FIELD;\n\n            $sqlParts[] = $this->walkPathExpression($item);\n        }\n\n        foreach ($this->getMetadataForDqlAlias($groupByItem)->associationMappings as $mapping) {\n            if ($mapping->isToOneOwningSide()) {\n                $item       = new AST\\PathExpression(AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping->fieldName);\n                $item->type = AST\\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;\n\n                $sqlParts[] = $this->walkPathExpression($item);\n            }\n        }\n\n        return implode(', ', $sqlParts);\n    }\n\n    /**\n     * Walks down a DeleteClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkDeleteClause(AST\\DeleteClause $deleteClause): string\n    {\n        $class     = $this->em->getClassMetadata($deleteClause->abstractSchemaName);\n        $tableName = $class->getTableName();\n        $sql       = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform);\n\n        $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);\n        $this->rootAliases[] = $deleteClause->aliasIdentificationVariable;\n\n        return $sql;\n    }\n\n    /**\n     * Walks down an UpdateClause AST node, thereby generating the appropriate SQL.\n     */\n    public function walkUpdateClause(AST\\UpdateClause $updateClause): string\n    {\n        $class     = $this->em->getClassMetadata($updateClause->abstractSchemaName);\n        $tableName = $class->getTableName();\n        $sql       = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform);\n\n        $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);\n        $this->rootAliases[] = $updateClause->aliasIdentificationVariable;\n\n        return $sql . ' SET ' . implode(', ', array_map($this->walkUpdateItem(...), $updateClause->updateItems));\n    }\n\n    /**\n     * Walks down an UpdateItem AST node, thereby generating the appropriate SQL.\n     */\n    public function walkUpdateItem(AST\\UpdateItem $updateItem): string\n    {\n        $useTableAliasesBefore    = $this->useSqlTableAliases;\n        $this->useSqlTableAliases = false;\n\n        $sql      = $this->walkPathExpression($updateItem->pathExpression) . ' = ';\n        $newValue = $updateItem->newValue;\n\n        $sql .= match (true) {\n            $newValue instanceof AST\\Node => $newValue->dispatch($this),\n            $newValue === null => 'NULL',\n        };\n\n        $this->useSqlTableAliases = $useTableAliasesBefore;\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a WhereClause AST node, thereby generating the appropriate SQL.\n     *\n     * WhereClause or not, the appropriate discriminator sql is added.\n     */\n    public function walkWhereClause(AST\\WhereClause|null $whereClause): string\n    {\n        $condSql  = $whereClause !== null ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';\n        $discrSql = $this->generateDiscriminatorColumnConditionSQL($this->rootAliases);\n\n        if ($this->em->hasFilters()) {\n            $filterClauses = [];\n            foreach ($this->rootAliases as $dqlAlias) {\n                $class      = $this->getMetadataForDqlAlias($dqlAlias);\n                $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);\n\n                $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);\n                if ($filterExpr) {\n                    $filterClauses[] = $filterExpr;\n                }\n            }\n\n            if (count($filterClauses)) {\n                if ($condSql) {\n                    $condSql = '(' . $condSql . ') AND ';\n                }\n\n                $condSql .= implode(' AND ', $filterClauses);\n            }\n        }\n\n        if ($condSql) {\n            return ' WHERE ' . (! $discrSql ? $condSql : '(' . $condSql . ') AND ' . $discrSql);\n        }\n\n        if ($discrSql) {\n            return ' WHERE ' . $discrSql;\n        }\n\n        return '';\n    }\n\n    /**\n     * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkConditionalExpression(\n        AST\\ConditionalExpression|AST\\Phase2OptimizableConditional $condExpr,\n    ): string {\n        // Phase 2 AST optimization: Skip processing of ConditionalExpression\n        // if only one ConditionalTerm is defined\n        if (! ($condExpr instanceof AST\\ConditionalExpression)) {\n            return $this->walkConditionalTerm($condExpr);\n        }\n\n        return implode(' OR ', array_map($this->walkConditionalTerm(...), $condExpr->conditionalTerms));\n    }\n\n    /**\n     * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.\n     */\n    public function walkConditionalTerm(\n        AST\\ConditionalTerm|AST\\ConditionalPrimary|AST\\ConditionalFactor $condTerm,\n    ): string {\n        // Phase 2 AST optimization: Skip processing of ConditionalTerm\n        // if only one ConditionalFactor is defined\n        if (! ($condTerm instanceof AST\\ConditionalTerm)) {\n            return $this->walkConditionalFactor($condTerm);\n        }\n\n        return implode(' AND ', array_map($this->walkConditionalFactor(...), $condTerm->conditionalFactors));\n    }\n\n    /**\n     * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.\n     */\n    public function walkConditionalFactor(\n        AST\\ConditionalFactor|AST\\ConditionalPrimary $factor,\n    ): string {\n        // Phase 2 AST optimization: Skip processing of ConditionalFactor\n        // if only one ConditionalPrimary is defined\n        return ! ($factor instanceof AST\\ConditionalFactor)\n            ? $this->walkConditionalPrimary($factor)\n            : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary);\n    }\n\n    /**\n     * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.\n     */\n    public function walkConditionalPrimary(AST\\ConditionalPrimary $primary): string\n    {\n        if ($primary->isSimpleConditionalExpression()) {\n            return $primary->simpleConditionalExpression->dispatch($this);\n        }\n\n        if ($primary->isConditionalExpression()) {\n            $condExpr = $primary->conditionalExpression;\n\n            return '(' . $this->walkConditionalExpression($condExpr) . ')';\n        }\n\n        throw new LogicException('Unexpected state of ConditionalPrimary node.');\n    }\n\n    /**\n     * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkExistsExpression(AST\\ExistsExpression $existsExpr): string\n    {\n        $sql = $existsExpr->not ? 'NOT ' : '';\n\n        $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')';\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkCollectionMemberExpression(AST\\CollectionMemberExpression $collMemberExpr): string\n    {\n        $sql  = $collMemberExpr->not ? 'NOT ' : '';\n        $sql .= 'EXISTS (SELECT 1 FROM ';\n\n        $entityExpr   = $collMemberExpr->entityExpression;\n        $collPathExpr = $collMemberExpr->collectionValuedPathExpression;\n        assert($collPathExpr->field !== null);\n\n        $fieldName = $collPathExpr->field;\n        $dqlAlias  = $collPathExpr->identificationVariable;\n\n        $class = $this->getMetadataForDqlAlias($dqlAlias);\n\n        switch (true) {\n            // InputParameter\n            case $entityExpr instanceof AST\\InputParameter:\n                $dqlParamKey = $entityExpr->name;\n                $entitySql   = '?';\n                break;\n\n            // SingleValuedAssociationPathExpression | IdentificationVariable\n            case $entityExpr instanceof AST\\PathExpression:\n                $entitySql = $this->walkPathExpression($entityExpr);\n                break;\n\n            default:\n                throw new BadMethodCallException('Not implemented');\n        }\n\n        $assoc = $class->associationMappings[$fieldName];\n\n        if ($assoc->isOneToMany()) {\n            $targetClass      = $this->em->getClassMetadata($assoc->targetEntity);\n            $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());\n            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);\n\n            $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE ';\n\n            $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy];\n            assert($owningAssoc->isManyToOne());\n            $sqlParts = [];\n\n            foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) {\n                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform);\n\n                $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;\n            }\n\n            foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {\n                if (isset($dqlParamKey)) {\n                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);\n                }\n\n                $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;\n            }\n\n            $sql .= implode(' AND ', $sqlParts);\n        } else { // many-to-many\n            $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n\n            $owningAssoc = $this->em->getMetadataFactory()->getOwningSide($assoc);\n            assert($owningAssoc->isManyToManyOwningSide());\n            $joinTable = $owningAssoc->joinTable;\n\n            // SQL table aliases\n            $joinTableAlias   = $this->getSQLTableAlias($joinTable->name);\n            $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);\n\n            $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias . ' WHERE ';\n\n            $joinColumns = $assoc->isOwningSide() ? $joinTable->joinColumns : $joinTable->inverseJoinColumns;\n            $sqlParts    = [];\n\n            foreach ($joinColumns as $joinColumn) {\n                $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn->referencedColumnName], $class, $this->platform);\n\n                $sqlParts[] = $joinTableAlias . '.' . $joinColumn->name . ' = ' . $sourceTableAlias . '.' . $targetColumn;\n            }\n\n            $joinColumns = $assoc->isOwningSide() ? $joinTable->inverseJoinColumns : $joinTable->joinColumns;\n\n            foreach ($joinColumns as $joinColumn) {\n                if (isset($dqlParamKey)) {\n                    $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);\n                }\n\n                $sqlParts[] = $joinTableAlias . '.' . $joinColumn->name . ' IN (' . $entitySql . ')';\n            }\n\n            $sql .= implode(' AND ', $sqlParts);\n        }\n\n        return $sql . ')';\n    }\n\n    /**\n     * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkEmptyCollectionComparisonExpression(AST\\EmptyCollectionComparisonExpression $emptyCollCompExpr): string\n    {\n        $sizeFunc                           = new AST\\Functions\\SizeFunction('size');\n        $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression;\n\n        return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0');\n    }\n\n    /**\n     * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkNullComparisonExpression(AST\\NullComparisonExpression $nullCompExpr): string\n    {\n        $expression = $nullCompExpr->expression;\n        $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL';\n\n        // Handle ResultVariable\n        if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) {\n            return $this->walkResultVariable($expression) . $comparison;\n        }\n\n        // Handle InputParameter mapping inclusion to ParserResult\n        if ($expression instanceof AST\\InputParameter) {\n            return $this->walkInputParameter($expression) . $comparison;\n        }\n\n        assert(! is_string($expression));\n\n        return $expression->dispatch($this) . $comparison;\n    }\n\n    /**\n     * Walks down an InExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkInListExpression(AST\\InListExpression $inExpr): string\n    {\n        return $this->walkArithmeticExpression($inExpr->expression)\n            . ($inExpr->not ? ' NOT' : '') . ' IN ('\n            . implode(', ', array_map($this->walkInParameter(...), $inExpr->literals))\n            . ')';\n    }\n\n    /**\n     * Walks down an InExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkInSubselectExpression(AST\\InSubselectExpression $inExpr): string\n    {\n        return $this->walkArithmeticExpression($inExpr->expression)\n            . ($inExpr->not ? ' NOT' : '') . ' IN ('\n            . $this->walkSubselect($inExpr->subselect)\n            . ')';\n    }\n\n    /**\n     * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.\n     *\n     * @throws QueryException\n     */\n    public function walkInstanceOfExpression(AST\\InstanceOfExpression $instanceOfExpr): string\n    {\n        $sql = '';\n\n        $dqlAlias   = $instanceOfExpr->identificationVariable;\n        $discrClass = $class = $this->getMetadataForDqlAlias($dqlAlias);\n\n        if ($class->discriminatorColumn) {\n            $discrClass = $this->em->getClassMetadata($class->rootEntityName);\n        }\n\n        if ($this->useSqlTableAliases) {\n            $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';\n        }\n\n        $sql .= $class->getDiscriminatorColumn()->name . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');\n        $sql .= $this->getChildDiscriminatorsFromClassMetadata($discrClass, $instanceOfExpr);\n\n        return $sql;\n    }\n\n    public function walkInParameter(mixed $inParam): string\n    {\n        return $inParam instanceof AST\\InputParameter\n            ? $this->walkInputParameter($inParam)\n            : $this->walkArithmeticExpression($inParam);\n    }\n\n    /**\n     * Walks down a literal that represents an AST node, thereby generating the appropriate SQL.\n     */\n    public function walkLiteral(AST\\Literal $literal): string\n    {\n        return match ($literal->type) {\n            AST\\Literal::STRING => $this->conn->quote($literal->value),\n            AST\\Literal::BOOLEAN => (string) $this->conn->getDatabasePlatform()->convertBooleans(strtolower($literal->value) === 'true'),\n            AST\\Literal::NUMERIC => (string) $literal->value,\n            default => throw QueryException::invalidLiteral($literal),\n        };\n    }\n\n    /**\n     * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkBetweenExpression(AST\\BetweenExpression $betweenExpr): string\n    {\n        $sql = $this->walkArithmeticExpression($betweenExpr->expression);\n\n        if ($betweenExpr->not) {\n            $sql .= ' NOT';\n        }\n\n        $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)\n            . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a LikeExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkLikeExpression(AST\\LikeExpression $likeExpr): string\n    {\n        $stringExpr = $likeExpr->stringExpression;\n        if (is_string($stringExpr)) {\n            if (! isset($this->queryComponents[$stringExpr]['resultVariable'])) {\n                throw new LogicException(sprintf('No result variable found for string expression \"%s\".', $stringExpr));\n            }\n\n            $leftExpr = $this->walkResultVariable($stringExpr);\n        } else {\n            $leftExpr = $stringExpr->dispatch($this);\n        }\n\n        $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE ';\n\n        if ($likeExpr->stringPattern instanceof AST\\InputParameter) {\n            $sql .= $this->walkInputParameter($likeExpr->stringPattern);\n        } elseif ($likeExpr->stringPattern instanceof AST\\Functions\\FunctionNode) {\n            $sql .= $this->walkFunction($likeExpr->stringPattern);\n        } elseif ($likeExpr->stringPattern instanceof AST\\PathExpression) {\n            $sql .= $this->walkPathExpression($likeExpr->stringPattern);\n        } else {\n            $sql .= $this->walkLiteral($likeExpr->stringPattern);\n        }\n\n        if ($likeExpr->escapeChar) {\n            $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar);\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkStateFieldPathExpression(AST\\PathExpression $stateFieldPathExpression): string\n    {\n        return $this->walkPathExpression($stateFieldPathExpression);\n    }\n\n    /**\n     * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkComparisonExpression(AST\\ComparisonExpression $compExpr): string\n    {\n        $leftExpr  = $compExpr->leftExpression;\n        $rightExpr = $compExpr->rightExpression;\n        $sql       = '';\n\n        $sql .= $leftExpr instanceof AST\\Node\n            ? $leftExpr->dispatch($this)\n            : (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr));\n\n        $sql .= ' ' . $compExpr->operator . ' ';\n\n        $sql .= $rightExpr instanceof AST\\Node\n            ? $rightExpr->dispatch($this)\n            : (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr));\n\n        return $sql;\n    }\n\n    /**\n     * Walks down an InputParameter AST node, thereby generating the appropriate SQL.\n     */\n    public function walkInputParameter(AST\\InputParameter $inputParam): string\n    {\n        $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);\n\n        $parameter = $this->query->getParameter($inputParam->name);\n\n        if ($parameter) {\n            $type = $parameter->getType();\n            if (is_string($type) && Type::hasType($type)) {\n                return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform);\n            }\n        }\n\n        return '?';\n    }\n\n    /**\n     * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkArithmeticExpression(AST\\ArithmeticExpression $arithmeticExpr): string\n    {\n        return $arithmeticExpr->isSimpleArithmeticExpression()\n            ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)\n            : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')';\n    }\n\n    /**\n     * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.\n     */\n    public function walkSimpleArithmeticExpression(AST\\Node|string $simpleArithmeticExpr): string\n    {\n        if (! ($simpleArithmeticExpr instanceof AST\\SimpleArithmeticExpression)) {\n            return $this->walkArithmeticTerm($simpleArithmeticExpr);\n        }\n\n        return implode(' ', array_map($this->walkArithmeticTerm(...), $simpleArithmeticExpr->arithmeticTerms));\n    }\n\n    /**\n     * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.\n     */\n    public function walkArithmeticTerm(AST\\Node|string $term): string\n    {\n        if (is_string($term)) {\n            return isset($this->queryComponents[$term])\n                ? $this->walkResultVariable($this->queryComponents[$term]['token']->value)\n                : $term;\n        }\n\n        // Phase 2 AST optimization: Skip processing of ArithmeticTerm\n        // if only one ArithmeticFactor is defined\n        if (! ($term instanceof AST\\ArithmeticTerm)) {\n            return $this->walkArithmeticFactor($term);\n        }\n\n        return implode(' ', array_map($this->walkArithmeticFactor(...), $term->arithmeticFactors));\n    }\n\n    /**\n     * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.\n     */\n    public function walkArithmeticFactor(AST\\Node|string $factor): string\n    {\n        if (is_string($factor)) {\n            return isset($this->queryComponents[$factor])\n                ? $this->walkResultVariable($this->queryComponents[$factor]['token']->value)\n                : $factor;\n        }\n\n        // Phase 2 AST optimization: Skip processing of ArithmeticFactor\n        // if only one ArithmeticPrimary is defined\n        if (! ($factor instanceof AST\\ArithmeticFactor)) {\n            return $this->walkArithmeticPrimary($factor);\n        }\n\n        $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');\n\n        return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);\n    }\n\n    /**\n     * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.\n     */\n    public function walkArithmeticPrimary(AST\\Node|string $primary): string\n    {\n        if ($primary instanceof AST\\SimpleArithmeticExpression) {\n            return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';\n        }\n\n        if ($primary instanceof AST\\Node) {\n            return $primary->dispatch($this);\n        }\n\n        return $this->walkEntityIdentificationVariable($primary);\n    }\n\n    /**\n     * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.\n     */\n    public function walkStringPrimary(AST\\Node|string $stringPrimary): string\n    {\n        return is_string($stringPrimary)\n            ? $this->conn->quote($stringPrimary)\n            : $stringPrimary->dispatch($this);\n    }\n\n    /**\n     * Walks down a ResultVariable that represents an AST node, thereby generating the appropriate SQL.\n     */\n    public function walkResultVariable(string $resultVariable): string\n    {\n        if (! isset($this->scalarResultAliasMap[$resultVariable])) {\n            throw new InvalidArgumentException(sprintf('Unknown result variable: %s', $resultVariable));\n        }\n\n        $resultAlias = $this->scalarResultAliasMap[$resultVariable];\n\n        if (is_array($resultAlias)) {\n            return implode(', ', $resultAlias);\n        }\n\n        return $resultAlias;\n    }\n\n    /**\n     * @return string The list in parentheses of valid child discriminators from the given class\n     *\n     * @throws QueryException\n     */\n    private function getChildDiscriminatorsFromClassMetadata(\n        ClassMetadata $rootClass,\n        AST\\InstanceOfExpression $instanceOfExpr,\n    ): string {\n        $sqlParameterList = [];\n        $discriminators   = [];\n        foreach ($instanceOfExpr->value as $parameter) {\n            if ($parameter instanceof AST\\InputParameter) {\n                $this->rsm->discriminatorParameters[$parameter->name] = $parameter->name;\n                $sqlParameterList[]                                   = $this->walkInParameter($parameter);\n                continue;\n            }\n\n            $metadata = $this->em->getClassMetadata($parameter);\n\n            if ($metadata->getName() !== $rootClass->name && ! $metadata->getReflectionClass()->isSubclassOf($rootClass->name)) {\n                throw QueryException::instanceOfUnrelatedClass($parameter, $rootClass->name);\n            }\n\n            $discriminators += HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($metadata, $this->em);\n        }\n\n        foreach (array_keys($discriminators) as $discriminatorValue) {\n            $sqlParameterList[] = $rootClass->getDiscriminatorColumn()->type === 'integer' && is_int($discriminatorValue)\n                ? $discriminatorValue\n                : $this->conn->quote((string) $discriminatorValue);\n        }\n\n        return '(' . implode(', ', $sqlParameterList) . ')';\n    }\n}\n"
  },
  {
    "path": "src/Query/TokenType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nenum TokenType: int\n{\n    // All tokens that are not valid identifiers must be < 100\n    case T_NONE              = 1;\n    case T_INTEGER           = 2;\n    case T_STRING            = 3;\n    case T_INPUT_PARAMETER   = 4;\n    case T_FLOAT             = 5;\n    case T_CLOSE_PARENTHESIS = 6;\n    case T_OPEN_PARENTHESIS  = 7;\n    case T_COMMA             = 8;\n    case T_DIVIDE            = 9;\n    case T_DOT               = 10;\n    case T_EQUALS            = 11;\n    case T_GREATER_THAN      = 12;\n    case T_LOWER_THAN        = 13;\n    case T_MINUS             = 14;\n    case T_MULTIPLY          = 15;\n    case T_NEGATE            = 16;\n    case T_PLUS              = 17;\n    case T_OPEN_CURLY_BRACE  = 18;\n    case T_CLOSE_CURLY_BRACE = 19;\n\n    // All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100\n    case T_FULLY_QUALIFIED_NAME = 101;\n    case T_IDENTIFIER           = 102;\n\n    // All keyword tokens should be >= 200\n    case T_ALL      = 200;\n    case T_AND      = 201;\n    case T_ANY      = 202;\n    case T_AS       = 203;\n    case T_ASC      = 204;\n    case T_AVG      = 205;\n    case T_BETWEEN  = 206;\n    case T_BOTH     = 207;\n    case T_BY       = 208;\n    case T_CASE     = 209;\n    case T_COALESCE = 210;\n    case T_COUNT    = 211;\n    case T_DELETE   = 212;\n    case T_DESC     = 213;\n    case T_DISTINCT = 214;\n    case T_ELSE     = 215;\n    case T_EMPTY    = 216;\n    case T_END      = 217;\n    case T_ESCAPE   = 218;\n    case T_EXISTS   = 219;\n    case T_FALSE    = 220;\n    case T_FROM     = 221;\n    case T_GROUP    = 222;\n    case T_HAVING   = 223;\n    case T_HIDDEN   = 224;\n    case T_IN       = 225;\n    case T_INDEX    = 226;\n    case T_INNER    = 227;\n    case T_INSTANCE = 228;\n    case T_IS       = 229;\n    case T_JOIN     = 230;\n    case T_LEADING  = 231;\n    case T_LEFT     = 232;\n    case T_LIKE     = 233;\n    case T_MAX      = 234;\n    case T_MEMBER   = 235;\n    case T_MIN      = 236;\n    case T_NEW      = 237;\n    case T_NOT      = 238;\n    case T_NULL     = 239;\n    case T_NULLIF   = 240;\n    case T_OF       = 241;\n    case T_OR       = 242;\n    case T_ORDER    = 243;\n    case T_OUTER    = 244;\n    case T_PARTIAL  = 245;\n    case T_SELECT   = 246;\n    case T_SET      = 247;\n    case T_SOME     = 248;\n    case T_SUM      = 249;\n    case T_THEN     = 250;\n    case T_TRAILING = 251;\n    case T_TRUE     = 252;\n    case T_UPDATE   = 253;\n    case T_WHEN     = 254;\n    case T_WHERE    = 255;\n    case T_WITH     = 256;\n    case T_NAMED    = 257;\n    case T_ON       = 258;\n}\n"
  },
  {
    "path": "src/Query/TreeWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\AbstractQuery;\n\n/**\n * Interface for walkers of DQL ASTs (abstract syntax trees).\n *\n * @phpstan-import-type QueryComponent from Parser\n */\ninterface TreeWalker\n{\n    /**\n     * Initializes TreeWalker with important information about the ASTs to be walked.\n     *\n     * @phpstan-param array<string, QueryComponent> $queryComponents The query components (symbol table).\n     */\n    public function __construct(AbstractQuery $query, ParserResult $parserResult, array $queryComponents);\n\n    /**\n     * Returns internal queryComponents array.\n     *\n     * @phpstan-return array<string, QueryComponent>\n     */\n    public function getQueryComponents(): array;\n\n    /**\n     * Walks down a SelectStatement AST node.\n     */\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): void;\n\n    /**\n     * Walks down an UpdateStatement AST node.\n     */\n    public function walkUpdateStatement(AST\\UpdateStatement $updateStatement): void;\n\n    /**\n     * Walks down a DeleteStatement AST node.\n     */\n    public function walkDeleteStatement(AST\\DeleteStatement $deleteStatement): void;\n}\n"
  },
  {
    "path": "src/Query/TreeWalkerAdapter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse LogicException;\n\nuse function array_diff;\nuse function array_keys;\nuse function sprintf;\n\n/**\n * An adapter implementation of the TreeWalker interface. The methods in this class\n * are empty. This class exists as convenience for creating tree walkers.\n *\n * @phpstan-import-type QueryComponent from Parser\n */\nabstract class TreeWalkerAdapter implements TreeWalker\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function __construct(\n        private readonly AbstractQuery $query,\n        private readonly ParserResult $parserResult,\n        private array $queryComponents,\n    ) {\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getQueryComponents(): array\n    {\n        return $this->queryComponents;\n    }\n\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): void\n    {\n    }\n\n    public function walkUpdateStatement(AST\\UpdateStatement $updateStatement): void\n    {\n    }\n\n    public function walkDeleteStatement(AST\\DeleteStatement $deleteStatement): void\n    {\n    }\n\n    /**\n     * Sets or overrides a query component for a given dql alias.\n     *\n     * @phpstan-param QueryComponent $queryComponent\n     */\n    protected function setQueryComponent(string $dqlAlias, array $queryComponent): void\n    {\n        $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'];\n\n        if (array_diff($requiredKeys, array_keys($queryComponent))) {\n            throw QueryException::invalidQueryComponent($dqlAlias);\n        }\n\n        $this->queryComponents[$dqlAlias] = $queryComponent;\n    }\n\n    /**\n     * Retrieves the Query Instance responsible for the current walkers execution.\n     */\n    protected function _getQuery(): AbstractQuery\n    {\n        return $this->query;\n    }\n\n    /**\n     * Retrieves the ParserResult.\n     */\n    protected function _getParserResult(): ParserResult\n    {\n        return $this->parserResult;\n    }\n\n    protected function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata\n    {\n        return $this->queryComponents[$dqlAlias]['metadata']\n            ?? throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias));\n    }\n}\n"
  },
  {
    "path": "src/Query/TreeWalkerChain.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Query;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Generator;\n\n/**\n * Represents a chain of tree walkers that modify an AST and finally emit output.\n * Only the last walker in the chain can emit output. Any previous walkers can modify\n * the AST to influence the final output produced by the last walker.\n *\n * @phpstan-import-type QueryComponent from Parser\n */\nclass TreeWalkerChain implements TreeWalker\n{\n    /**\n     * The tree walkers.\n     *\n     * @var list<class-string<TreeWalker>>\n     */\n    private array $walkers = [];\n\n    /**\n     * {@inheritDoc}\n     */\n    public function __construct(\n        private readonly AbstractQuery $query,\n        private readonly ParserResult $parserResult,\n        private array $queryComponents,\n    ) {\n    }\n\n    /**\n     * Returns the internal queryComponents array.\n     *\n     * {@inheritDoc}\n     */\n    public function getQueryComponents(): array\n    {\n        return $this->queryComponents;\n    }\n\n    /**\n     * Adds a tree walker to the chain.\n     *\n     * @param class-string<TreeWalker> $walkerClass The class of the walker to instantiate.\n     */\n    public function addTreeWalker(string $walkerClass): void\n    {\n        $this->walkers[] = $walkerClass;\n    }\n\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): void\n    {\n        foreach ($this->getWalkers() as $walker) {\n            $walker->walkSelectStatement($selectStatement);\n\n            $this->queryComponents = $walker->getQueryComponents();\n        }\n    }\n\n    public function walkUpdateStatement(AST\\UpdateStatement $updateStatement): void\n    {\n        foreach ($this->getWalkers() as $walker) {\n            $walker->walkUpdateStatement($updateStatement);\n        }\n    }\n\n    public function walkDeleteStatement(AST\\DeleteStatement $deleteStatement): void\n    {\n        foreach ($this->getWalkers() as $walker) {\n            $walker->walkDeleteStatement($deleteStatement);\n        }\n    }\n\n    /** @phpstan-return Generator<int, TreeWalker> */\n    private function getWalkers(): Generator\n    {\n        foreach ($this->walkers as $walkerClass) {\n            yield new $walkerClass($this->query, $this->parserResult, $this->queryComponents);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Query.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\AST\\DeleteStatement;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\AST\\UpdateStatement;\nuse Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse Doctrine\\ORM\\Query\\OutputWalker;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Utility\\HierarchyDiscriminatorResolver;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nuse function array_keys;\nuse function array_values;\nuse function assert;\nuse function count;\nuse function get_debug_type;\nuse function in_array;\nuse function is_a;\nuse function ksort;\nuse function md5;\nuse function reset;\nuse function serialize;\nuse function sha1;\nuse function stripos;\n\n/**\n * A Query object represents a DQL query.\n *\n * @final\n */\nclass Query extends AbstractQuery\n{\n    /**\n     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.\n     */\n    public const STATE_CLEAN = 1;\n\n    /**\n     * A query object is in state DIRTY when it has DQL parts that have not yet been\n     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart\n     * is called.\n     */\n    public const STATE_DIRTY = 2;\n\n    /* Query HINTS */\n\n    /**\n     * The refresh hint turns any query into a refresh query with the result that\n     * any local changes in entities are overridden with the fetched values.\n     */\n    public const HINT_REFRESH = 'doctrine.refresh';\n\n    public const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';\n\n    public const HINT_CACHE_EVICT = 'doctrine.cache.evict';\n\n    /**\n     * Internal hint: is set to the proxy entity that is currently triggered for loading\n     */\n    public const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';\n\n    /**\n     * The forcePartialLoad query hint forces a particular query to return\n     * partial objects.\n     *\n     * @todo Rename: HINT_OPTIMIZE\n     */\n    public const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';\n\n    /**\n     * The includeMetaColumns query hint causes meta columns like foreign keys and\n     * discriminator columns to be selected and returned as part of the query result.\n     *\n     * This hint does only apply to non-object queries.\n     */\n    public const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';\n\n    /**\n     * An array of class names that implement \\Doctrine\\ORM\\Query\\TreeWalker and\n     * are iterated and executed after the DQL has been parsed into an AST.\n     */\n    public const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';\n\n    /**\n     * A string with a class name that implements \\Doctrine\\ORM\\Query\\TreeWalker\n     * and is used for generating the target SQL from any DQL AST tree.\n     */\n    public const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';\n\n    /**\n     * Marks queries as creating only read only objects.\n     *\n     * If the object retrieved from the query is already in the identity map\n     * then it does not get marked as read only if it wasn't already.\n     */\n    public const HINT_READ_ONLY = 'doctrine.readOnly';\n\n    public const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';\n\n    public const HINT_LOCK_MODE = 'doctrine.lockMode';\n\n    /**\n     * The current state of this query.\n     *\n     * @phpstan-var self::STATE_*\n     */\n    private int $state = self::STATE_DIRTY;\n\n    /**\n     * A snapshot of the parameter types the query was parsed with.\n     *\n     * @var array<string,Type>\n     */\n    private array $parsedTypes = [];\n\n    /**\n     * Cached DQL query.\n     */\n    private string|null $dql = null;\n\n    /**\n     * The parser result that holds DQL => SQL information.\n     */\n    private ParserResult $parserResult;\n\n    /**\n     * The first result to return (the \"offset\").\n     */\n    private int $firstResult = 0;\n\n    /**\n     * The maximum number of results to return (the \"limit\").\n     */\n    private int|null $maxResults = null;\n\n    /**\n     * The cache driver used for caching queries.\n     */\n    private CacheItemPoolInterface|null $queryCache = null;\n\n    /**\n     * Whether or not expire the query cache.\n     */\n    private bool $expireQueryCache = false;\n\n    /**\n     * The query cache lifetime.\n     */\n    private int|null $queryCacheTTL = null;\n\n    /**\n     * Whether to use a query cache, if available. Defaults to TRUE.\n     */\n    private bool $useQueryCache = true;\n\n    /**\n     * Gets the SQL query/queries that correspond to this DQL query.\n     *\n     * @return list<string>|string The built sql query or an array of all sql queries.\n     */\n    public function getSQL(): string|array\n    {\n        return $this->getSqlExecutor()->getSqlStatements();\n    }\n\n    /**\n     * Returns the corresponding AST for this DQL query.\n     */\n    public function getAST(): SelectStatement|UpdateStatement|DeleteStatement\n    {\n        $parser = new Parser($this);\n\n        return $parser->getAST();\n    }\n\n    protected function getResultSetMapping(): ResultSetMapping\n    {\n        // parse query or load from cache\n        if ($this->resultSetMapping === null) {\n            $this->resultSetMapping = $this->parse()->getResultSetMapping();\n        }\n\n        return $this->resultSetMapping;\n    }\n\n    /**\n     * Parses the DQL query, if necessary, and stores the parser result.\n     *\n     * Note: Populates $this->_parserResult as a side-effect.\n     */\n    private function parse(): ParserResult\n    {\n        $types = [];\n\n        foreach ($this->parameters as $parameter) {\n            /** @var Query\\Parameter $parameter */\n            $types[$parameter->getName()] = $parameter->getType();\n        }\n\n        // Return previous parser result if the query and the filter collection are both clean\n        if ($this->state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->em->isFiltersStateClean()) {\n            return $this->parserResult;\n        }\n\n        $this->state       = self::STATE_CLEAN;\n        $this->parsedTypes = $types;\n\n        $queryCache = $this->queryCache ?? $this->em->getConfiguration()->getQueryCache();\n        // Check query cache.\n        if (! ($this->useQueryCache && $queryCache)) {\n            $parser = new Parser($this);\n\n            $this->parserResult = $parser->parse();\n\n            return $this->parserResult;\n        }\n\n        $cacheItem = $queryCache->getItem($this->getQueryCacheId());\n\n        if (! $this->expireQueryCache && $cacheItem->isHit()) {\n            $cached = $cacheItem->get();\n            if ($cached instanceof ParserResult) {\n                // Cache hit.\n                $this->parserResult = $cached;\n\n                return $this->parserResult;\n            }\n        }\n\n        // Cache miss.\n        $parser = new Parser($this);\n\n        $this->parserResult = $parser->parse();\n\n        $queryCache->save($cacheItem->set($this->parserResult)->expiresAfter($this->queryCacheTTL));\n\n        return $this->parserResult;\n    }\n\n    protected function _doExecute(): Result|int\n    {\n        $executor = $this->getSqlExecutor();\n\n        if ($this->queryCacheProfile) {\n            $executor->setQueryCacheProfile($this->queryCacheProfile);\n        } else {\n            $executor->removeQueryCacheProfile();\n        }\n\n        if ($this->resultSetMapping === null) {\n            $this->resultSetMapping = $this->parserResult->getResultSetMapping();\n        }\n\n        // Prepare parameters\n        $paramMappings = $this->parserResult->getParameterMappings();\n        $paramCount    = count($this->parameters);\n        $mappingCount  = count($paramMappings);\n\n        if ($paramCount > $mappingCount) {\n            throw QueryException::tooManyParameters($mappingCount, $paramCount);\n        }\n\n        if ($paramCount < $mappingCount) {\n            throw QueryException::tooFewParameters($mappingCount, $paramCount);\n        }\n\n        // evict all cache for the entity region\n        if ($this->hasCache && isset($this->hints[self::HINT_CACHE_EVICT]) && $this->hints[self::HINT_CACHE_EVICT]) {\n            $this->evictEntityCacheRegion();\n        }\n\n        [$sqlParams, $types] = $this->processParameterMappings($paramMappings);\n\n        $this->evictResultSetCache(\n            $executor,\n            $sqlParams,\n            $types,\n            $this->em->getConnection()->getParams(),\n        );\n\n        return $executor->execute($this->em->getConnection(), $sqlParams, $types);\n    }\n\n    /**\n     * @param array<string,mixed> $sqlParams\n     * @param array<string,Type>  $types\n     * @param array<string,mixed> $connectionParams\n     */\n    private function evictResultSetCache(\n        AbstractSqlExecutor $executor,\n        array $sqlParams,\n        array $types,\n        array $connectionParams,\n    ): void {\n        if ($this->queryCacheProfile === null || ! $this->getExpireResultCache()) {\n            return;\n        }\n\n        $cache = $this->queryCacheProfile->getResultCache();\n\n        assert($cache !== null);\n\n        $statements = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array\n\n        foreach ($statements as $statement) {\n            $cacheKeys = $this->queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);\n            $cache->deleteItem(reset($cacheKeys));\n        }\n    }\n\n    /**\n     * Evict entity cache region\n     */\n    private function evictEntityCacheRegion(): void\n    {\n        $AST = $this->getAST();\n\n        if ($AST instanceof SelectStatement) {\n            throw new QueryException('The hint \"HINT_CACHE_EVICT\" is not valid for select statements.');\n        }\n\n        $className = $AST instanceof DeleteStatement\n            ? $AST->deleteClause->abstractSchemaName\n            : $AST->updateClause->abstractSchemaName;\n\n        $this->em->getCache()->evictEntityRegion($className);\n    }\n\n    /**\n     * Processes query parameter mappings.\n     *\n     * @param array<list<int>> $paramMappings\n     *\n     * @return mixed[][]\n     * @phpstan-return array{0: list<mixed>, 1: array}\n     *\n     * @throws Query\\QueryException\n     */\n    private function processParameterMappings(array $paramMappings): array\n    {\n        $sqlParams = [];\n        $types     = [];\n\n        foreach ($this->parameters as $parameter) {\n            $key = $parameter->getName();\n\n            if (! isset($paramMappings[$key])) {\n                throw QueryException::unknownParameter($key);\n            }\n\n            [$value, $type] = $this->resolveParameterValue($parameter);\n\n            foreach ($paramMappings[$key] as $position) {\n                $types[$position] = $type;\n            }\n\n            $sqlPositions = $paramMappings[$key];\n\n            // optimized multi value sql positions away for now,\n            // they are not allowed in DQL anyways.\n            $value      = [$value];\n            $countValue = count($value);\n\n            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {\n                $sqlParams[$sqlPositions[$i]] = $value[$i % $countValue];\n            }\n        }\n\n        if (count($sqlParams) !== count($types)) {\n            throw QueryException::parameterTypeMismatch();\n        }\n\n        if ($sqlParams) {\n            ksort($sqlParams);\n            $sqlParams = array_values($sqlParams);\n\n            ksort($types);\n            $types = array_values($types);\n        }\n\n        return [$sqlParams, $types];\n    }\n\n    /**\n     * @return mixed[] tuple of (value, type)\n     * @phpstan-return array{0: mixed, 1: mixed}\n     */\n    private function resolveParameterValue(Parameter $parameter): array\n    {\n        if ($parameter->typeWasSpecified()) {\n            return [$parameter->getValue(), $parameter->getType()];\n        }\n\n        $key           = $parameter->getName();\n        $originalValue = $parameter->getValue();\n        $value         = $originalValue;\n        $rsm           = $this->getResultSetMapping();\n\n        if ($value instanceof ClassMetadata && isset($rsm->metadataParameterMapping[$key])) {\n            $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);\n        }\n\n        if ($value instanceof ClassMetadata && isset($rsm->discriminatorParameters[$key])) {\n            $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->em));\n        }\n\n        $processedValue = $this->processParameterValue($value);\n\n        return [\n            $processedValue,\n            $originalValue === $processedValue\n                ? $parameter->getType()\n                : ParameterTypeInferer::inferType($processedValue),\n        ];\n    }\n\n    /**\n     * Defines a cache driver to be used for caching queries.\n     *\n     * @return $this\n     */\n    public function setQueryCache(CacheItemPoolInterface|null $queryCache): self\n    {\n        $this->queryCache = $queryCache;\n\n        return $this;\n    }\n\n    /**\n     * Defines whether the query should make use of a query cache, if available.\n     *\n     * @return $this\n     */\n    public function useQueryCache(bool $bool): self\n    {\n        $this->useQueryCache = $bool;\n\n        return $this;\n    }\n\n    /**\n     * Defines how long the query cache will be active before expire.\n     *\n     * @param int|null $timeToLive How long the cache entry is valid.\n     *\n     * @return $this\n     */\n    public function setQueryCacheLifetime(int|null $timeToLive): self\n    {\n        $this->queryCacheTTL = $timeToLive;\n\n        return $this;\n    }\n\n    /**\n     * Retrieves the lifetime of resultset cache.\n     */\n    public function getQueryCacheLifetime(): int|null\n    {\n        return $this->queryCacheTTL;\n    }\n\n    /**\n     * Defines if the query cache is active or not.\n     *\n     * @return $this\n     */\n    public function expireQueryCache(bool $expire = true): self\n    {\n        $this->expireQueryCache = $expire;\n\n        return $this;\n    }\n\n    /**\n     * Retrieves if the query cache is active or not.\n     */\n    public function getExpireQueryCache(): bool\n    {\n        return $this->expireQueryCache;\n    }\n\n    public function free(): void\n    {\n        parent::free();\n\n        $this->dql   = null;\n        $this->state = self::STATE_CLEAN;\n    }\n\n    /**\n     * Sets a DQL query string.\n     */\n    public function setDQL(string $dqlQuery): self\n    {\n        $this->dql   = $dqlQuery;\n        $this->state = self::STATE_DIRTY;\n\n        return $this;\n    }\n\n    /**\n     * Returns the DQL query that is represented by this query object.\n     */\n    public function getDQL(): string|null\n    {\n        return $this->dql;\n    }\n\n    /**\n     * Returns the state of this query object\n     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL\n     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.\n     *\n     * @see AbstractQuery::STATE_CLEAN\n     * @see AbstractQuery::STATE_DIRTY\n     *\n     * @return int The query state.\n     * @phpstan-return self::STATE_* The query state.\n     */\n    public function getState(): int\n    {\n        return $this->state;\n    }\n\n    /**\n     * Method to check if an arbitrary piece of DQL exists\n     *\n     * @param string $dql Arbitrary piece of DQL to check for.\n     */\n    public function contains(string $dql): bool\n    {\n        return stripos($this->getDQL(), $dql) !== false;\n    }\n\n    /**\n     * Sets the position of the first result to retrieve (the \"offset\").\n     *\n     * @param int $firstResult The first result to return.\n     *\n     * @return $this\n     */\n    public function setFirstResult(int $firstResult): self\n    {\n        $this->firstResult = $firstResult;\n        $this->state       = self::STATE_DIRTY;\n\n        return $this;\n    }\n\n    /**\n     * Gets the position of the first result the query object was set to retrieve (the \"offset\").\n     * Returns 0 if {@link setFirstResult} was not applied to this query.\n     *\n     * @return int The position of the first result.\n     */\n    public function getFirstResult(): int\n    {\n        return $this->firstResult;\n    }\n\n    /**\n     * Sets the maximum number of results to retrieve (the \"limit\").\n     *\n     * @return $this\n     */\n    public function setMaxResults(int|null $maxResults): self\n    {\n        $this->maxResults = $maxResults;\n        $this->state      = self::STATE_DIRTY;\n\n        return $this;\n    }\n\n    /**\n     * Gets the maximum number of results the query object was set to retrieve (the \"limit\").\n     * Returns NULL if {@link setMaxResults} was not applied to this query.\n     *\n     * @return int|null Maximum number of results.\n     */\n    public function getMaxResults(): int|null\n    {\n        return $this->maxResults;\n    }\n\n    /** {@inheritDoc} */\n    public function toIterable(iterable $parameters = [], $hydrationMode = self::HYDRATE_OBJECT): iterable\n    {\n        $this->setHint(self::HINT_INTERNAL_ITERATION, true);\n\n        return parent::toIterable($parameters, $hydrationMode);\n    }\n\n    public function setHint(string $name, mixed $value): static\n    {\n        $this->state = self::STATE_DIRTY;\n\n        return parent::setHint($name, $value);\n    }\n\n    public function setHydrationMode(string|int $hydrationMode): static\n    {\n        $this->state = self::STATE_DIRTY;\n\n        return parent::setHydrationMode($hydrationMode);\n    }\n\n    /**\n     * Set the lock mode for this Query.\n     *\n     * @see \\Doctrine\\DBAL\\LockMode\n     *\n     * @phpstan-param LockMode::* $lockMode\n     *\n     * @return $this\n     *\n     * @throws TransactionRequiredException\n     */\n    public function setLockMode(LockMode|int $lockMode): self\n    {\n        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {\n            if (! $this->em->getConnection()->isTransactionActive()) {\n                throw TransactionRequiredException::transactionRequired();\n            }\n        }\n\n        $this->setHint(self::HINT_LOCK_MODE, $lockMode);\n\n        return $this;\n    }\n\n    /**\n     * Get the current lock mode for this query.\n     *\n     * @return LockMode|int|null The current lock mode of this query or NULL if no specific lock mode is set.\n     * @phpstan-return LockMode::*|null\n     */\n    public function getLockMode(): LockMode|int|null\n    {\n        $lockMode = $this->getHint(self::HINT_LOCK_MODE);\n\n        if ($lockMode === false) {\n            return null;\n        }\n\n        return $lockMode;\n    }\n\n    /**\n     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.\n     */\n    protected function getQueryCacheId(): string\n    {\n        ksort($this->hints);\n\n        if (! $this->hasHint(self::HINT_CUSTOM_OUTPUT_WALKER)) {\n            // Assume Parser will create the SqlOutputWalker; save is_a call, which might trigger a class load\n            $firstAndMaxResult = '';\n        } else {\n            $outputWalkerClass = $this->getHint(self::HINT_CUSTOM_OUTPUT_WALKER);\n            if (is_a($outputWalkerClass, OutputWalker::class, true)) {\n                $firstAndMaxResult = '';\n            } else {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/pull/11188/',\n                    'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.',\n                    $outputWalkerClass,\n                    OutputWalker::class,\n                    SqlFinalizer::class,\n                );\n                $firstAndMaxResult = '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults;\n            }\n        }\n\n        return md5(\n            $this->getDQL() . serialize($this->hints) .\n            '&platform=' . get_debug_type($this->getEntityManager()->getConnection()->getDatabasePlatform()) .\n            ($this->em->hasFilters() ? $this->em->getFilters()->getHash() : '') .\n            $firstAndMaxResult .\n            '&hydrationMode=' . $this->hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT',\n        );\n    }\n\n    protected function getHash(): string\n    {\n        return sha1(parent::getHash() . '-' . $this->firstResult . '-' . $this->maxResults);\n    }\n\n    /**\n     * Cleanup Query resource when clone is called.\n     */\n    public function __clone()\n    {\n        parent::__clone();\n\n        $this->state = self::STATE_DIRTY;\n    }\n\n    private function getSqlExecutor(): AbstractSqlExecutor\n    {\n        return $this->parse()->prepareSqlExecutor($this);\n    }\n}\n"
  },
  {
    "path": "src/QueryBuilder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Internal\\NoUnknownNamedArguments;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\QueryExpressionVisitor;\nuse InvalidArgumentException;\nuse RuntimeException;\nuse Stringable;\n\nuse function array_keys;\nuse function array_unshift;\nuse function assert;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function is_array;\nuse function is_numeric;\nuse function is_object;\nuse function is_string;\nuse function key;\nuse function reset;\nuse function sprintf;\nuse function str_starts_with;\nuse function strpos;\nuse function strrpos;\nuse function substr;\n\n/**\n * This class is responsible for building DQL query strings via an object oriented\n * PHP interface.\n */\nclass QueryBuilder implements Stringable\n{\n    use NoUnknownNamedArguments;\n\n    /**\n     * The array of DQL parts collected.\n     *\n     * @phpstan-var array<string, mixed>\n     */\n    private array $dqlParts = [\n        'distinct' => false,\n        'select'  => [],\n        'from'    => [],\n        'join'    => [],\n        'set'     => [],\n        'where'   => null,\n        'groupBy' => [],\n        'having'  => null,\n        'orderBy' => [],\n    ];\n\n    private QueryType $type = QueryType::Select;\n\n    /**\n     * The complete DQL string for this query.\n     */\n    private string|null $dql = null;\n\n    /**\n     * The query parameters.\n     *\n     * @phpstan-var ArrayCollection<int, Parameter>\n     */\n    private ArrayCollection $parameters;\n\n    /**\n     * The index of the first result to retrieve.\n     */\n    private int $firstResult = 0;\n\n    /**\n     * The maximum number of results to retrieve.\n     */\n    private int|null $maxResults = null;\n\n    /**\n     * Keeps root entity alias names for join entities.\n     *\n     * @phpstan-var array<string, string>\n     */\n    private array $joinRootAliases = [];\n\n    /**\n     * Whether to use second level cache, if available.\n     */\n    protected bool $cacheable = false;\n\n    /**\n     * Second level cache region name.\n     */\n    protected string|null $cacheRegion = null;\n\n    /**\n     * Second level query cache mode.\n     *\n     * @phpstan-var Cache::MODE_*|null\n     */\n    protected int|null $cacheMode = null;\n\n    protected int $lifetime = 0;\n\n    /**\n     * The counter of bound parameters.\n     *\n     * @var int<0, max>\n     */\n    private int $boundCounter = 0;\n\n    /**\n     * The hints to set on the query.\n     *\n     * @var array<string, string|int|bool|iterable<mixed>|object>\n     */\n    private array $hints = [];\n\n    /**\n     * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.\n     *\n     * @param EntityManagerInterface $em The EntityManager to use.\n     */\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->parameters = new ArrayCollection();\n    }\n\n    final protected function getType(): QueryType\n    {\n        return $this->type;\n    }\n\n    /**\n     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.\n     * This producer method is intended for convenient inline usage. Example:\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder();\n     *     $qb\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where($qb->expr()->eq('u.id', 1));\n     * </code>\n     *\n     * For more complex expression construction, consider storing the expression\n     * builder object in a local variable.\n     */\n    public function expr(): Expr\n    {\n        return $this->em->getExpressionBuilder();\n    }\n\n    /**\n     * Enable/disable second level query (result) caching for this query.\n     *\n     * @return $this\n     */\n    public function setCacheable(bool $cacheable): static\n    {\n        $this->cacheable = $cacheable;\n\n        return $this;\n    }\n\n    /**\n     * Are the query results enabled for second level cache?\n     */\n    public function isCacheable(): bool\n    {\n        return $this->cacheable;\n    }\n\n    /** @return $this */\n    public function setCacheRegion(string $cacheRegion): static\n    {\n        $this->cacheRegion = $cacheRegion;\n\n        return $this;\n    }\n\n    /**\n     * Obtain the name of the second level query cache region in which query results will be stored\n     *\n     * @return string|null The cache region name; NULL indicates the default region.\n     */\n    public function getCacheRegion(): string|null\n    {\n        return $this->cacheRegion;\n    }\n\n    public function getLifetime(): int\n    {\n        return $this->lifetime;\n    }\n\n    /**\n     * Sets the life-time for this query into second level cache.\n     *\n     * @return $this\n     */\n    public function setLifetime(int $lifetime): static\n    {\n        $this->lifetime = $lifetime;\n\n        return $this;\n    }\n\n    /** @return array<string, string|int|bool|iterable<mixed>|object> */\n    public function getHints(): array\n    {\n        return $this->hints;\n    }\n\n    /**\n     * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.\n     *\n     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.\n     */\n    public function getHint(string $name): mixed\n    {\n        return $this->hints[$name] ?? false;\n    }\n\n    public function hasHint(string $name): bool\n    {\n        return isset($this->hints[$name]);\n    }\n\n    /**\n     * Adds hints for the query.\n     *\n     * @return $this\n     */\n    public function setHint(string $name, mixed $value): static\n    {\n        $this->hints[$name] = $value;\n\n        return $this;\n    }\n\n    /** @phpstan-return Cache::MODE_*|null */\n    public function getCacheMode(): int|null\n    {\n        return $this->cacheMode;\n    }\n\n    /**\n     * @phpstan-param Cache::MODE_* $cacheMode\n     *\n     * @return $this\n     */\n    public function setCacheMode(int $cacheMode): static\n    {\n        $this->cacheMode = $cacheMode;\n\n        return $this;\n    }\n\n    /**\n     * Gets the associated EntityManager for this query builder.\n     */\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    /**\n     * Gets the complete DQL string formed by the current specifications of this QueryBuilder.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u');\n     *     echo $qb->getDql(); // SELECT u FROM User u\n     * </code>\n     */\n    public function getDQL(): string\n    {\n        return $this->dql ??= match ($this->type) {\n            QueryType::Select => $this->getDQLForSelect(),\n            QueryType::Delete => $this->getDQLForDelete(),\n            QueryType::Update => $this->getDQLForUpdate(),\n        };\n    }\n\n    /**\n     * Constructs a Query instance from the current specifications of the builder.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u');\n     *     $q = $qb->getQuery();\n     *     $results = $q->execute();\n     * </code>\n     */\n    public function getQuery(): Query\n    {\n        $parameters = clone $this->parameters;\n        $query      = $this->em->createQuery($this->getDQL())\n            ->setParameters($parameters)\n            ->setFirstResult($this->firstResult)\n            ->setMaxResults($this->maxResults);\n\n        if ($this->lifetime) {\n            $query->setLifetime($this->lifetime);\n        }\n\n        if ($this->cacheMode) {\n            $query->setCacheMode($this->cacheMode);\n        }\n\n        if ($this->cacheable) {\n            $query->setCacheable($this->cacheable);\n        }\n\n        if ($this->cacheRegion) {\n            $query->setCacheRegion($this->cacheRegion);\n        }\n\n        foreach ($this->hints as $name => $value) {\n            $query->setHint($name, $value);\n        }\n\n        return $query;\n    }\n\n    /**\n     * Finds the root entity alias of the joined entity.\n     *\n     * @param string $alias       The alias of the new join entity\n     * @param string $parentAlias The parent entity alias of the join relationship\n     */\n    private function findRootAlias(string $alias, string $parentAlias): string\n    {\n        if (in_array($parentAlias, $this->getRootAliases(), true)) {\n            $rootAlias = $parentAlias;\n        } elseif (isset($this->joinRootAliases[$parentAlias])) {\n            $rootAlias = $this->joinRootAliases[$parentAlias];\n        } else {\n            // Should never happen with correct joining order. Might be\n            // thoughtful to throw exception instead.\n            $aliases = $this->getRootAliases();\n\n            if (! isset($aliases[0])) {\n                throw new RuntimeException('No alias was set before invoking getRootAlias().');\n            }\n\n            $rootAlias = $aliases[0];\n        }\n\n        $this->joinRootAliases[$alias] = $rootAlias;\n\n        return $rootAlias;\n    }\n\n    /**\n     * Gets the FIRST root alias of the query. This is the first entity alias involved\n     * in the construction of the query.\n     *\n     * <code>\n     * $qb = $em->createQueryBuilder()\n     *     ->select('u')\n     *     ->from('User', 'u');\n     *\n     * echo $qb->getRootAlias(); // u\n     * </code>\n     *\n     * @deprecated Please use $qb->getRootAliases() instead.\n     *\n     * @throws RuntimeException\n     */\n    public function getRootAlias(): string\n    {\n        $aliases = $this->getRootAliases();\n\n        if (! isset($aliases[0])) {\n            throw new RuntimeException('No alias was set before invoking getRootAlias().');\n        }\n\n        return $aliases[0];\n    }\n\n    /**\n     * Gets the root aliases of the query. This is the entity aliases involved\n     * in the construction of the query.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u');\n     *\n     *     $qb->getRootAliases(); // array('u')\n     * </code>\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    public function getRootAliases(): array\n    {\n        $aliases = [];\n\n        foreach ($this->dqlParts['from'] as &$fromClause) {\n            if (is_string($fromClause)) {\n                $spacePos = strrpos($fromClause, ' ');\n\n                /** @phpstan-var class-string $from */\n                $from  = substr($fromClause, 0, $spacePos);\n                $alias = substr($fromClause, $spacePos + 1);\n\n                $fromClause = new Query\\Expr\\From($from, $alias);\n            }\n\n            $aliases[] = $fromClause->getAlias();\n        }\n\n        return $aliases;\n    }\n\n    /**\n     * Gets all the aliases that have been used in the query.\n     * Including all select root aliases and join aliases\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->join('u.articles','a');\n     *\n     *     $qb->getAllAliases(); // array('u','a')\n     * </code>\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    public function getAllAliases(): array\n    {\n        return [...$this->getRootAliases(), ...array_keys($this->joinRootAliases)];\n    }\n\n    /**\n     * Gets the root entities of the query. This is the entity classes involved\n     * in the construction of the query.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u');\n     *\n     *     $qb->getRootEntities(); // array('User')\n     * </code>\n     *\n     * @return string[]\n     * @phpstan-return list<class-string>\n     */\n    public function getRootEntities(): array\n    {\n        $entities = [];\n\n        foreach ($this->dqlParts['from'] as &$fromClause) {\n            if (is_string($fromClause)) {\n                $spacePos = strrpos($fromClause, ' ');\n\n                /** @phpstan-var class-string $from */\n                $from  = substr($fromClause, 0, $spacePos);\n                $alias = substr($fromClause, $spacePos + 1);\n\n                $fromClause = new Query\\Expr\\From($from, $alias);\n            }\n\n            $entities[] = $fromClause->getFrom();\n        }\n\n        return $entities;\n    }\n\n    /**\n     * Sets a query parameter for the query being constructed.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where('u.id = :user_id')\n     *         ->setParameter('user_id', 1);\n     * </code>\n     *\n     * @param string|int                                       $key  The parameter position or name.\n     * @param ParameterType|ArrayParameterType|string|int|null $type ParameterType::*, ArrayParameterType::* or \\Doctrine\\DBAL\\Types\\Type::* constant\n     *\n     * @return $this\n     */\n    public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static\n    {\n        $existingParameter = $this->getParameter($key);\n\n        if ($existingParameter !== null) {\n            $existingParameter->setValue($value, $type);\n\n            return $this;\n        }\n\n        $this->parameters->add(new Parameter($key, $value, $type));\n\n        return $this;\n    }\n\n    /**\n     * Sets a collection of query parameters for the query being constructed.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where('u.id = :user_id1 OR u.id = :user_id2')\n     *         ->setParameters(new ArrayCollection(array(\n     *             new Parameter('user_id1', 1),\n     *             new Parameter('user_id2', 2)\n     *        )));\n     * </code>\n     *\n     * @phpstan-param ArrayCollection<int, Parameter> $parameters\n     *\n     * @return $this\n     */\n    public function setParameters(ArrayCollection $parameters): static\n    {\n        $this->parameters = $parameters;\n\n        return $this;\n    }\n\n    /**\n     * Gets all defined query parameters for the query being constructed.\n     *\n     * @phpstan-return ArrayCollection<int, Parameter>\n     */\n    public function getParameters(): ArrayCollection\n    {\n        return $this->parameters;\n    }\n\n    /**\n     * Gets a (previously set) query parameter of the query being constructed.\n     */\n    public function getParameter(string|int $key): Parameter|null\n    {\n        $key = Parameter::normalizeName($key);\n\n        $filteredParameters = $this->parameters->filter(\n            static fn (Parameter $parameter): bool => $key === $parameter->getName(),\n        );\n\n        return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;\n    }\n\n    /**\n     * Sets the position of the first result to retrieve (the \"offset\").\n     *\n     * @return $this\n     */\n    public function setFirstResult(int|null $firstResult): static\n    {\n        $this->firstResult = (int) $firstResult;\n\n        return $this;\n    }\n\n    /**\n     * Gets the position of the first result the query object was set to retrieve (the \"offset\").\n     */\n    public function getFirstResult(): int\n    {\n        return $this->firstResult;\n    }\n\n    /**\n     * Sets the maximum number of results to retrieve (the \"limit\").\n     *\n     * @return $this\n     */\n    public function setMaxResults(int|null $maxResults): static\n    {\n        if ($this->type === QueryType::Delete || $this->type === QueryType::Update) {\n            throw new RuntimeException('Setting a limit is not supported for delete or update queries.');\n        }\n\n        $this->maxResults = $maxResults;\n\n        return $this;\n    }\n\n    /**\n     * Gets the maximum number of results the query object was set to retrieve (the \"limit\").\n     * Returns NULL if {@link setMaxResults} was not applied to this query builder.\n     */\n    public function getMaxResults(): int|null\n    {\n        return $this->maxResults;\n    }\n\n    /**\n     * Either appends to or replaces a single, generic query part.\n     *\n     * The available parts are: 'select', 'from', 'join', 'set', 'where',\n     * 'groupBy', 'having' and 'orderBy'.\n     *\n     * @phpstan-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart\n     *\n     * @return $this\n     */\n    public function add(string $dqlPartName, string|object|array $dqlPart, bool $append = false): static\n    {\n        if ($append && ($dqlPartName === 'where' || $dqlPartName === 'having')) {\n            throw new InvalidArgumentException(\n                \"Using \\$append = true does not have an effect with 'where' or 'having' \" .\n                'parts. See QueryBuilder#andWhere() for an example for correct usage.',\n            );\n        }\n\n        $isMultiple = is_array($this->dqlParts[$dqlPartName])\n            && ! ($dqlPartName === 'join' && ! $append);\n\n        // Allow adding any part retrieved from self::getDQLParts().\n        if (is_array($dqlPart) && $dqlPartName !== 'join') {\n            $dqlPart = reset($dqlPart);\n        }\n\n        if ($dqlPartName === 'join') {\n            $newDqlPart = [];\n\n            foreach ($dqlPart as $k => $v) {\n                if (is_numeric($k)) {\n                    Deprecation::trigger(\n                        'doctrine/orm',\n                        'https://github.com/doctrine/orm/pull/12051',\n                        'Using numeric keys in %s for join parts is deprecated and will not be supported in 4.0. Use an associative array with the root alias as key instead.',\n                        __METHOD__,\n                    );\n                    $aliases = $this->getRootAliases();\n\n                    if (! isset($aliases[0])) {\n                        throw new RuntimeException('No alias was set before invoking add().');\n                    }\n\n                    $k = $aliases[0];\n                }\n\n                $newDqlPart[$k] = $v;\n            }\n\n            $dqlPart = $newDqlPart;\n        }\n\n        if ($append && $isMultiple) {\n            if (is_array($dqlPart)) {\n                $key = key($dqlPart);\n\n                $this->dqlParts[$dqlPartName][$key][] = $dqlPart[$key];\n            } else {\n                $this->dqlParts[$dqlPartName][] = $dqlPart;\n            }\n        } else {\n            $this->dqlParts[$dqlPartName] = $isMultiple ? [$dqlPart] : $dqlPart;\n        }\n\n        $this->dql = null;\n\n        return $this;\n    }\n\n    /**\n     * Specifies an item that is to be returned in the query result.\n     * Replaces any previously specified selections, if any.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u', 'p')\n     *         ->from('User', 'u')\n     *         ->leftJoin('u.Phonenumbers', 'p');\n     * </code>\n     *\n     * @return $this\n     */\n    public function select(mixed ...$select): static\n    {\n        self::validateVariadicParameter($select);\n\n        $this->type = QueryType::Select;\n\n        if ($select === []) {\n            return $this;\n        }\n\n        return $this->add('select', new Expr\\Select($select), false);\n    }\n\n    /**\n     * Adds a DISTINCT flag to this query.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->distinct()\n     *         ->from('User', 'u');\n     * </code>\n     *\n     * @return $this\n     */\n    public function distinct(bool $flag = true): static\n    {\n        if ($this->dqlParts['distinct'] !== $flag) {\n            $this->dqlParts['distinct'] = $flag;\n            $this->dql                  = null;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an item that is to be returned in the query result.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->addSelect('p')\n     *         ->from('User', 'u')\n     *         ->leftJoin('u.Phonenumbers', 'p');\n     * </code>\n     *\n     * @return $this\n     */\n    public function addSelect(mixed ...$select): static\n    {\n        self::validateVariadicParameter($select);\n\n        $this->type = QueryType::Select;\n\n        if ($select === []) {\n            return $this;\n        }\n\n        return $this->add('select', new Expr\\Select($select), true);\n    }\n\n    /**\n     * Turns the query being built into a bulk delete query that ranges over\n     * a certain entity type.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->delete('User', 'u')\n     *         ->where('u.id = :user_id')\n     *         ->setParameter('user_id', 1);\n     * </code>\n     *\n     * @param class-string|null $delete The class/type whose instances are subject to the deletion.\n     * @param string|null       $alias  The class/type alias used in the constructed query.\n     *\n     * @return $this\n     */\n    public function delete(string|null $delete = null, string|null $alias = null): static\n    {\n        $this->type = QueryType::Delete;\n\n        if (! $delete) {\n            return $this;\n        }\n\n        if (! $alias) {\n            throw new InvalidArgumentException(sprintf(\n                '%s(): The alias for entity %s must not be omitted.',\n                __METHOD__,\n                $delete,\n            ));\n        }\n\n        return $this->add('from', new Expr\\From($delete, $alias));\n    }\n\n    /**\n     * Turns the query being built into a bulk update query that ranges over\n     * a certain entity type.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->update('User', 'u')\n     *         ->set('u.password', '?1')\n     *         ->where('u.id = ?2');\n     * </code>\n     *\n     * @param class-string|null $update The class/type whose instances are subject to the update.\n     * @param string|null       $alias  The class/type alias used in the constructed query.\n     *\n     * @return $this\n     */\n    public function update(string|null $update = null, string|null $alias = null): static\n    {\n        $this->type = QueryType::Update;\n\n        if (! $update) {\n            return $this;\n        }\n\n        if (! $alias) {\n            throw new InvalidArgumentException(sprintf(\n                '%s(): The alias for entity %s must not be omitted.',\n                __METHOD__,\n                $update,\n            ));\n        }\n\n        return $this->add('from', new Expr\\From($update, $alias));\n    }\n\n    /**\n     * Creates and adds a query root corresponding to the entity identified by the given alias,\n     * forming a cartesian product with any existing query roots.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u');\n     * </code>\n     *\n     * @param class-string $from    The class name.\n     * @param string       $alias   The alias of the class.\n     * @param string|null  $indexBy The index for the from.\n     *\n     * @return $this\n     */\n    public function from(string $from, string $alias, string|null $indexBy = null): static\n    {\n        return $this->add('from', new Expr\\From($from, $alias, $indexBy), true);\n    }\n\n    /**\n     * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with\n     * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it\n     * setting an index by.\n     *\n     * <code>\n     *     $qb = $userRepository->createQueryBuilder('u')\n     *         ->indexBy('u', 'u.id');\n     *\n     *     // Is equivalent to...\n     *\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u', 'u.id');\n     * </code>\n     *\n     * @return $this\n     *\n     * @throws Query\\QueryException\n     */\n    public function indexBy(string $alias, string $indexBy): static\n    {\n        $rootAliases = $this->getRootAliases();\n\n        if (! in_array($alias, $rootAliases, true)) {\n            throw new Query\\QueryException(\n                sprintf('Specified root alias %s must be set before invoking indexBy().', $alias),\n            );\n        }\n\n        foreach ($this->dqlParts['from'] as &$fromClause) {\n            assert($fromClause instanceof Expr\\From);\n            if ($fromClause->getAlias() !== $alias) {\n                continue;\n            }\n\n            $fromClause = new Expr\\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Creates and adds a join over an entity association to the query.\n     *\n     * The entities in the joined association will be fetched as part of the query\n     * result if the alias used for the joined association is placed in the select\n     * expressions.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->join('u.Phonenumbers', 'p', Expr\\Join::WITH, 'p.is_primary = 1');\n     * </code>\n     *\n     * @phpstan-param Expr\\Join::ON|Expr\\Join::WITH|null $conditionType\n     *\n     * @return $this\n     */\n    public function join(\n        string $join,\n        string $alias,\n        string|null $conditionType = null,\n        string|Expr\\Composite|Expr\\Comparison|Expr\\Func|null $condition = null,\n        string|null $indexBy = null,\n    ): static {\n        return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);\n    }\n\n    /**\n     * Creates and adds a join over an entity association to the query.\n     *\n     * The entities in the joined association will be fetched as part of the query\n     * result if the alias used for the joined association is placed in the select\n     * expressions.\n     *\n     *     [php]\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->innerJoin('u.Phonenumbers', 'p', Expr\\Join::WITH, 'p.is_primary = 1');\n     *\n     * @phpstan-param Expr\\Join::ON|Expr\\Join::WITH|null $conditionType\n     *\n     * @return $this\n     */\n    public function innerJoin(\n        string $join,\n        string $alias,\n        string|null $conditionType = null,\n        string|Expr\\Composite|Expr\\Comparison|Expr\\Func|null $condition = null,\n        string|null $indexBy = null,\n    ): static {\n        $parentAlias = substr($join, 0, (int) strpos($join, '.'));\n\n        $rootAlias = $this->findRootAlias($alias, $parentAlias);\n\n        $join = new Expr\\Join(\n            Expr\\Join::INNER_JOIN,\n            $join,\n            $alias,\n            $conditionType,\n            $condition,\n            $indexBy,\n        );\n\n        return $this->add('join', [$rootAlias => $join], true);\n    }\n\n    /**\n     * Creates and adds a left join over an entity association to the query.\n     *\n     * The entities in the joined association will be fetched as part of the query\n     * result if the alias used for the joined association is placed in the select\n     * expressions.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->leftJoin('u.Phonenumbers', 'p', Expr\\Join::WITH, 'p.is_primary = 1');\n     * </code>\n     *\n     * @phpstan-param Expr\\Join::ON|Expr\\Join::WITH|null $conditionType\n     *\n     * @return $this\n     */\n    public function leftJoin(\n        string $join,\n        string $alias,\n        string|null $conditionType = null,\n        string|Expr\\Composite|Expr\\Comparison|Expr\\Func|null $condition = null,\n        string|null $indexBy = null,\n    ): static {\n        $parentAlias = substr($join, 0, (int) strpos($join, '.'));\n\n        $rootAlias = $this->findRootAlias($alias, $parentAlias);\n\n        $join = new Expr\\Join(\n            Expr\\Join::LEFT_JOIN,\n            $join,\n            $alias,\n            $conditionType,\n            $condition,\n            $indexBy,\n        );\n\n        return $this->add('join', [$rootAlias => $join], true);\n    }\n\n    /**\n     * Sets a new value for a field in a bulk update query.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->update('User', 'u')\n     *         ->set('u.password', '?1')\n     *         ->where('u.id = ?2');\n     * </code>\n     *\n     * @return $this\n     */\n    public function set(string $key, mixed $value): static\n    {\n        return $this->add('set', new Expr\\Comparison($key, Expr\\Comparison::EQ, $value), true);\n    }\n\n    /**\n     * Specifies one or more restrictions to the query result.\n     * Replaces any previously specified restrictions, if any.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where('u.id = ?');\n     *\n     *     // You can optionally programmatically build and/or expressions\n     *     $qb = $em->createQueryBuilder();\n     *\n     *     $or = $qb->expr()->orX();\n     *     $or->add($qb->expr()->eq('u.id', 1));\n     *     $or->add($qb->expr()->eq('u.id', 2));\n     *\n     *     $qb->update('User', 'u')\n     *         ->set('u.password', '?')\n     *         ->where($or);\n     * </code>\n     *\n     * @return $this\n     */\n    public function where(mixed ...$predicates): static\n    {\n        self::validateVariadicParameter($predicates);\n\n        if (! (count($predicates) === 1 && $predicates[0] instanceof Expr\\Composite)) {\n            $predicates = new Expr\\Andx($predicates);\n        }\n\n        return $this->add('where', $predicates);\n    }\n\n    /**\n     * Adds one or more restrictions to the query results, forming a logical\n     * conjunction with any previously specified restrictions.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where('u.username LIKE ?')\n     *         ->andWhere('u.is_active = 1');\n     * </code>\n     *\n     * @see where()\n     *\n     * @return $this\n     */\n    public function andWhere(mixed ...$where): static\n    {\n        self::validateVariadicParameter($where);\n\n        $dql = $this->getDQLPart('where');\n\n        if ($dql instanceof Expr\\Andx) {\n            $dql->addMultiple($where);\n        } else {\n            array_unshift($where, $dql);\n            $dql = new Expr\\Andx($where);\n        }\n\n        return $this->add('where', $dql);\n    }\n\n    /**\n     * Adds one or more restrictions to the query results, forming a logical\n     * disjunction with any previously specified restrictions.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->where('u.id = 1')\n     *         ->orWhere('u.id = 2');\n     * </code>\n     *\n     * @see where()\n     *\n     * @return $this\n     */\n    public function orWhere(mixed ...$where): static\n    {\n        self::validateVariadicParameter($where);\n\n        $dql = $this->getDQLPart('where');\n\n        if ($dql instanceof Expr\\Orx) {\n            $dql->addMultiple($where);\n        } else {\n            array_unshift($where, $dql);\n            $dql = new Expr\\Orx($where);\n        }\n\n        return $this->add('where', $dql);\n    }\n\n    /**\n     * Specifies a grouping over the results of the query.\n     * Replaces any previously specified groupings, if any.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->groupBy('u.id');\n     * </code>\n     *\n     * @return $this\n     */\n    public function groupBy(string ...$groupBy): static\n    {\n        self::validateVariadicParameter($groupBy);\n\n        return $this->add('groupBy', new Expr\\GroupBy($groupBy));\n    }\n\n    /**\n     * Adds a grouping expression to the query.\n     *\n     * <code>\n     *     $qb = $em->createQueryBuilder()\n     *         ->select('u')\n     *         ->from('User', 'u')\n     *         ->groupBy('u.lastLogin')\n     *         ->addGroupBy('u.createdAt');\n     * </code>\n     *\n     * @return $this\n     */\n    public function addGroupBy(string ...$groupBy): static\n    {\n        self::validateVariadicParameter($groupBy);\n\n        return $this->add('groupBy', new Expr\\GroupBy($groupBy), true);\n    }\n\n    /**\n     * Specifies a restriction over the groups of the query.\n     * Replaces any previous having restrictions, if any.\n     *\n     * @return $this\n     */\n    public function having(mixed ...$having): static\n    {\n        self::validateVariadicParameter($having);\n\n        if (! (count($having) === 1 && ($having[0] instanceof Expr\\Andx || $having[0] instanceof Expr\\Orx))) {\n            $having = new Expr\\Andx($having);\n        }\n\n        return $this->add('having', $having);\n    }\n\n    /**\n     * Adds a restriction over the groups of the query, forming a logical\n     * conjunction with any existing having restrictions.\n     *\n     * @return $this\n     */\n    public function andHaving(mixed ...$having): static\n    {\n        self::validateVariadicParameter($having);\n\n        $dql = $this->getDQLPart('having');\n\n        if ($dql instanceof Expr\\Andx) {\n            $dql->addMultiple($having);\n        } else {\n            array_unshift($having, $dql);\n            $dql = new Expr\\Andx($having);\n        }\n\n        return $this->add('having', $dql);\n    }\n\n    /**\n     * Adds a restriction over the groups of the query, forming a logical\n     * disjunction with any existing having restrictions.\n     *\n     * @return $this\n     */\n    public function orHaving(mixed ...$having): static\n    {\n        self::validateVariadicParameter($having);\n\n        $dql = $this->getDQLPart('having');\n\n        if ($dql instanceof Expr\\Orx) {\n            $dql->addMultiple($having);\n        } else {\n            array_unshift($having, $dql);\n            $dql = new Expr\\Orx($having);\n        }\n\n        return $this->add('having', $dql);\n    }\n\n    /**\n     * Specifies an ordering for the query results.\n     * Replaces any previously specified orderings, if any.\n     *\n     * @return $this\n     */\n    public function orderBy(string|Expr\\OrderBy $sort, string|null $order = null): static\n    {\n        $orderBy = $sort instanceof Expr\\OrderBy ? $sort : new Expr\\OrderBy($sort, $order);\n\n        return $this->add('orderBy', $orderBy);\n    }\n\n    /**\n     * Adds an ordering to the query results.\n     *\n     * @return $this\n     */\n    public function addOrderBy(string|Expr\\OrderBy $sort, string|null $order = null): static\n    {\n        $orderBy = $sort instanceof Expr\\OrderBy ? $sort : new Expr\\OrderBy($sort, $order);\n\n        return $this->add('orderBy', $orderBy, true);\n    }\n\n    /**\n     * Adds criteria to the query.\n     *\n     * Adds where expressions with AND operator.\n     * Adds orderings.\n     * Overrides firstResult and maxResults if they're set.\n     *\n     * @return $this\n     *\n     * @throws Query\\QueryException\n     */\n    public function addCriteria(Criteria $criteria): static\n    {\n        $allAliases = $this->getAllAliases();\n        if (! isset($allAliases[0])) {\n            throw new Query\\QueryException('No aliases are set before invoking addCriteria().');\n        }\n\n        $visitor = new QueryExpressionVisitor($this->getAllAliases());\n\n        $whereExpression = $criteria->getWhereExpression();\n        if ($whereExpression) {\n            $this->andWhere($visitor->dispatch($whereExpression));\n            foreach ($visitor->getParameters() as $parameter) {\n                $this->parameters->add($parameter);\n            }\n        }\n\n        foreach ($criteria->orderings() as $sort => $order) {\n            $hasValidAlias = false;\n            foreach ($allAliases as $alias) {\n                if (str_starts_with($sort . '.', $alias . '.')) {\n                    $hasValidAlias = true;\n                    break;\n                }\n            }\n\n            if (! $hasValidAlias) {\n                $sort = $allAliases[0] . '.' . $sort;\n            }\n\n            $this->addOrderBy($sort, $order->value);\n        }\n\n        // Overwrite limits only if they was set in criteria\n        $firstResult = $criteria->getFirstResult();\n        if ($firstResult > 0) {\n            $this->setFirstResult($firstResult);\n        }\n\n        $maxResults = $criteria->getMaxResults();\n        if ($maxResults !== null) {\n            $this->setMaxResults($maxResults);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Gets a query part by its name.\n     */\n    public function getDQLPart(string $queryPartName): mixed\n    {\n        return $this->dqlParts[$queryPartName];\n    }\n\n    /**\n     * Gets all query parts.\n     *\n     * @phpstan-return array<string, mixed> $dqlParts\n     */\n    public function getDQLParts(): array\n    {\n        return $this->dqlParts;\n    }\n\n    private function getDQLForDelete(): string\n    {\n         return 'DELETE'\n              . $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])\n              . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])\n              . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);\n    }\n\n    private function getDQLForUpdate(): string\n    {\n         return 'UPDATE'\n              . $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])\n              . $this->getReducedDQLQueryPart('set', ['pre' => ' SET ', 'separator' => ', '])\n              . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])\n              . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);\n    }\n\n    private function getDQLForSelect(): string\n    {\n        $dql = 'SELECT'\n             . ($this->dqlParts['distinct'] === true ? ' DISTINCT' : '')\n             . $this->getReducedDQLQueryPart('select', ['pre' => ' ', 'separator' => ', ']);\n\n        $fromParts   = $this->getDQLPart('from');\n        $joinParts   = $this->getDQLPart('join');\n        $fromClauses = [];\n\n        // Loop through all FROM clauses\n        if (! empty($fromParts)) {\n            $dql .= ' FROM ';\n\n            foreach ($fromParts as $from) {\n                $fromClause = (string) $from;\n\n                if ($from instanceof Expr\\From && isset($joinParts[$from->getAlias()])) {\n                    foreach ($joinParts[$from->getAlias()] as $join) {\n                        $fromClause .= ' ' . ((string) $join);\n                    }\n                }\n\n                $fromClauses[] = $fromClause;\n            }\n        }\n\n        $dql .= implode(', ', $fromClauses)\n              . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])\n              . $this->getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ', 'separator' => ', '])\n              . $this->getReducedDQLQueryPart('having', ['pre' => ' HAVING '])\n              . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);\n\n        return $dql;\n    }\n\n    /** @phpstan-param array<string, mixed> $options */\n    private function getReducedDQLQueryPart(string $queryPartName, array $options = []): string\n    {\n        $queryPart = $this->getDQLPart($queryPartName);\n\n        if (empty($queryPart)) {\n            return $options['empty'] ?? '';\n        }\n\n        return ($options['pre'] ?? '')\n             . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart)\n             . ($options['post'] ?? '');\n    }\n\n    /**\n     * Resets DQL parts.\n     *\n     * @param string[]|null $parts\n     * @phpstan-param list<string>|null $parts\n     *\n     * @return $this\n     */\n    public function resetDQLParts(array|null $parts = null): static\n    {\n        if ($parts === null) {\n            $parts = array_keys($this->dqlParts);\n        }\n\n        foreach ($parts as $part) {\n            $this->resetDQLPart($part);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Resets single DQL part.\n     *\n     * @return $this\n     */\n    public function resetDQLPart(string $part): static\n    {\n        $this->dqlParts[$part] = is_array($this->dqlParts[$part]) ? [] : null;\n        $this->dql             = null;\n\n        return $this;\n    }\n\n    /**\n     * Creates a new named parameter and bind the value $value to it.\n     *\n     * The parameter $value specifies the value that you want to bind. If\n     * $placeholder is not provided createNamedParameter() will automatically\n     * create a placeholder for you. An automatic placeholder will be of the\n     * name ':dcValue1', ':dcValue2' etc.\n     *\n     * Example:\n     *  <code>\n     *   $qb = $em->createQueryBuilder();\n     *   $qb\n     *      ->select('u')\n     *      ->from('User', 'u')\n     *      ->where('u.username = ' . $qb->createNamedParameter('Foo', Types::STRING))\n     *      ->orWhere('u.username = ' . $qb->createNamedParameter('Bar', Types::STRING))\n     *  </code>\n     *\n     * @param ParameterType|ArrayParameterType|string|int|null $type        ParameterType::*, ArrayParameterType::* or \\Doctrine\\DBAL\\Types\\Type::* constant\n     * @param non-empty-string|null                            $placeholder The name to bind with. The string must start with a colon ':'.\n     *\n     * @return non-empty-string the placeholder name used.\n     */\n    public function createNamedParameter(mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null, string|null $placeholder = null): string\n    {\n        if ($placeholder === null) {\n            $this->boundCounter++;\n            $placeholder = ':dcValue' . $this->boundCounter;\n        }\n\n        $this->setParameter(substr($placeholder, 1), $value, $type);\n\n        return $placeholder;\n    }\n\n    /**\n     * Gets a string representation of this QueryBuilder which corresponds to\n     * the final DQL query being constructed.\n     */\n    public function __toString(): string\n    {\n        return $this->getDQL();\n    }\n\n    /**\n     * Deep clones all expression objects in the DQL parts.\n     *\n     * @return void\n     */\n    public function __clone()\n    {\n        foreach ($this->dqlParts as $part => $elements) {\n            if (is_array($this->dqlParts[$part])) {\n                foreach ($this->dqlParts[$part] as $idx => $element) {\n                    if (is_object($element)) {\n                        $this->dqlParts[$part][$idx] = clone $element;\n                    }\n                }\n            } elseif (is_object($elements)) {\n                $this->dqlParts[$part] = clone $elements;\n            }\n        }\n\n        $parameters = [];\n\n        foreach ($this->parameters as $parameter) {\n            $parameters[] = clone $parameter;\n        }\n\n        $this->parameters = new ArrayCollection($parameters);\n    }\n}\n"
  },
  {
    "path": "src/QueryType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nenum QueryType\n{\n    case Select;\n    case Delete;\n    case Update;\n}\n"
  },
  {
    "path": "src/Repository/DefaultRepositoryFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Repository;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\Persistence\\ObjectRepository;\n\nuse function spl_object_id;\n\n/**\n * This factory is used to create default repository objects for entities at runtime.\n */\nfinal class DefaultRepositoryFactory implements RepositoryFactory\n{\n    /**\n     * The list of EntityRepository instances.\n     *\n     * @var ObjectRepository[]\n     * @phpstan-var array<string, EntityRepository>\n     */\n    private array $repositoryList = [];\n\n    public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository\n    {\n        $repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_id($entityManager);\n\n        return $this->repositoryList[$repositoryHash] ??= $this->createRepository($entityManager, $entityName);\n    }\n\n    /**\n     * Create a new repository instance for an entity class.\n     *\n     * @param EntityManagerInterface $entityManager The EntityManager instance.\n     * @param string                 $entityName    The name of the entity.\n     */\n    private function createRepository(\n        EntityManagerInterface $entityManager,\n        string $entityName,\n    ): EntityRepository {\n        $metadata            = $entityManager->getClassMetadata($entityName);\n        $repositoryClassName = $metadata->customRepositoryClassName\n            ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();\n\n        return new $repositoryClassName($entityManager, $metadata);\n    }\n}\n"
  },
  {
    "path": "src/Repository/Exception/InvalidFindByCall.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Repository\\Exception;\n\nuse Doctrine\\ORM\\Exception\\RepositoryException;\nuse LogicException;\n\nfinal class InvalidFindByCall extends LogicException implements RepositoryException\n{\n    public static function fromInverseSideUsage(\n        string $entityName,\n        string $associationFieldName,\n    ): self {\n        return new self(\n            \"You cannot search for the association field '\" . $entityName . '#' . $associationFieldName . \"', \" .\n            'because it is the inverse side of an association. Find methods only work on owning side associations.',\n        );\n    }\n}\n"
  },
  {
    "path": "src/Repository/Exception/InvalidMagicMethodCall.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Repository\\Exception;\n\nuse Doctrine\\ORM\\Exception\\RepositoryException;\nuse LogicException;\n\nfinal class InvalidMagicMethodCall extends LogicException implements RepositoryException\n{\n    public static function becauseFieldNotFoundIn(\n        string $entityName,\n        string $fieldName,\n        string $method,\n    ): self {\n        return new self(\n            \"Entity '\" . $entityName . \"' has no field '\" . $fieldName . \"'. \" .\n            \"You can therefore not call '\" . $method . \"' on the entities' repository.\",\n        );\n    }\n\n    public static function onMissingParameter(string $methodName): self\n    {\n        return new self(\"You need to pass a parameter to '\" . $methodName . \"'\");\n    }\n}\n"
  },
  {
    "path": "src/Repository/RepositoryFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Repository;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityRepository;\n\n/**\n * Interface for entity repository factory.\n */\ninterface RepositoryFactory\n{\n    /**\n     * Gets the repository for an entity class.\n     *\n     * @param EntityManagerInterface $entityManager The EntityManager instance.\n     * @param class-string<T>        $entityName    The name of the entity.\n     *\n     * @return EntityRepository<T>\n     *\n     * @template T of object\n     */\n    public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository;\n}\n"
  },
  {
    "path": "src/Tools/AttachEntityListenersListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Builder\\EntityListenerBuilder;\n\nuse function assert;\nuse function ltrim;\n\n/**\n * Mechanism to programmatically attach entity listeners.\n */\nclass AttachEntityListenersListener\n{\n    /**\n     * @var array<class-string, list<array{\n     *     event: Events::*|null,\n     *     class: class-string,\n     *     method: string|null,\n     * }>>\n     */\n    private array $entityListeners = [];\n\n    /**\n     * Adds an entity listener for a specific entity.\n     *\n     * @param class-string          $entityClass      The entity to attach the listener.\n     * @param class-string          $listenerClass    The listener class.\n     * @param Events::*|null        $eventName        The entity lifecycle event.\n     * @param non-falsy-string|null $listenerCallback The listener callback method or NULL to use $eventName.\n     */\n    public function addEntityListener(\n        string $entityClass,\n        string $listenerClass,\n        string|null $eventName = null,\n        string|null $listenerCallback = null,\n    ): void {\n        $this->entityListeners[ltrim($entityClass, '\\\\')][] = [\n            'event'  => $eventName,\n            'class'  => $listenerClass,\n            'method' => $listenerCallback ?? $eventName,\n        ];\n    }\n\n    /**\n     * Processes event and attach the entity listener.\n     */\n    public function loadClassMetadata(LoadClassMetadataEventArgs $event): void\n    {\n        $metadata = $event->getClassMetadata();\n\n        if (! isset($this->entityListeners[$metadata->name])) {\n            return;\n        }\n\n        foreach ($this->entityListeners[$metadata->name] as $listener) {\n            if ($listener['event'] === null) {\n                EntityListenerBuilder::bindEntityListener($metadata, $listener['class']);\n            } else {\n                assert($listener['method'] !== null);\n                $metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/ApplicationCompatibility.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console;\n\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Command\\Command;\n\nuse function method_exists;\n\n/**\n * Forward compatibility with Symfony Console 7.4\n *\n * @internal\n */\ntrait ApplicationCompatibility\n{\n    private static function addCommandToApplication(Application $application, Command $command): Command|null\n    {\n        // @phpstan-ignore function.alreadyNarrowedType (This method did not exist before Symfony 7.4)\n        if (method_exists(Application::class, 'addCommand')) {\n            return $application->addCommand($command);\n        }\n\n        // @phpstan-ignore method.notFound\n        return $application->add($command);\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/AbstractEntityManagerCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\n\nabstract class AbstractEntityManagerCommand extends Command\n{\n    public function __construct(private readonly EntityManagerProvider $entityManagerProvider)\n    {\n        parent::__construct();\n    }\n\n    final protected function getEntityManager(InputInterface $input): EntityManagerInterface\n    {\n        return $input->getOption('em') === null\n            ? $this->entityManagerProvider->getDefaultManager()\n            : $this->entityManagerProvider->getManager($input->getOption('em'));\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function sprintf;\n\n/**\n * Command to clear a collection cache region.\n */\nclass CollectionRegionCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:region:collection')\n             ->setDescription('Clear a second-level cache collection region')\n             ->addArgument('owner-class', InputArgument::OPTIONAL, 'The owner entity name.')\n             ->addArgument('association', InputArgument::OPTIONAL, 'The association collection name.')\n             ->addArgument('owner-id', InputArgument::OPTIONAL, 'The owner identifier.')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.')\n             ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, all cache entries will be flushed.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command is meant to clear a second-level cache collection regions for an associated Entity Manager.\nIt is possible to delete/invalidate all collection region, a specific collection region or flushes the cache provider.\n\nThe execution type differ on how you execute the command.\nIf you want to invalidate all entries for an collection region this command would do the work:\n\n<info>%command.name% 'Entities\\MyEntity' 'collectionName'</info>\n\nTo invalidate a specific entry you should use :\n\n<info>%command.name% 'Entities\\MyEntity' 'collectionName' 1</info>\n\nIf you want to invalidate all entries for the all collection regions:\n\n<info>%command.name% --all</info>\n\nAlternatively, if you want to flush the configured cache provider for an collection region use this command:\n\n<info>%command.name% 'Entities\\MyEntity' 'collectionName' --flush</info>\n\nFinally, be aware that if <info>--flush</info> option is passed,\nnot all cache providers are able to flush entries, because of a limitation of its execution nature.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em         = $this->getEntityManager($input);\n        $ownerClass = $input->getArgument('owner-class');\n        $assoc      = $input->getArgument('association');\n        $ownerId    = $input->getArgument('owner-id');\n        $cache      = $em->getCache();\n\n        if (! $cache instanceof Cache) {\n            throw new InvalidArgumentException('No second-level cache is configured on the given EntityManager.');\n        }\n\n        if (( ! $ownerClass || ! $assoc) && ! $input->getOption('all')) {\n            throw new InvalidArgumentException('Missing arguments \"--owner-class\" \"--association\"');\n        }\n\n        if ($input->getOption('flush')) {\n            $cache->getCollectionCacheRegion($ownerClass, $assoc)\n                ->evictAll();\n\n            $ui->comment(\n                sprintf(\n                    'Flushing cache provider configured for <info>\"%s#%s\"</info>',\n                    $ownerClass,\n                    $assoc,\n                ),\n            );\n\n            return 0;\n        }\n\n        if ($input->getOption('all')) {\n            $ui->comment('Clearing <info>all</info> second-level cache collection regions');\n\n            $cache->evictEntityRegions();\n\n            return 0;\n        }\n\n        if ($ownerId) {\n            $ui->comment(\n                sprintf(\n                    'Clearing second-level cache entry for collection <info>\"%s#%s\"</info> owner entity identified by <info>\"%s\"</info>',\n                    $ownerClass,\n                    $assoc,\n                    $ownerId,\n                ),\n            );\n            $cache->evictCollection($ownerClass, $assoc, $ownerId);\n\n            return 0;\n        }\n\n        $ui->comment(sprintf('Clearing second-level cache for collection <info>\"%s#%s\"</info>', $ownerClass, $assoc));\n        $cache->evictCollectionRegion($ownerClass, $assoc);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/EntityRegionCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function sprintf;\n\n/**\n * Command to clear a entity cache region.\n */\nclass EntityRegionCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:region:entity')\n             ->setDescription('Clear a second-level cache entity region')\n             ->addArgument('entity-class', InputArgument::OPTIONAL, 'The entity name.')\n             ->addArgument('entity-id', InputArgument::OPTIONAL, 'The entity identifier.')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.')\n             ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, all cache entries will be flushed.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command is meant to clear a second-level cache entity region for an associated Entity Manager.\nIt is possible to delete/invalidate all entity region, a specific entity region or flushes the cache provider.\n\nThe execution type differ on how you execute the command.\nIf you want to invalidate all entries for an entity region this command would do the work:\n\n<info>%command.name% 'Entities\\MyEntity'</info>\n\nTo invalidate a specific entry you should use :\n\n<info>%command.name% 'Entities\\MyEntity' 1</info>\n\nIf you want to invalidate all entries for the all entity regions:\n\n<info>%command.name% --all</info>\n\nAlternatively, if you want to flush the configured cache provider for an entity region use this command:\n\n<info>%command.name% 'Entities\\MyEntity' --flush</info>\n\nFinally, be aware that if <info>--flush</info> option is passed,\nnot all cache providers are able to flush entries, because of a limitation of its execution nature.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em          = $this->getEntityManager($input);\n        $entityClass = $input->getArgument('entity-class');\n        $entityId    = $input->getArgument('entity-id');\n        $cache       = $em->getCache();\n\n        if (! $cache instanceof Cache) {\n            throw new InvalidArgumentException('No second-level cache is configured on the given EntityManager.');\n        }\n\n        if (! $entityClass && ! $input->getOption('all')) {\n            throw new InvalidArgumentException('Invalid argument \"--entity-class\"');\n        }\n\n        if ($input->getOption('flush')) {\n            $cache->getEntityCacheRegion($entityClass)\n                ->evictAll();\n\n            $ui->comment(sprintf('Flushing cache provider configured for entity named <info>\"%s\"</info>', $entityClass));\n\n            return 0;\n        }\n\n        if ($input->getOption('all')) {\n            $ui->comment('Clearing <info>all</info> second-level cache entity regions');\n\n            $cache->evictEntityRegions();\n\n            return 0;\n        }\n\n        if ($entityId) {\n            $ui->comment(\n                sprintf(\n                    'Clearing second-level cache entry for entity <info>\"%s\"</info> identified by <info>\"%s\"</info>',\n                    $entityClass,\n                    $entityId,\n                ),\n            );\n            $cache->evictEntity($entityClass, $entityId);\n\n            return 0;\n        }\n\n        $ui->comment(sprintf('Clearing second-level cache for entity <info>\"%s\"</info>', $entityClass));\n        $cache->evictEntityRegion($entityClass);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/MetadataCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\n/**\n * Command to clear the metadata cache of the various cache drivers.\n *\n * @link    www.doctrine-project.org\n */\nclass MetadataCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:metadata')\n             ->setDescription('Clear all metadata cache of the various cache drivers')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, cache entries will be flushed instead of deleted/invalidated.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command is meant to clear the metadata cache of associated Entity Manager.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em          = $this->getEntityManager($input);\n        $cacheDriver = $em->getConfiguration()->getMetadataCache();\n\n        if (! $cacheDriver) {\n            throw new InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.');\n        }\n\n        $ui->comment('Clearing <info>all</info> Metadata cache entries');\n\n        $result  = $cacheDriver->clear();\n        $message = $result ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';\n\n        $ui->success($message);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/QueryCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse LogicException;\nuse Symfony\\Component\\Cache\\Adapter\\ApcuAdapter;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\n/**\n * Command to clear the query cache of the various cache drivers.\n *\n * @link    www.doctrine-project.org\n */\nclass QueryCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:query')\n             ->setDescription('Clear all query cache of the various cache drivers')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->setHelp('The <info>%command.name%</info> command is meant to clear the query cache of associated Entity Manager.');\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em    = $this->getEntityManager($input);\n        $cache = $em->getConfiguration()->getQueryCache();\n\n        if (! $cache) {\n            throw new InvalidArgumentException('No Query cache driver is configured on given EntityManager.');\n        }\n\n        if ($cache instanceof ApcuAdapter) {\n            throw new LogicException('Cannot clear APCu Cache from Console, it\\'s shared in the Webserver memory and not accessible from the CLI.');\n        }\n\n        $ui->comment('Clearing <info>all</info> Query cache entries');\n\n        $message = $cache->clear() ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';\n\n        $ui->success($message);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/QueryRegionCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function sprintf;\n\n/**\n * Command to clear a query cache region.\n */\nclass QueryRegionCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:region:query')\n             ->setDescription('Clear a second-level cache query region')\n             ->addArgument('region-name', InputArgument::OPTIONAL, 'The query region to clear.')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all query regions will be deleted/invalidated.')\n             ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, all cache entries will be flushed.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command is meant to clear a second-level cache query region for an associated Entity Manager.\nIt is possible to delete/invalidate all query region, a specific query region or flushes the cache provider.\n\nThe execution type differ on how you execute the command.\nIf you want to invalidate all entries for the default query region this command would do the work:\n\n<info>%command.name%</info>\n\nTo invalidate entries for a specific query region you should use :\n\n<info>%command.name% my_region_name</info>\n\nIf you want to invalidate all entries for the all query region:\n\n<info>%command.name% --all</info>\n\nAlternatively, if you want to flush the configured cache provider use this command:\n\n<info>%command.name% my_region_name --flush</info>\n\nFinally, be aware that if <info>--flush</info> option is passed,\nnot all cache providers are able to flush entries, because of a limitation of its execution nature.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em    = $this->getEntityManager($input);\n        $name  = $input->getArgument('region-name');\n        $cache = $em->getCache();\n\n        if ($name === null) {\n            $name = Cache::DEFAULT_QUERY_REGION_NAME;\n        }\n\n        if (! $cache instanceof Cache) {\n            throw new InvalidArgumentException('No second-level cache is configured on the given EntityManager.');\n        }\n\n        if ($input->getOption('flush')) {\n            $cache->getQueryCache($name)\n                ->getRegion()\n                ->evictAll();\n\n            $ui->comment(\n                sprintf(\n                    'Flushing cache provider configured for second-level cache query region named <info>\"%s\"</info>',\n                    $name,\n                ),\n            );\n\n            return 0;\n        }\n\n        if ($input->getOption('all')) {\n            $ui->comment('Clearing <info>all</info> second-level cache query regions');\n\n            $cache->evictQueryRegions();\n\n            return 0;\n        }\n\n        $ui->comment(sprintf('Clearing second-level cache query region named <info>\"%s\"</info>', $name));\n        $cache->evictQueryRegion($name);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ClearCache/ResultCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\n/**\n * Command to clear the result cache of the various cache drivers.\n *\n * @link    www.doctrine-project.org\n */\nclass ResultCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:clear-cache:result')\n             ->setDescription('Clear all result cache of the various cache drivers')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, cache entries will be flushed instead of deleted/invalidated.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command is meant to clear the result cache of associated Entity Manager.\nIt is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider\ninstance completely.\n\nThe execution type differ on how you execute the command.\nIf you want to invalidate the entries (and not delete from cache instance), this command would do the work:\n\n<info>%command.name%</info>\n\nAlternatively, if you want to flush the cache provider using this command:\n\n<info>%command.name% --flush</info>\n\nFinally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,\nbecause of a limitation of its execution nature.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em    = $this->getEntityManager($input);\n        $cache = $em->getConfiguration()->getResultCache();\n\n        if (! $cache) {\n            throw new InvalidArgumentException('No Result cache driver is configured on given EntityManager.');\n        }\n\n        $ui->comment('Clearing <info>all</info> Result cache entries');\n\n        $message = $cache->clear() ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';\n\n        $ui->success($message);\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/Debug/AbstractCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\Debug;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Symfony\\Component\\Console\\Command\\Command;\n\nuse function assert;\n\n/** @internal */\nabstract class AbstractCommand extends Command\n{\n    public function __construct(private readonly ManagerRegistry $managerRegistry)\n    {\n        parent::__construct();\n    }\n\n    final protected function getEntityManager(string $name): EntityManagerInterface\n    {\n        $manager = $this->getManagerRegistry()->getManager($name);\n\n        assert($manager instanceof EntityManagerInterface);\n\n        return $manager;\n    }\n\n    final protected function getManagerRegistry(): ManagerRegistry\n    {\n        return $this->managerRegistry;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/Debug/DebugEntityListenersDoctrineCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\Debug;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Symfony\\Component\\Console\\Completion\\CompletionInput;\nuse Symfony\\Component\\Console\\Completion\\CompletionSuggestions;\nuse Symfony\\Component\\Console\\Helper\\TableSeparator;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function array_keys;\nuse function array_merge;\nuse function array_unique;\nuse function array_values;\nuse function assert;\nuse function class_exists;\nuse function ksort;\nuse function ltrim;\nuse function sort;\nuse function sprintf;\n\nfinal class DebugEntityListenersDoctrineCommand extends AbstractCommand\n{\n    protected function configure(): void\n    {\n        $this\n            ->setName('orm:debug:entity-listeners')\n            ->setDescription('Lists entity listeners for a given entity')\n            ->addArgument('entity', InputArgument::OPTIONAL, 'The fully-qualified entity class name')\n            ->addArgument('event', InputArgument::OPTIONAL, 'The event name to filter by (e.g. postPersist)')\n            ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command lists all entity listeners for a given entity:\n\n    <info>php %command.full_name% 'App\\Entity\\User'</info>\n\nTo show only listeners for a specific event, pass the event name:\n\n    <info>php %command.full_name% 'App\\Entity\\User' postPersist</info>\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $io = new SymfonyStyle($input, $output);\n\n        /** @var class-string|null $entityName */\n        $entityName = $input->getArgument('entity');\n\n        if ($entityName === null) {\n            $choices = $this->listAllEntities();\n\n            if ($choices === []) {\n                $io->error('No entities are configured.');\n\n                return self::FAILURE;\n            }\n\n            /** @var class-string $entityName */\n            $entityName = $io->choice('Which entity do you want to list listeners for?', $choices);\n        }\n\n        $entityName    = ltrim($entityName, '\\\\');\n        $entityManager = $this->getManagerRegistry()->getManagerForClass($entityName);\n\n        if ($entityManager === null) {\n            $io->error(sprintf('No entity manager found for class \"%s\".', $entityName));\n\n            return self::FAILURE;\n        }\n\n        $classMetadata = $entityManager->getClassMetadata($entityName);\n        assert($classMetadata instanceof ClassMetadata);\n\n        $eventName = $input->getArgument('event');\n\n        if ($eventName === null) {\n            $allListeners = $classMetadata->entityListeners;\n            if (! $allListeners) {\n                $io->info(sprintf('No listeners are configured for the \"%s\" entity.', $entityName));\n\n                return self::SUCCESS;\n            }\n\n            ksort($allListeners);\n        } else {\n            if (! isset($classMetadata->entityListeners[$eventName])) {\n                $io->info(sprintf('No listeners are configured for the \"%s\" event.', $eventName));\n\n                return self::SUCCESS;\n            }\n\n            $allListeners = [$eventName => $classMetadata->entityListeners[$eventName]];\n        }\n\n        $io->title(sprintf('Entity listeners for <info>%s</info>', $entityName));\n\n        $rows = [];\n        foreach ($allListeners as $event => $listeners) {\n            if ($rows) {\n                $rows[] = new TableSeparator();\n            }\n\n            foreach ($listeners as $order => $listener) {\n                $rows[] = [$order === 0 ? $event : '', sprintf('#%d', ++$order), sprintf('%s::%s()', $listener['class'], $listener['method'])];\n            }\n        }\n\n        $io->table(['Event', 'Order', 'Listener'], $rows);\n\n        return self::SUCCESS;\n    }\n\n    public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void\n    {\n        if ($input->mustSuggestArgumentValuesFor('entity')) {\n            $suggestions->suggestValues($this->listAllEntities());\n\n            return;\n        }\n\n        if ($input->mustSuggestArgumentValuesFor('event')) {\n            $entityName = ltrim($input->getArgument('entity'), '\\\\');\n\n            if (! class_exists($entityName)) {\n                return;\n            }\n\n            $entityManager = $this->getManagerRegistry()->getManagerForClass($entityName);\n\n            if ($entityManager === null) {\n                return;\n            }\n\n            $classMetadata = $entityManager->getClassMetadata($entityName);\n            assert($classMetadata instanceof ClassMetadata);\n\n            $suggestions->suggestValues(array_keys($classMetadata->entityListeners));\n\n            return;\n        }\n    }\n\n    /** @return list<class-string> */\n    private function listAllEntities(): array\n    {\n        $entities = [];\n        foreach (array_keys($this->getManagerRegistry()->getManagerNames()) as $managerName) {\n            $entities[] = $this->getEntityManager($managerName)->getConfiguration()->getMetadataDriverImpl()->getAllClassNames();\n        }\n\n        $entities = array_values(array_unique(array_merge(...$entities)));\n\n        sort($entities);\n\n        return $entities;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/Debug/DebugEventManagerDoctrineCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\Debug;\n\nuse Symfony\\Component\\Console\\Completion\\CompletionInput;\nuse Symfony\\Component\\Console\\Completion\\CompletionSuggestions;\nuse Symfony\\Component\\Console\\Helper\\TableSeparator;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function array_keys;\nuse function array_values;\nuse function ksort;\nuse function method_exists;\nuse function sprintf;\n\nfinal class DebugEventManagerDoctrineCommand extends AbstractCommand\n{\n    protected function configure(): void\n    {\n        $this\n            ->setName('orm:debug:event-manager')\n            ->setDescription('Lists event listeners for an entity manager')\n            ->addArgument('event', InputArgument::OPTIONAL, 'The event name to filter by (e.g. postPersist)')\n            ->addOption('em', null, InputOption::VALUE_REQUIRED, 'The entity manager to use for this command')\n            ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command lists all event listeners for the default entity manager:\n\n    <info>php %command.full_name%</info>\n\nYou can also specify an entity manager:\n\n    <info>php %command.full_name% --em=default</info>\n\nTo show only listeners for a specific event, pass the event name as an argument:\n\n    <info>php %command.full_name% postPersist</info>\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $io = new SymfonyStyle($input, $output);\n\n        $entityManagerName = $input->getOption('em') ?: $this->getManagerRegistry()->getDefaultManagerName();\n        $eventManager      = $this->getEntityManager($entityManagerName)->getEventManager();\n\n        $eventName = $input->getArgument('event');\n\n        if ($eventName === null) {\n            $allListeners = $eventManager->getAllListeners();\n            if (! $allListeners) {\n                $io->info(sprintf('No listeners are configured for the \"%s\" entity manager.', $entityManagerName));\n\n                return self::SUCCESS;\n            }\n\n            ksort($allListeners);\n        } else {\n            $listeners = $eventManager->hasListeners($eventName) ? $eventManager->getListeners($eventName) : [];\n            if (! $listeners) {\n                $io->info(sprintf('No listeners are configured for the \"%s\" event.', $eventName));\n\n                return self::SUCCESS;\n            }\n\n            $allListeners = [$eventName => $listeners];\n        }\n\n        $io->title(sprintf('Event listeners for <info>%s</info> entity manager', $entityManagerName));\n\n        $rows = [];\n        foreach ($allListeners as $event => $listeners) {\n            if ($rows) {\n                $rows[] = new TableSeparator();\n            }\n\n            foreach (array_values($listeners) as $order => $listener) {\n                $method = method_exists($listener, '__invoke') ? '__invoke' : $event;\n                $rows[] = [$order === 0 ? $event : '', sprintf('#%d', ++$order), sprintf('%s::%s()', $listener::class, $method)];\n            }\n        }\n\n        $io->table(['Event', 'Order', 'Listener'], $rows);\n\n        return self::SUCCESS;\n    }\n\n    public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void\n    {\n        if ($input->mustSuggestArgumentValuesFor('event')) {\n            $entityManagerName = $input->getOption('em') ?: $this->getManagerRegistry()->getDefaultManagerName();\n            $eventManager      = $this->getEntityManager($entityManagerName)->getEventManager();\n\n            $suggestions->suggestValues(array_keys($eventManager->getAllListeners()));\n\n            return;\n        }\n\n        if ($input->mustSuggestOptionValuesFor('em')) {\n            $suggestions->suggestValues(array_keys($this->getManagerRegistry()->getManagerNames()));\n\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/GenerateProxiesCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Tools\\Console\\MetadataFilter;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function file_exists;\nuse function is_dir;\nuse function is_writable;\nuse function mkdir;\nuse function realpath;\nuse function sprintf;\n\nuse const PHP_VERSION_ID;\n\n/**\n * Command to (re)generate the proxy classes used by doctrine.\n *\n * @link    www.doctrine-project.org\n */\nclass GenerateProxiesCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:generate-proxies')\n             ->setAliases(['orm:generate:proxies'])\n             ->setDescription('Generates proxy classes for entity classes')\n             ->addArgument('dest-path', InputArgument::OPTIONAL, 'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.')\n             ->setHelp('Generates proxy classes for entity classes.');\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/12005',\n                'Generating proxies is deprecated and will be impossible in Doctrine ORM 4.0.',\n            );\n        }\n\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em = $this->getEntityManager($input);\n\n        $metadatas = $em->getMetadataFactory()->getAllMetadata();\n        $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));\n\n        // Process destination directory\n        $destPath = $input->getArgument('dest-path');\n        if ($destPath === null) {\n            $destPath = $em->getConfiguration()->getProxyDir();\n\n            if ($destPath === null) {\n                throw new InvalidArgumentException('Proxy directory cannot be null');\n            }\n        }\n\n        if (! is_dir($destPath)) {\n            mkdir($destPath, 0775, true);\n        }\n\n        $destPath = realpath($destPath);\n\n        if (! file_exists($destPath)) {\n            throw new InvalidArgumentException(\n                sprintf(\"Proxies destination directory '<info>%s</info>' does not exist.\", $em->getConfiguration()->getProxyDir()),\n            );\n        }\n\n        if (! is_writable($destPath)) {\n            throw new InvalidArgumentException(\n                sprintf(\"Proxies destination directory '<info>%s</info>' does not have write permissions.\", $destPath),\n            );\n        }\n\n        if (empty($metadatas)) {\n            $ui->success('No Metadata Classes to process.');\n\n            return 0;\n        }\n\n        foreach ($metadatas as $metadata) {\n            $ui->text(sprintf('Processing entity \"<info>%s</info>\"', $metadata->name));\n        }\n\n        // Generating Proxies\n        $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath);\n\n        // Outputting information message\n        $ui->newLine();\n        $ui->text(sprintf('Proxy classes generated to \"<info>%s</info>\"', $destPath));\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/InfoCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function count;\nuse function sprintf;\n\n/**\n * Show information about mapped entities.\n *\n * @link    www.doctrine-project.org\n */\nclass InfoCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:info')\n             ->setDescription('Show basic information about all mapped entities')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> shows basic information about which\nentities exist and possibly if their mapping information contains errors or\nnot.\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = new SymfonyStyle($input, $output);\n\n        $entityManager = $this->getEntityManager($input);\n\n        $entityClassNames = $entityManager->getConfiguration()\n                                          ->getMetadataDriverImpl()\n                                          ->getAllClassNames();\n\n        if (! $entityClassNames) {\n            $ui->getErrorStyle()->caution(\n                [\n                    'You do not have any mapped Doctrine ORM entities according to the current configuration.',\n                    'If you have entities or mapping files you should check your mapping configuration for errors.',\n                ],\n            );\n\n            return 1;\n        }\n\n        $ui->text(sprintf('Found <info>%d</info> mapped entities:', count($entityClassNames)));\n        $ui->newLine();\n\n        $failure = false;\n\n        foreach ($entityClassNames as $entityClassName) {\n            try {\n                $entityManager->getClassMetadata($entityClassName);\n                $ui->text(sprintf('<info>[OK]</info>   %s', $entityClassName));\n            } catch (MappingException $e) {\n                $ui->text(\n                    [\n                        sprintf('<error>[FAIL]</error> %s', $entityClassName),\n                        sprintf('<comment>%s</comment>', $e->getMessage()),\n                        '',\n                    ],\n                );\n\n                $failure = true;\n            }\n        }\n\n        return $failure ? 1 : 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/MappingDescribeCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\FieldMapping;\nuse Doctrine\\Persistence\\Mapping\\MappingException;\nuse InvalidArgumentException;\nuse JsonException;\nuse Symfony\\Component\\Console\\Completion\\CompletionInput;\nuse Symfony\\Component\\Console\\Completion\\CompletionSuggestions;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function array_filter;\nuse function array_map;\nuse function array_merge;\nuse function array_values;\nuse function count;\nuse function current;\nuse function get_debug_type;\nuse function implode;\nuse function is_array;\nuse function is_bool;\nuse function is_object;\nuse function is_scalar;\nuse function json_encode;\nuse function preg_match;\nuse function preg_quote;\nuse function print_r;\nuse function sprintf;\nuse function str_replace;\n\nuse const JSON_PRETTY_PRINT;\nuse const JSON_THROW_ON_ERROR;\nuse const JSON_UNESCAPED_SLASHES;\nuse const JSON_UNESCAPED_UNICODE;\n\n/**\n * Show information about mapped entities.\n *\n * @link    www.doctrine-project.org\n */\nfinal class MappingDescribeCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:mapping:describe')\n            ->addArgument('entityName', InputArgument::REQUIRED, 'Full or partial name of entity')\n            ->setDescription('Display information about mapped objects')\n            ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n            ->addOption(\n                'format',\n                null,\n                InputOption::VALUE_REQUIRED,\n                'Output format (text, json)',\n                MappingDescribeCommandFormat::TEXT->value,\n                array_map(static fn (MappingDescribeCommandFormat $format) => $format->value, MappingDescribeCommandFormat::cases()),\n            )\n             ->setHelp(<<<'EOT'\nThe %command.full_name% command describes the metadata for the given full or partial entity class name.\n\n    <info>%command.full_name%</info> My\\Namespace\\Entity\\MyEntity\n\nOr:\n\n    <info>%command.full_name%</info> MyEntity\n    \nTo output the metadata in JSON format, use the <info>--format</info> option:\n  <info>%command.full_name% My\\Namespace\\Entity\\MyEntity --format=json</info>\n\nTo use a specific entity manager (e.g., for multi-DB projects), use the <info>--em</info> option:\n  <info>%command.full_name% My\\Namespace\\Entity\\MyEntity --em=my_custom_entity_manager</info>\n\nEOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = new SymfonyStyle($input, $output);\n\n        $format = MappingDescribeCommandFormat::from($input->getOption('format'));\n\n        $entityManager = $this->getEntityManager($input);\n\n        $this->displayEntity($input->getArgument('entityName'), $entityManager, $ui, $format);\n\n        return 0;\n    }\n\n    public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void\n    {\n        if ($input->mustSuggestArgumentValuesFor('entityName')) {\n            $entityManager = $this->getEntityManager($input);\n\n            $entities = array_map(\n                static fn (string $fqcn) => str_replace('\\\\', '\\\\\\\\', $fqcn),\n                $this->getMappedEntities($entityManager),\n            );\n\n            $suggestions->suggestValues(array_values($entities));\n        }\n\n        if ($input->mustSuggestOptionValuesFor('format')) {\n            $suggestions->suggestValues(array_map(static fn (MappingDescribeCommandFormat $format) => $format->value, MappingDescribeCommandFormat::cases()));\n        }\n    }\n\n    /**\n     * Display all the mapping information for a single Entity.\n     *\n     * @param string $entityName Full or partial entity class name\n     */\n    private function displayEntity(\n        string $entityName,\n        EntityManagerInterface $entityManager,\n        SymfonyStyle $ui,\n        MappingDescribeCommandFormat $format,\n    ): void {\n        $metadata = $this->getClassMetadata($entityName, $entityManager);\n\n        if ($format === MappingDescribeCommandFormat::JSON) {\n            $ui->text(json_encode(\n                [\n                    'name' => $metadata->name,\n                    'rootEntityName' => $metadata->rootEntityName,\n                    'customGeneratorDefinition' => $this->formatValueAsJson($metadata->customGeneratorDefinition),\n                    'customRepositoryClassName' => $metadata->customRepositoryClassName,\n                    'isMappedSuperclass' => $metadata->isMappedSuperclass,\n                    'isEmbeddedClass' => $metadata->isEmbeddedClass,\n                    'parentClasses' => $metadata->parentClasses,\n                    'subClasses' => $metadata->subClasses,\n                    'embeddedClasses' => $metadata->embeddedClasses,\n                    'identifier' => $metadata->identifier,\n                    'inheritanceType' => $metadata->inheritanceType,\n                    'discriminatorColumn' => $this->formatValueAsJson($metadata->discriminatorColumn),\n                    'discriminatorValue' => $metadata->discriminatorValue,\n                    'discriminatorMap' => $metadata->discriminatorMap,\n                    'generatorType' => $metadata->generatorType,\n                    'table' => $this->formatValueAsJson($metadata->table),\n                    'isIdentifierComposite' => $metadata->isIdentifierComposite,\n                    'containsForeignIdentifier' => $metadata->containsForeignIdentifier,\n                    'containsEnumIdentifier' => $metadata->containsEnumIdentifier,\n                    'sequenceGeneratorDefinition' => $this->formatValueAsJson($metadata->sequenceGeneratorDefinition),\n                    'changeTrackingPolicy' => $metadata->changeTrackingPolicy,\n                    'isVersioned' => $metadata->isVersioned,\n                    'versionField' => $metadata->versionField,\n                    'isReadOnly' => $metadata->isReadOnly,\n                    'entityListeners' => $metadata->entityListeners,\n                    'associationMappings' => $this->formatMappingsAsJson($metadata->associationMappings),\n                    'fieldMappings' => $this->formatMappingsAsJson($metadata->fieldMappings),\n                ],\n                JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR,\n            ));\n\n            return;\n        }\n\n        $ui->table(\n            ['Field', 'Value'],\n            array_merge(\n                [\n                    $this->formatField('Name', $metadata->name),\n                    $this->formatField('Root entity name', $metadata->rootEntityName),\n                    $this->formatField('Custom generator definition', $metadata->customGeneratorDefinition),\n                    $this->formatField('Custom repository class', $metadata->customRepositoryClassName),\n                    $this->formatField('Mapped super class?', $metadata->isMappedSuperclass),\n                    $this->formatField('Embedded class?', $metadata->isEmbeddedClass),\n                    $this->formatField('Parent classes', $metadata->parentClasses),\n                    $this->formatField('Sub classes', $metadata->subClasses),\n                    $this->formatField('Embedded classes', $metadata->embeddedClasses),\n                    $this->formatField('Identifier', $metadata->identifier),\n                    $this->formatField('Inheritance type', $metadata->inheritanceType),\n                    $this->formatField('Discriminator column', $metadata->discriminatorColumn),\n                    $this->formatField('Discriminator value', $metadata->discriminatorValue),\n                    $this->formatField('Discriminator map', $metadata->discriminatorMap),\n                    $this->formatField('Generator type', $metadata->generatorType),\n                    $this->formatField('Table', $metadata->table),\n                    $this->formatField('Composite identifier?', $metadata->isIdentifierComposite),\n                    $this->formatField('Foreign identifier?', $metadata->containsForeignIdentifier),\n                    $this->formatField('Enum identifier?', $metadata->containsEnumIdentifier),\n                    $this->formatField('Sequence generator definition', $metadata->sequenceGeneratorDefinition),\n                    $this->formatField('Change tracking policy', $metadata->changeTrackingPolicy),\n                    $this->formatField('Versioned?', $metadata->isVersioned),\n                    $this->formatField('Version field', $metadata->versionField),\n                    $this->formatField('Read only?', $metadata->isReadOnly),\n\n                    $this->formatEntityListeners($metadata->entityListeners),\n                ],\n                [$this->formatField('Association mappings:', '')],\n                $this->formatMappings($metadata->associationMappings),\n                [$this->formatField('Field mappings:', '')],\n                $this->formatMappings($metadata->fieldMappings),\n            ),\n        );\n    }\n\n    /**\n     * Return all mapped entity class names\n     *\n     * @return class-string[]\n     */\n    private function getMappedEntities(EntityManagerInterface $entityManager): array\n    {\n        $entityClassNames = $entityManager->getConfiguration()\n                                          ->getMetadataDriverImpl()\n                                          ->getAllClassNames();\n\n        if (! $entityClassNames) {\n            throw new InvalidArgumentException(\n                'You do not have any mapped Doctrine ORM entities according to the current configuration. ' .\n                'If you have entities or mapping files you should check your mapping configuration for errors.',\n            );\n        }\n\n        return $entityClassNames;\n    }\n\n    /**\n     * Return the class metadata for the given entity\n     * name\n     *\n     * @param string $entityName Full or partial entity name\n     */\n    private function getClassMetadata(\n        string $entityName,\n        EntityManagerInterface $entityManager,\n    ): ClassMetadata {\n        try {\n            return $entityManager->getClassMetadata($entityName);\n        } catch (MappingException) {\n        }\n\n        $matches = array_filter(\n            $this->getMappedEntities($entityManager),\n            static fn ($mappedEntity) => preg_match('{' . preg_quote($entityName) . '}', $mappedEntity),\n        );\n\n        if (! $matches) {\n            throw new InvalidArgumentException(sprintf(\n                'Could not find any mapped Entity classes matching \"%s\"',\n                $entityName,\n            ));\n        }\n\n        if (count($matches) > 1) {\n            throw new InvalidArgumentException(sprintf(\n                'Entity name \"%s\" is ambiguous, possible matches: \"%s\"',\n                $entityName,\n                implode(', ', $matches),\n            ));\n        }\n\n        return $entityManager->getClassMetadata(current($matches));\n    }\n\n    /**\n     * Format the given value for console output\n     */\n    private function formatValue(mixed $value): string\n    {\n        if ($value === '') {\n            return '';\n        }\n\n        if ($value === null) {\n            return '<comment>Null</comment>';\n        }\n\n        if (is_bool($value)) {\n            return '<comment>' . ($value ? 'True' : 'False') . '</comment>';\n        }\n\n        if (empty($value)) {\n            return '<comment>Empty</comment>';\n        }\n\n        if (is_array($value)) {\n            return json_encode(\n                $value,\n                JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR,\n            );\n        }\n\n        if (is_object($value)) {\n            return sprintf('<%s>', get_debug_type($value));\n        }\n\n        if (is_scalar($value)) {\n            return (string) $value;\n        }\n\n        throw new InvalidArgumentException(sprintf('Do not know how to format value \"%s\"', print_r($value, true)));\n    }\n\n    /** @throws JsonException */\n    private function formatValueAsJson(mixed $value): mixed\n    {\n        if (is_object($value)) {\n            $value = (array) $value;\n        }\n\n        if (is_array($value)) {\n            foreach ($value as $k => $v) {\n                $value[$k] = $this->formatValueAsJson($v);\n            }\n        }\n\n        return $value;\n    }\n\n    /**\n     * Add the given label and value to the two column table output\n     *\n     * @param string $label Label for the value\n     * @param mixed  $value A Value to show\n     *\n     * @return string[]\n     * @phpstan-return array{0: string, 1: string}\n     */\n    private function formatField(string $label, mixed $value): array\n    {\n        if ($value === null) {\n            $value = '<comment>None</comment>';\n        }\n\n        return [sprintf('<info>%s</info>', $label), $this->formatValue($value)];\n    }\n\n    /**\n     * Format the association mappings\n     *\n     * @phpstan-param array<string, FieldMapping|AssociationMapping> $propertyMappings\n     *\n     * @return string[][]\n     * @phpstan-return list<array{0: string, 1: string}>\n     */\n    private function formatMappings(array $propertyMappings): array\n    {\n        $output = [];\n\n        foreach ($propertyMappings as $propertyName => $mapping) {\n            $output[] = $this->formatField(sprintf('  %s', $propertyName), '');\n\n            foreach ((array) $mapping as $field => $value) {\n                $output[] = $this->formatField(sprintf('    %s', $field), $this->formatValue($value));\n            }\n        }\n\n        return $output;\n    }\n\n    /**\n     * @param array<string, FieldMapping|AssociationMapping> $propertyMappings\n     *\n     * @return array<string, mixed>\n     */\n    private function formatMappingsAsJson(array $propertyMappings): array\n    {\n        $output = [];\n\n        foreach ($propertyMappings as $propertyName => $mapping) {\n            $output[$propertyName] = $this->formatValueAsJson((array) $mapping);\n        }\n\n        return $output;\n    }\n\n    /**\n     * Format the entity listeners\n     *\n     * @phpstan-param list<object> $entityListeners\n     *\n     * @return string[]\n     * @phpstan-return array{0: string, 1: string}\n     */\n    private function formatEntityListeners(array $entityListeners): array\n    {\n        return $this->formatField('Entity listeners', array_map('get_class', $entityListeners));\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/MappingDescribeCommandFormat.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nenum MappingDescribeCommandFormat: string\n{\n    case TEXT = 'text';\n    case JSON = 'json';\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/RunDqlCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Debug;\nuse LogicException;\nuse RuntimeException;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function constant;\nuse function defined;\nuse function is_numeric;\nuse function sprintf;\nuse function str_replace;\nuse function strtoupper;\n\n/**\n * Command to execute DQL queries in a given EntityManager.\n *\n * @link    www.doctrine-project.org\n */\nclass RunDqlCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:run-dql')\n             ->setDescription('Executes arbitrary DQL directly from the command line')\n             ->addArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('hydrate', null, InputOption::VALUE_REQUIRED, 'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.', 'object')\n             ->addOption('first-result', null, InputOption::VALUE_REQUIRED, 'The first result in the result set.')\n             ->addOption('max-result', null, InputOption::VALUE_REQUIRED, 'The maximum number of results in the result set.')\n             ->addOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of Entity graph.', 7)\n             ->addOption('show-sql', null, InputOption::VALUE_NONE, 'Dump generated SQL instead of executing query')\n             ->setHelp(<<<'EOT'\n                The <info>%command.name%</info> command executes the given DQL query and\n                outputs the results:\n\n                <info>php %command.full_name% \"SELECT u FROM App\\Entity\\User u\"</info>\n\n                You can also optionally specify some additional options like what type of\n                hydration to use when executing the query:\n\n                <info>php %command.full_name% \"SELECT u FROM App\\Entity\\User u\" --hydrate=array</info>\n\n                Additionally you can specify the first result and maximum amount of results to\n                show:\n\n                <info>php %command.full_name% \"SELECT u FROM App\\Entity\\User u\" --first-result=0 --max-result=30</info>\n                EOT);\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = new SymfonyStyle($input, $output);\n\n        $em = $this->getEntityManager($input);\n\n        $dql = $input->getArgument('dql');\n        if ($dql === null) {\n            throw new RuntimeException(\"Argument 'dql' is required in order to execute this command correctly.\");\n        }\n\n        $depth = $input->getOption('depth');\n\n        if (! is_numeric($depth)) {\n            throw new LogicException(\"Option 'depth' must contain an integer value\");\n        }\n\n        $hydrationModeName = (string) $input->getOption('hydrate');\n        $hydrationMode     = 'Doctrine\\ORM\\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName));\n\n        if (! defined($hydrationMode)) {\n            throw new RuntimeException(sprintf(\n                \"Hydration mode '%s' does not exist. It should be either: object. array, scalar or single-scalar.\",\n                $hydrationModeName,\n            ));\n        }\n\n        $query = $em->createQuery($dql);\n\n        $firstResult = $input->getOption('first-result');\n        if ($firstResult !== null) {\n            if (! is_numeric($firstResult)) {\n                throw new LogicException(\"Option 'first-result' must contain an integer value\");\n            }\n\n            $query->setFirstResult((int) $firstResult);\n        }\n\n        $maxResult = $input->getOption('max-result');\n        if ($maxResult !== null) {\n            if (! is_numeric($maxResult)) {\n                throw new LogicException(\"Option 'max-result' must contain an integer value\");\n            }\n\n            $query->setMaxResults((int) $maxResult);\n        }\n\n        if ($input->getOption('show-sql')) {\n            $ui->text($query->getSQL());\n\n            return 0;\n        }\n\n        $resultSet = $query->execute([], constant($hydrationMode));\n\n        $ui->text(Debug::dump($resultSet, (int) $input->getOption('depth')));\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/SchemaTool/AbstractCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\AbstractEntityManagerCommand;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\n/**\n * Base class for CreateCommand, DropCommand and UpdateCommand.\n *\n * @link    www.doctrine-project.org\n */\nabstract class AbstractCommand extends AbstractEntityManagerCommand\n{\n    /** @param mixed[] $metadatas */\n    abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int;\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = new SymfonyStyle($input, $output);\n\n        $em = $this->getEntityManager($input);\n\n        $metadatas = $em->getMetadataFactory()->getAllMetadata();\n\n        if (empty($metadatas)) {\n            $ui->getErrorStyle()->success('No Metadata Classes to process.');\n\n            return 0;\n        }\n\n        return $this->executeSchemaCommand($input, $output, new SchemaTool($em), $metadatas, $ui);\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/SchemaTool/CreateCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function sprintf;\n\n/**\n * Command to create the database schema for a set of classes based on their mappings.\n *\n * @link    www.doctrine-project.org\n */\nclass CreateCommand extends AbstractCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:schema-tool:create')\n             ->setDescription('Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('dump-sql', null, InputOption::VALUE_NONE, 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.')\n             ->setHelp(<<<'EOT'\nProcesses the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.\n\n<comment>Hint:</comment> If you have a database with tables that should not be managed\nby the ORM, you can use a DBAL functionality to filter the tables and sequences down\non a global level:\n\n    $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool {\n        if ($assetName instanceof AbstractAsset) {\n            $assetName = $assetName->getName();\n        }\n\n        return !str_starts_with($assetName, 'audit_');\n    });\nEOT);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int\n    {\n        $dumpSql = $input->getOption('dump-sql') === true;\n\n        if ($dumpSql) {\n            $sqls = $schemaTool->getCreateSchemaSql($metadatas);\n\n            foreach ($sqls as $sql) {\n                $ui->writeln(sprintf('%s;', $sql));\n            }\n\n            return 0;\n        }\n\n        $notificationUi = $ui->getErrorStyle();\n\n        $notificationUi->caution('This operation should not be executed in a production environment!');\n\n        $notificationUi->text('Creating database schema...');\n        $notificationUi->newLine();\n\n        $schemaTool->createSchema($metadatas);\n\n        $notificationUi->success('Database schema created successfully!');\n\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/SchemaTool/DropCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function count;\nuse function sprintf;\n\n/**\n * Command to drop the database schema for a set of classes based on their mappings.\n *\n * @link    www.doctrine-project.org\n */\nclass DropCommand extends AbstractCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:schema-tool:drop')\n             ->setDescription('Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('dump-sql', null, InputOption::VALUE_NONE, 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.')\n             ->addOption('force', 'f', InputOption::VALUE_NONE, \"Don't ask for the deletion of the database, but force the operation to run.\")\n             ->addOption('full-database', null, InputOption::VALUE_NONE, 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.')\n             ->setHelp(<<<'EOT'\nProcesses the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.\nBeware that the complete database is dropped by this command, even tables that are not relevant to your metadata model.\n\n<comment>Hint:</comment> If you have a database with tables that should not be managed\nby the ORM, you can use a DBAL functionality to filter the tables and sequences down\non a global level:\n\n    $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool {\n        if ($assetName instanceof AbstractAsset) {\n            $assetName = $assetName->getName();\n        }\n\n        return !str_starts_with($assetName, 'audit_');\n    });\nEOT);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int\n    {\n        $isFullDatabaseDrop = $input->getOption('full-database');\n        $dumpSql            = $input->getOption('dump-sql') === true;\n        $force              = $input->getOption('force') === true;\n\n        if ($dumpSql) {\n            if ($isFullDatabaseDrop) {\n                $sqls = $schemaTool->getDropDatabaseSQL();\n            } else {\n                $sqls = $schemaTool->getDropSchemaSQL($metadatas);\n            }\n\n            foreach ($sqls as $sql) {\n                $ui->writeln(sprintf('%s;', $sql));\n            }\n\n            return 0;\n        }\n\n        $notificationUi = $ui->getErrorStyle();\n\n        if ($force) {\n            $notificationUi->text('Dropping database schema...');\n            $notificationUi->newLine();\n\n            if ($isFullDatabaseDrop) {\n                $schemaTool->dropDatabase();\n            } else {\n                $schemaTool->dropSchema($metadatas);\n            }\n\n            $notificationUi->success('Database schema dropped successfully!');\n\n            return 0;\n        }\n\n        $notificationUi->caution('This operation should not be executed in a production environment!');\n\n        if ($isFullDatabaseDrop) {\n            $sqls = $schemaTool->getDropDatabaseSQL();\n        } else {\n            $sqls = $schemaTool->getDropSchemaSQL($metadatas);\n        }\n\n        if (empty($sqls)) {\n            $notificationUi->success('Nothing to drop. The database is empty!');\n\n            return 0;\n        }\n\n        $notificationUi->text(\n            [\n                sprintf('The Schema-Tool would execute <info>\"%s\"</info> queries to update the database.', count($sqls)),\n                '',\n                'Please run the operation by passing one - or both - of the following options:',\n                '',\n                sprintf('    <info>%s --force</info> to execute the command', $this->getName()),\n                sprintf('    <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()),\n            ],\n        );\n\n        return 1;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/SchemaTool/UpdateCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function count;\nuse function sprintf;\n\n/**\n * Command to generate the SQL needed to update the database schema to match\n * the current mapping information.\n *\n * @link    www.doctrine-project.org\n */\nclass UpdateCommand extends AbstractCommand\n{\n    protected string $name = 'orm:schema-tool:update';\n\n    protected function configure(): void\n    {\n        $this->setName($this->name)\n             ->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('complete', null, InputOption::VALUE_NONE, 'This option is a no-op, is deprecated and will be removed in 4.0')\n             ->addOption('dump-sql', null, InputOption::VALUE_NONE, 'Dumps the generated SQL statements to the screen (does not execute them).')\n             ->addOption('force', 'f', InputOption::VALUE_NONE, 'Causes the generated SQL statements to be physically executed against your database.')\n             ->setHelp(<<<'EOT'\nThe <info>%command.name%</info> command generates the SQL needed to\nsynchronize the database schema with the current mapping metadata of the\ndefault entity manager.\n\nFor example, if you add metadata for a new column to an entity, this command\nwould generate and output the SQL needed to add the new column to the database:\n\n<info>%command.name% --dump-sql</info>\n\nAlternatively, you can execute the generated queries:\n\n<info>%command.name% --force</info>\n\nIf both options are specified, the queries are output and then executed:\n\n<info>%command.name% --dump-sql --force</info>\n\nFinally, be aware that this task will drop all database assets (e.g. tables,\netc) that are *not* described by the current metadata. In other words, without\nthis option, this task leaves untouched any \"extra\" tables that exist in the\ndatabase, but which aren't described by any metadata.\n\n<comment>Hint:</comment> If you have a database with tables that should not be managed\nby the ORM, you can use a DBAL functionality to filter the tables and sequences down\non a global level:\n\n    $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool {\n        if ($assetName instanceof AbstractAsset) {\n            $assetName = $assetName->getName();\n        }\n\n        return !str_starts_with($assetName, 'audit_');\n    });\nEOT);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int\n    {\n        $notificationUi = $ui->getErrorStyle();\n\n        if ($input->getOption('complete') === true) {\n            Deprecation::trigger(\n                'doctrine/orm',\n                'https://github.com/doctrine/orm/pull/11354',\n                'The --complete option is a no-op, is deprecated and will be removed in Doctrine ORM 4.0.',\n            );\n            $notificationUi->warning('The --complete option is a no-op, is deprecated and will be removed in Doctrine ORM 4.0.');\n        }\n\n        $sqls = $schemaTool->getUpdateSchemaSql($metadatas);\n\n        if (empty($sqls)) {\n            $notificationUi->success('Nothing to update - your database is already in sync with the current entity metadata.');\n\n            return 0;\n        }\n\n        $dumpSql = $input->getOption('dump-sql') === true;\n        $force   = $input->getOption('force') === true;\n\n        if ($dumpSql) {\n            foreach ($sqls as $sql) {\n                $ui->writeln(sprintf('%s;', $sql));\n            }\n        }\n\n        if ($force) {\n            if ($dumpSql) {\n                $notificationUi->newLine();\n            }\n\n            $notificationUi->text('Updating database schema...');\n            $notificationUi->newLine();\n\n            $schemaTool->updateSchema($metadatas);\n\n            $pluralization = count($sqls) === 1 ? 'query was' : 'queries were';\n\n            $notificationUi->text(sprintf('    <info>%s</info> %s executed', count($sqls), $pluralization));\n            $notificationUi->success('Database schema updated successfully!');\n        }\n\n        if ($dumpSql || $force) {\n            return 0;\n        }\n\n        $notificationUi->caution(\n            [\n                'This operation should not be executed in a production environment!',\n                '',\n                'Use the incremental update to detect changes during development and use',\n                'the SQL DDL provided to manually update your database in production.',\n            ],\n        );\n\n        $notificationUi->text(\n            [\n                sprintf('The Schema-Tool would execute <info>\"%s\"</info> queries to update the database.', count($sqls)),\n                '',\n                'Please run the operation by passing one - or both - of the following options:',\n                '',\n                sprintf('    <info>%s --force</info> to execute the command', $this->getName()),\n                sprintf('    <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()),\n            ],\n        );\n\n        return 1;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/Command/ValidateSchemaCommand.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nuse function count;\nuse function sprintf;\n\n/**\n * Command to validate that the current mapping is valid.\n *\n * @link        www.doctrine-project.com\n */\nclass ValidateSchemaCommand extends AbstractEntityManagerCommand\n{\n    protected function configure(): void\n    {\n        $this->setName('orm:validate-schema')\n             ->setDescription('Validate the mapping files')\n             ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')\n             ->addOption('skip-mapping', null, InputOption::VALUE_NONE, 'Skip the mapping validation check')\n             ->addOption('skip-sync', null, InputOption::VALUE_NONE, 'Skip checking if the mapping is in sync with the database')\n             ->addOption('skip-property-types', null, InputOption::VALUE_NONE, 'Skip checking if property types match the Doctrine types')\n             ->setHelp('Validate that the mapping files are correct and in sync with the database.');\n    }\n\n    protected function execute(InputInterface $input, OutputInterface $output): int\n    {\n        $ui = (new SymfonyStyle($input, $output))->getErrorStyle();\n\n        $em        = $this->getEntityManager($input);\n        $validator = new SchemaValidator($em, ! $input->getOption('skip-property-types'));\n        $exit      = 0;\n\n        $ui->section('Mapping');\n\n        if ($input->getOption('skip-mapping')) {\n            $ui->text('<comment>[SKIPPED] The mapping was not checked.</comment>');\n        } else {\n            $errors = $validator->validateMapping();\n            if ($errors) {\n                foreach ($errors as $className => $errorMessages) {\n                    $ui->text(\n                        sprintf(\n                            '<error>[FAIL]</error> The entity-class <comment>%s</comment> mapping is invalid:',\n                            $className,\n                        ),\n                    );\n\n                    $ui->listing($errorMessages);\n                    $ui->newLine();\n                }\n\n                ++$exit;\n            } else {\n                $ui->success('The mapping files are correct.');\n            }\n        }\n\n        $ui->section('Database');\n\n        if ($input->getOption('skip-sync')) {\n            $ui->text('<comment>[SKIPPED] The database was not checked for synchronicity.</comment>');\n        } elseif (! $validator->schemaInSyncWithMetadata()) {\n            $ui->error('The database schema is not in sync with the current mapping file.');\n\n            if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {\n                $sqls = $validator->getUpdateSchemaList();\n                $ui->comment(sprintf('<info>%d</info> schema diff(s) detected:', count($sqls)));\n                foreach ($sqls as $sql) {\n                    $ui->text(sprintf('    %s;', $sql));\n                }\n            }\n\n            $exit += 2;\n        } else {\n            $ui->success('The database schema is in sync with the mapping files.');\n        }\n\n        return $exit;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/ConsoleRunner.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console;\n\nuse Composer\\InstalledVersions;\nuse Doctrine\\DBAL\\Tools\\Console as DBALConsole;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\ConnectionFromManagerProvider;\nuse OutOfBoundsException;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Command\\Command as SymfonyCommand;\n\nuse function assert;\nuse function class_exists;\n\n/**\n * Handles running the Console Tools inside Symfony Console context.\n */\nfinal class ConsoleRunner\n{\n    use ApplicationCompatibility;\n\n    /**\n     * Runs console with the given helper set.\n     *\n     * @param SymfonyCommand[] $commands\n     */\n    public static function run(EntityManagerProvider $entityManagerProvider, array $commands = []): void\n    {\n        $cli = self::createApplication($entityManagerProvider, $commands);\n        $cli->run();\n    }\n\n    /**\n     * Creates a console application with the given helperset and\n     * optional commands.\n     *\n     * @param SymfonyCommand[] $commands\n     *\n     * @throws OutOfBoundsException\n     */\n    public static function createApplication(\n        EntityManagerProvider $entityManagerProvider,\n        array $commands = [],\n    ): Application {\n        $version = InstalledVersions::getVersion('doctrine/orm');\n        assert($version !== null);\n\n        $cli = new Application('Doctrine Command Line Interface', $version);\n        $cli->setCatchExceptions(true);\n\n        self::addCommands($cli, $entityManagerProvider);\n        $cli->addCommands($commands);\n\n        return $cli;\n    }\n\n    public static function addCommands(Application $cli, EntityManagerProvider $entityManagerProvider): void\n    {\n        $connectionProvider = new ConnectionFromManagerProvider($entityManagerProvider);\n\n        if (class_exists(DBALConsole\\Command\\ReservedWordsCommand::class)) {\n            self::addCommandToApplication(\n                $cli,\n                new DBALConsole\\Command\\ReservedWordsCommand($connectionProvider),\n            );\n        }\n\n        $cli->addCommands(\n            [\n                // DBAL Commands\n                new DBALConsole\\Command\\RunSqlCommand($connectionProvider),\n\n                // ORM Commands\n                new Command\\ClearCache\\CollectionRegionCommand($entityManagerProvider),\n                new Command\\ClearCache\\EntityRegionCommand($entityManagerProvider),\n                new Command\\ClearCache\\MetadataCommand($entityManagerProvider),\n                new Command\\ClearCache\\QueryCommand($entityManagerProvider),\n                new Command\\ClearCache\\QueryRegionCommand($entityManagerProvider),\n                new Command\\ClearCache\\ResultCommand($entityManagerProvider),\n                new Command\\SchemaTool\\CreateCommand($entityManagerProvider),\n                new Command\\SchemaTool\\UpdateCommand($entityManagerProvider),\n                new Command\\SchemaTool\\DropCommand($entityManagerProvider),\n                new Command\\GenerateProxiesCommand($entityManagerProvider),\n                new Command\\RunDqlCommand($entityManagerProvider),\n                new Command\\ValidateSchemaCommand($entityManagerProvider),\n                new Command\\InfoCommand($entityManagerProvider),\n                new Command\\MappingDescribeCommand($entityManagerProvider),\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/EntityManagerProvider/ConnectionFromManagerProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\n\nfinal class ConnectionFromManagerProvider implements ConnectionProvider\n{\n    public function __construct(private readonly EntityManagerProvider $entityManagerProvider)\n    {\n    }\n\n    public function getDefaultConnection(): Connection\n    {\n        return $this->entityManagerProvider->getDefaultManager()->getConnection();\n    }\n\n    public function getConnection(string $name): Connection\n    {\n        return $this->entityManagerProvider->getManager($name)->getConnection();\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/EntityManagerProvider/SingleManagerProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\n\nfinal class SingleManagerProvider implements EntityManagerProvider\n{\n    public function __construct(\n        private readonly EntityManagerInterface $entityManager,\n        private readonly string $defaultManagerName = 'default',\n    ) {\n    }\n\n    public function getDefaultManager(): EntityManagerInterface\n    {\n        return $this->entityManager;\n    }\n\n    public function getManager(string $name): EntityManagerInterface\n    {\n        if ($name !== $this->defaultManagerName) {\n            throw UnknownManagerException::unknownManager($name, [$this->defaultManagerName]);\n        }\n\n        return $this->entityManager;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/EntityManagerProvider/UnknownManagerException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\n\nuse OutOfBoundsException;\n\nuse function implode;\nuse function sprintf;\n\nfinal class UnknownManagerException extends OutOfBoundsException\n{\n    /** @phpstan-param list<string> $knownManagers */\n    public static function unknownManager(string $unknownManager, array $knownManagers = []): self\n    {\n        return new self(sprintf(\n            'Requested unknown entity manager: %s, known managers: %s',\n            $unknownManager,\n            implode(', ', $knownManagers),\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Tools/Console/EntityManagerProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\n\ninterface EntityManagerProvider\n{\n    public function getDefaultManager(): EntityManagerInterface;\n\n    public function getManager(string $name): EntityManagerInterface;\n}\n"
  },
  {
    "path": "src/Tools/Console/MetadataFilter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Console;\n\nuse ArrayIterator;\nuse Countable;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\nuse FilterIterator;\nuse RuntimeException;\n\nuse function assert;\nuse function count;\nuse function iterator_to_array;\nuse function preg_match;\nuse function sprintf;\n\n/**\n * Used by CLI Tools to restrict entity-based commands to given patterns.\n *\n * @link        www.doctrine-project.com\n */\nclass MetadataFilter extends FilterIterator implements Countable\n{\n    /** @var mixed[] */\n    private array $filter = [];\n\n    /**\n     * Filter Metadatas by one or more filter options.\n     *\n     * @param ClassMetadata[] $metadatas\n     * @param string[]|string $filter\n     *\n     * @return ClassMetadata[]\n     */\n    public static function filter(array $metadatas, array|string $filter): array\n    {\n        $metadatas = new MetadataFilter(new ArrayIterator($metadatas), $filter);\n\n        return iterator_to_array($metadatas);\n    }\n\n    /** @param mixed[]|string $filter */\n    public function __construct(ArrayIterator $metadata, array|string $filter)\n    {\n        $this->filter = (array) $filter;\n\n        parent::__construct($metadata);\n    }\n\n    public function accept(): bool\n    {\n        if (count($this->filter) === 0) {\n            return true;\n        }\n\n        $it       = $this->getInnerIterator();\n        $metadata = $it->current();\n\n        foreach ($this->filter as $filter) {\n            $pregResult = preg_match('/' . $filter . '/', $metadata->getName());\n\n            if ($pregResult === false) {\n                throw new RuntimeException(\n                    sprintf(\"Error while evaluating regex '/%s/'.\", $filter),\n                );\n            }\n\n            if ($pregResult) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /** @return ArrayIterator<int, ClassMetadata> */\n    public function getInnerIterator(): ArrayIterator\n    {\n        $innerIterator = parent::getInnerIterator();\n\n        assert($innerIterator instanceof ArrayIterator);\n\n        return $innerIterator;\n    }\n\n    public function count(): int\n    {\n        return count($this->getInnerIterator());\n    }\n}\n"
  },
  {
    "path": "src/Tools/Debug.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse ArrayIterator;\nuse ArrayObject;\nuse DateTimeInterface;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\Persistence\\Proxy;\nuse stdClass;\n\nuse function array_keys;\nuse function count;\nuse function end;\nuse function explode;\nuse function extension_loaded;\nuse function html_entity_decode;\nuse function ini_get;\nuse function ini_set;\nuse function is_array;\nuse function is_object;\nuse function ob_end_clean;\nuse function ob_get_contents;\nuse function ob_start;\nuse function strip_tags;\nuse function var_dump;\n\n/**\n * Static class containing most used debug methods.\n *\n * @internal\n *\n * @link   www.doctrine-project.org\n */\nfinal class Debug\n{\n    /**\n     * Private constructor (prevents instantiation).\n     */\n    private function __construct()\n    {\n    }\n\n    /**\n     * Prints a dump of the public, protected and private properties of $var.\n     *\n     * @link https://xdebug.org/\n     *\n     * @param mixed $var      The variable to dump.\n     * @param int   $maxDepth The maximum nesting level for object properties.\n     */\n    public static function dump(mixed $var, int $maxDepth = 2): string\n    {\n        $html = ini_get('html_errors');\n\n        if ($html !== '1') {\n            ini_set('html_errors', 'on');\n        }\n\n        if (extension_loaded('xdebug')) {\n            $previousDepth = ini_get('xdebug.var_display_max_depth');\n            ini_set('xdebug.var_display_max_depth', (string) $maxDepth);\n        }\n\n        try {\n            $var = self::export($var, $maxDepth);\n\n            ob_start();\n            var_dump($var);\n\n            $dump = ob_get_contents();\n\n            ob_end_clean();\n\n            $dumpText = strip_tags(html_entity_decode($dump));\n        } finally {\n            ini_set('html_errors', $html);\n\n            if (isset($previousDepth)) {\n                ini_set('xdebug.var_display_max_depth', $previousDepth);\n            }\n        }\n\n        return $dumpText;\n    }\n\n    public static function export(mixed $var, int $maxDepth): mixed\n    {\n        if ($var instanceof Collection) {\n            $var = $var->toArray();\n        }\n\n        if (! $maxDepth) {\n            return is_object($var) ? $var::class\n                : (is_array($var) ? 'Array(' . count($var) . ')' : $var);\n        }\n\n        if (is_array($var)) {\n            $return = [];\n\n            foreach ($var as $k => $v) {\n                $return[$k] = self::export($v, $maxDepth - 1);\n            }\n\n            return $return;\n        }\n\n        if (! is_object($var)) {\n            return $var;\n        }\n\n        $return = new stdClass();\n        if ($var instanceof DateTimeInterface) {\n            $return->__CLASS__ = $var::class;\n            $return->date      = $var->format('c');\n            $return->timezone  = $var->getTimezone()->getName();\n\n            return $return;\n        }\n\n        $return->__CLASS__ = DefaultProxyClassNameResolver::getClass($var);\n\n        if ($var instanceof Proxy) {\n            $return->__IS_PROXY__          = true;\n            $return->__PROXY_INITIALIZED__ = $var->__isInitialized();\n        }\n\n        if ($var instanceof ArrayObject || $var instanceof ArrayIterator) {\n            $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);\n        }\n\n        return self::fillReturnWithClassAttributes($var, $return, $maxDepth);\n    }\n\n    /**\n     * Fill the $return variable with class attributes\n     * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075}\n     */\n    private static function fillReturnWithClassAttributes(object $var, stdClass $return, int $maxDepth): stdClass\n    {\n        $clone = (array) $var;\n\n        foreach (array_keys($clone) as $key) {\n            $aux  = explode(\"\\0\", (string) $key);\n            $name = end($aux);\n            if ($aux[0] === '') {\n                $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private');\n            }\n\n            $return->$name = self::export($clone[$key], $maxDepth - 1);\n        }\n\n        return $return;\n    }\n}\n"
  },
  {
    "path": "src/Tools/DebugUnitOfWorkListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\OnFlushEventArgs;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\UnitOfWork;\nuse ReflectionObject;\n\nuse function count;\nuse function fclose;\nuse function fopen;\nuse function fwrite;\nuse function gettype;\nuse function is_object;\nuse function spl_object_id;\n\n/**\n * Use this logger to dump the identity map during the onFlush event. This is useful for debugging\n * weird UnitOfWork behavior with complex operations.\n */\nclass DebugUnitOfWorkListener\n{\n    /**\n     * Pass a stream and context information for the debugging session.\n     *\n     * The stream can be php://output to print to the screen.\n     */\n    public function __construct(\n        private readonly string $file = 'php://output',\n        private readonly string $context = '',\n    ) {\n    }\n\n    public function onFlush(OnFlushEventArgs $args): void\n    {\n        $this->dumpIdentityMap($args->getObjectManager());\n    }\n\n    /**\n     * Dumps the contents of the identity map into a stream.\n     */\n    public function dumpIdentityMap(EntityManagerInterface $em): void\n    {\n        $uow         = $em->getUnitOfWork();\n        $identityMap = $uow->getIdentityMap();\n\n        $fh = fopen($this->file, 'xb+');\n        if (count($identityMap) === 0) {\n            fwrite($fh, 'Flush Operation [' . $this->context . \"] - Empty identity map.\\n\");\n\n            return;\n        }\n\n        fwrite($fh, 'Flush Operation [' . $this->context . \"] - Dumping identity map:\\n\");\n        foreach ($identityMap as $className => $map) {\n            fwrite($fh, 'Class: ' . $className . \"\\n\");\n\n            foreach ($map as $entity) {\n                fwrite($fh, ' Entity: ' . $this->getIdString($entity, $uow) . ' ' . spl_object_id($entity) . \"\\n\");\n                fwrite($fh, \"  Associations:\\n\");\n\n                $cm = $em->getClassMetadata($className);\n\n                foreach ($cm->associationMappings as $field => $assoc) {\n                    fwrite($fh, '   ' . $field . ' ');\n                    $value = $cm->getFieldValue($entity, $field);\n\n                    if ($assoc->isToOne()) {\n                        if ($value === null) {\n                            fwrite($fh, \" NULL\\n\");\n                        } else {\n                            if ($uow->isUninitializedObject($value)) {\n                                fwrite($fh, '[PROXY] ');\n                            }\n\n                            fwrite($fh, $this->getIdString($value, $uow) . ' ' . spl_object_id($value) . \"\\n\");\n                        }\n                    } else {\n                        $initialized = ! ($value instanceof PersistentCollection) || $value->isInitialized();\n                        if ($value === null) {\n                            fwrite($fh, \" NULL\\n\");\n                        } elseif ($initialized) {\n                            fwrite($fh, '[INITIALIZED] ' . $this->getType($value) . ' ' . count($value) . \" elements\\n\");\n\n                            foreach ($value as $obj) {\n                                fwrite($fh, '    ' . $this->getIdString($obj, $uow) . ' ' . spl_object_id($obj) . \"\\n\");\n                            }\n                        } else {\n                            fwrite($fh, '[PROXY] ' . $this->getType($value) . \" unknown element size\\n\");\n                            foreach ($value->unwrap() as $obj) {\n                                fwrite($fh, '    ' . $this->getIdString($obj, $uow) . ' ' . spl_object_id($obj) . \"\\n\");\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        fclose($fh);\n    }\n\n    private function getType(mixed $var): string\n    {\n        if (is_object($var)) {\n            $refl = new ReflectionObject($var);\n\n            return $refl->getShortName();\n        }\n\n        return gettype($var);\n    }\n\n    private function getIdString(object $entity, UnitOfWork $uow): string\n    {\n        if ($uow->isInIdentityMap($entity)) {\n            $ids      = $uow->getEntityIdentifier($entity);\n            $idstring = '';\n\n            foreach ($ids as $k => $v) {\n                $idstring .= $k . '=' . $v;\n            }\n        } else {\n            $idstring = 'NEWOBJECT ';\n        }\n\n        $state = $uow->getEntityState($entity);\n\n        if ($state === UnitOfWork::STATE_NEW) {\n            $idstring .= ' [NEW]';\n        } elseif ($state === UnitOfWork::STATE_REMOVED) {\n            $idstring .= ' [REMOVED]';\n        } elseif ($state === UnitOfWork::STATE_MANAGED) {\n            $idstring .= ' [MANAGED]';\n        } elseif ($state === UnitOfWork::STATE_DETACHED) {\n            $idstring .= ' [DETACHED]';\n        }\n\n        return $idstring;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Event/GenerateSchemaEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Event;\n\nuse Doctrine\\Common\\EventArgs;\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\ORM\\EntityManagerInterface;\n\n/**\n * Event Args used for the Events::postGenerateSchema event.\n *\n * @link        www.doctrine-project.com\n */\nclass GenerateSchemaEventArgs extends EventArgs\n{\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        private readonly Schema $schema,\n    ) {\n    }\n\n    public function getEntityManager(): EntityManagerInterface\n    {\n        return $this->em;\n    }\n\n    public function getSchema(): Schema\n    {\n        return $this->schema;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Event/GenerateSchemaTableEventArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Event;\n\nuse Doctrine\\Common\\EventArgs;\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * Event Args used for the Events::postGenerateSchemaTable event.\n *\n * @link        www.doctrine-project.com\n */\nclass GenerateSchemaTableEventArgs extends EventArgs\n{\n    public function __construct(\n        private readonly ClassMetadata $classMetadata,\n        private readonly Schema $schema,\n        private readonly Table $classTable,\n    ) {\n    }\n\n    public function getClassMetadata(): ClassMetadata\n    {\n        return $this->classMetadata;\n    }\n\n    public function getSchema(): Schema\n    {\n        return $this->schema;\n    }\n\n    public function getClassTable(): Table\n    {\n        return $this->classTable;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Exception/MissingColumnException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Exception;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nuse function sprintf;\n\nfinal class MissingColumnException extends LogicException implements ORMException\n{\n    public static function fromColumnSourceAndTarget(string $column, string $source, string $target): self\n    {\n        return new self(sprintf(\n            'Column name \"%s\" referenced for relation from %s towards %s does not exist.',\n            $column,\n            $source,\n            $target,\n        ));\n    }\n}\n"
  },
  {
    "path": "src/Tools/Exception/NotSupported.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Exception;\n\nuse Doctrine\\ORM\\Exception\\SchemaToolException;\nuse LogicException;\n\nfinal class NotSupported extends LogicException implements SchemaToolException\n{\n    public static function create(): self\n    {\n        return new self('This behaviour is (currently) not supported by Doctrine 2');\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/CountOutputWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLServerPlatform;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\nuse RuntimeException;\n\nuse function array_diff;\nuse function array_keys;\nuse function assert;\nuse function count;\nuse function implode;\nuse function reset;\nuse function sprintf;\n\n/**\n * Wraps the query in order to accurately count the root objects.\n *\n * Given a DQL like `SELECT u FROM User u` it will generate an SQL query like:\n * SELECT COUNT(*) (SELECT DISTINCT <id> FROM (<original SQL>))\n *\n * Works with composite keys but cannot deal with queries that have multiple\n * root entities (e.g. `SELECT f, b from Foo, Bar`)\n *\n * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL)\n * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery\n * that will most likely be executed next can be read from the native SQL cache.\n *\n * @phpstan-import-type QueryComponent from Parser\n */\nclass CountOutputWalker extends SqlOutputWalker\n{\n    private readonly AbstractPlatform $platform;\n    private readonly ResultSetMapping $rsm;\n\n    /**\n     * {@inheritDoc}\n     */\n    public function __construct(Query $query, ParserResult $parserResult, array $queryComponents)\n    {\n        $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();\n        $this->rsm      = $parserResult->getResultSetMapping();\n\n        parent::__construct($query, $parserResult, $queryComponents);\n    }\n\n    protected function createSqlForFinalizer(SelectStatement $selectStatement): string\n    {\n        if ($this->platform instanceof SQLServerPlatform) {\n            $selectStatement->orderByClause = null;\n        }\n\n        $sql = parent::createSqlForFinalizer($selectStatement);\n\n        if ($selectStatement->groupByClause) {\n            return sprintf(\n                'SELECT COUNT(*) AS dctrn_count FROM (%s) dctrn_table',\n                $sql,\n            );\n        }\n\n        // Find out the SQL alias of the identifier column of the root entity\n        // It may be possible to make this work with multiple root entities but that\n        // would probably require issuing multiple queries or doing a UNION SELECT\n        // so for now, It's not supported.\n\n        // Get the root entity and alias from the AST fromClause\n        $from = $selectStatement->fromClause->identificationVariableDeclarations;\n        if (count($from) > 1) {\n            throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');\n        }\n\n        $fromRoot       = reset($from);\n        $rootAlias      = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass      = $this->getMetadataForDqlAlias($rootAlias);\n        $rootIdentifier = $rootClass->identifier;\n\n        // For every identifier, find out the SQL alias by combing through the ResultSetMapping\n        $sqlIdentifier = [];\n        foreach ($rootIdentifier as $property) {\n            if (isset($rootClass->fieldMappings[$property])) {\n                foreach (array_keys($this->rsm->fieldMappings, $property, true) as $alias) {\n                    if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {\n                        $sqlIdentifier[$property] = $alias;\n                    }\n                }\n            }\n\n            if (isset($rootClass->associationMappings[$property])) {\n                $association = $rootClass->associationMappings[$property];\n                assert($association->isToOneOwningSide());\n                $joinColumn = $association->joinColumns[0]->name;\n\n                foreach (array_keys($this->rsm->metaMappings, $joinColumn, true) as $alias) {\n                    if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {\n                        $sqlIdentifier[$property] = $alias;\n                    }\n                }\n            }\n        }\n\n        if (count($rootIdentifier) !== count($sqlIdentifier)) {\n            throw new RuntimeException(sprintf(\n                'Not all identifier properties can be found in the ResultSetMapping: %s',\n                implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))),\n            ));\n        }\n\n        // Build the counter query\n        return sprintf(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table',\n            implode(', ', $sqlIdentifier),\n            $sql,\n        );\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/CountWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse RuntimeException;\n\nuse function count;\nuse function reset;\n\n/**\n * Replaces the selectClause of the AST with a COUNT statement.\n */\nclass CountWalker extends TreeWalkerAdapter\n{\n    /**\n     * Distinct mode hint name.\n     */\n    public const HINT_DISTINCT = 'doctrine_paginator.distinct';\n\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        if ($selectStatement->havingClause) {\n            throw new RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination');\n        }\n\n        // Get the root entity and alias from the AST fromClause\n        $from = $selectStatement->fromClause->identificationVariableDeclarations;\n\n        if (count($from) > 1) {\n            throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');\n        }\n\n        $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT);\n\n        $countPathExpressionOrLiteral = '*';\n        if ($distinct) {\n            $fromRoot            = reset($from);\n            $rootAlias           = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n            $rootClass           = $this->getMetadataForDqlAlias($rootAlias);\n            $identifierFieldName = $rootClass->getSingleIdentifierFieldName();\n\n            $pathType = PathExpression::TYPE_STATE_FIELD;\n            if (isset($rootClass->associationMappings[$identifierFieldName])) {\n                $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;\n            }\n\n            $countPathExpressionOrLiteral       = new PathExpression(\n                PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,\n                $rootAlias,\n                $identifierFieldName,\n            );\n            $countPathExpressionOrLiteral->type = $pathType;\n        }\n\n        $selectStatement->selectClause->selectExpressions = [\n            new SelectExpression(\n                new AggregateExpression('count', $countPathExpressionOrLiteral, $distinct),\n                null,\n            ),\n        ];\n\n        // ORDER BY is not needed, only increases query execution through unnecessary sorting.\n        $selectStatement->orderByClause = null;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/Exception/RowNumberOverFunctionNotEnabled.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination\\Exception;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\nfinal class RowNumberOverFunctionNotEnabled extends LogicException implements ORMException\n{\n    public static function create(): self\n    {\n        return new self('The RowNumberOverFunction is not intended for, nor is it enabled for use in DQL.');\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/LimitSubqueryOutputWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\DB2Platform;\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLServerPlatform;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\AST\\OrderByClause;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\AST\\Subselect;\nuse Doctrine\\ORM\\Query\\Exec\\SingleSelectSqlFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\nuse LogicException;\nuse RuntimeException;\n\nuse function array_diff;\nuse function array_keys;\nuse function assert;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function is_string;\nuse function method_exists;\nuse function preg_replace;\nuse function reset;\nuse function sprintf;\nuse function strrpos;\nuse function substr;\n\n/**\n * Wraps the query in order to select root entity IDs for pagination.\n *\n * Given a DQL like `SELECT u FROM User u` it will generate an SQL query like:\n * SELECT DISTINCT <id> FROM (<original SQL>) LIMIT x OFFSET y\n *\n * Works with composite keys but cannot deal with queries that have multiple\n * root entities (e.g. `SELECT f, b from Foo, Bar`)\n *\n * @phpstan-import-type QueryComponent from Parser\n */\nclass LimitSubqueryOutputWalker extends SqlOutputWalker\n{\n    private const ORDER_BY_PATH_EXPRESSION = '/(?<![a-z0-9_])%s\\.%s(?![a-z0-9_])/i';\n\n    private readonly AbstractPlatform $platform;\n    private readonly ResultSetMapping $rsm;\n    private readonly int $firstResult;\n    private readonly int|null $maxResults;\n    private readonly EntityManagerInterface $em;\n    private readonly QuoteStrategy $quoteStrategy;\n\n    /** @var list<PathExpression> */\n    private array $orderByPathExpressions = [];\n\n    /**\n     * We don't want to add path expressions from sub-selects into the select clause of the containing query.\n     * This state flag simply keeps track on whether we are walking on a subquery or not\n     */\n    private bool $inSubSelect = false;\n\n    /**\n     * Stores various parameters that are otherwise unavailable\n     * because Doctrine\\ORM\\Query\\SqlWalker keeps everything private without\n     * accessors.\n     *\n     * {@inheritDoc}\n     */\n    public function __construct(\n        Query $query,\n        ParserResult $parserResult,\n        array $queryComponents,\n    ) {\n        $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();\n        $this->rsm      = $parserResult->getResultSetMapping();\n\n        $cloneQuery = clone $query;\n\n        $cloneQuery->setParameters(clone $query->getParameters());\n        $cloneQuery->setCacheable(false);\n\n        foreach ($query->getHints() as $name => $value) {\n            $cloneQuery->setHint($name, $value);\n        }\n\n        // Reset limit and offset\n        $this->firstResult = $cloneQuery->getFirstResult();\n        $this->maxResults  = $cloneQuery->getMaxResults();\n        $cloneQuery->setFirstResult(0)->setMaxResults(null);\n\n        $this->em            = $cloneQuery->getEntityManager();\n        $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();\n\n        parent::__construct($cloneQuery, $parserResult, $queryComponents);\n    }\n\n    /**\n     * Check if the platform supports the ROW_NUMBER window function.\n     */\n    private function platformSupportsRowNumber(): bool\n    {\n        return $this->platform instanceof PostgreSQLPlatform\n            || $this->platform instanceof SQLServerPlatform\n            || $this->platform instanceof OraclePlatform\n            || $this->platform instanceof DB2Platform\n            || (method_exists($this->platform, 'supportsRowNumberFunction')\n                && $this->platform->supportsRowNumberFunction());\n    }\n\n    /**\n     * Rebuilds a select statement's order by clause for use in a\n     * ROW_NUMBER() OVER() expression.\n     */\n    private function rebuildOrderByForRowNumber(SelectStatement $AST): void\n    {\n        $orderByClause              = $AST->orderByClause;\n        $selectAliasToExpressionMap = [];\n        // Get any aliases that are available for select expressions.\n        foreach ($AST->selectClause->selectExpressions as $selectExpression) {\n            if ($selectExpression->fieldIdentificationVariable !== null) {\n                $selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression;\n            }\n        }\n\n        // Rebuild string orderby expressions to use the select expression they're referencing\n        foreach ($orderByClause->orderByItems as $orderByItem) {\n            if (is_string($orderByItem->expression) && isset($selectAliasToExpressionMap[$orderByItem->expression])) {\n                $orderByItem->expression = $selectAliasToExpressionMap[$orderByItem->expression];\n            }\n        }\n\n        $func                                   = new RowNumberOverFunction('dctrn_rownum');\n        $func->orderByClause                    = $AST->orderByClause;\n        $AST->selectClause->selectExpressions[] = new SelectExpression($func, 'dctrn_rownum', true);\n\n        // No need for an order by clause, we'll order by rownum in the outer query.\n        $AST->orderByClause = null;\n    }\n\n    public function walkSelectStatement(SelectStatement $selectStatement): string\n    {\n        $sqlFinalizer = $this->getFinalizer($selectStatement);\n\n        $query = $this->getQuery();\n\n        $abstractSqlExecutor = $sqlFinalizer->createExecutor($query);\n\n        return $abstractSqlExecutor->getSqlStatements();\n    }\n\n    public function getFinalizer(AST\\DeleteStatement|AST\\UpdateStatement|AST\\SelectStatement $AST): SqlFinalizer\n    {\n        if (! $AST instanceof SelectStatement) {\n            throw new LogicException(self::class . ' is to be used on SelectStatements only');\n        }\n\n        if ($this->platformSupportsRowNumber()) {\n            $sql = $this->createSqlWithRowNumber($AST);\n        } else {\n            $sql = $this->createSqlWithoutRowNumber($AST);\n        }\n\n        return new SingleSelectSqlFinalizer($sql);\n    }\n\n    /**\n     * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT.\n     * This method is for use with platforms which support ROW_NUMBER.\n     *\n     * @throws RuntimeException\n     */\n    public function walkSelectStatementWithRowNumber(SelectStatement $AST): string\n    {\n        // Apply the limit and offset.\n        return $this->platform->modifyLimitQuery(\n            $this->createSqlWithRowNumber($AST),\n            $this->maxResults,\n            $this->firstResult,\n        );\n    }\n\n    private function createSqlWithRowNumber(SelectStatement $AST): string\n    {\n        $hasOrderBy   = false;\n        $outerOrderBy = ' ORDER BY dctrn_minrownum ASC';\n        $orderGroupBy = '';\n        if ($AST->orderByClause instanceof OrderByClause) {\n            $hasOrderBy = true;\n            $this->rebuildOrderByForRowNumber($AST);\n        }\n\n        $innerSql = $this->getInnerSQL($AST);\n\n        $sqlIdentifier = $this->getSQLIdentifier($AST);\n\n        if ($hasOrderBy) {\n            $orderGroupBy    = ' GROUP BY ' . implode(', ', $sqlIdentifier);\n            $sqlIdentifier[] = 'MIN(' . $this->walkResultVariable('dctrn_rownum') . ') AS dctrn_minrownum';\n        }\n\n        // Build the counter query\n        $sql = sprintf(\n            'SELECT DISTINCT %s FROM (%s) dctrn_result',\n            implode(', ', $sqlIdentifier),\n            $innerSql,\n        );\n\n        if ($hasOrderBy) {\n            $sql .= $orderGroupBy . $outerOrderBy;\n        }\n\n        // Add the columns to the ResultSetMapping. It's not really nice but\n        // it works. Preferably I'd clear the RSM or simply create a new one\n        // but that is not possible from inside the output walker, so we dirty\n        // up the one we have.\n        foreach ($sqlIdentifier as $property => $alias) {\n            $this->rsm->addScalarResult($alias, $property);\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT.\n     * This method is for platforms which DO NOT support ROW_NUMBER.\n     *\n     * @throws RuntimeException\n     */\n    public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, bool $addMissingItemsFromOrderByToSelect = true): string\n    {\n        // Apply the limit and offset.\n        return $this->platform->modifyLimitQuery(\n            $this->createSqlWithoutRowNumber($AST, $addMissingItemsFromOrderByToSelect),\n            $this->maxResults,\n            $this->firstResult,\n        );\n    }\n\n    private function createSqlWithoutRowNumber(SelectStatement $AST, bool $addMissingItemsFromOrderByToSelect = true): string\n    {\n        // We don't want to call this recursively!\n        if ($AST->orderByClause instanceof OrderByClause && $addMissingItemsFromOrderByToSelect) {\n            // In the case of ordering a query by columns from joined tables, we\n            // must add those columns to the select clause of the query BEFORE\n            // the SQL is generated.\n            $this->addMissingItemsFromOrderByToSelect($AST);\n        }\n\n        // Remove order by clause from the inner query\n        // It will be re-appended in the outer select generated by this method\n        $orderByClause      = $AST->orderByClause;\n        $AST->orderByClause = null;\n\n        $innerSql = $this->getInnerSQL($AST);\n\n        $sqlIdentifier = $this->getSQLIdentifier($AST);\n\n        // Build the counter query\n        $sql = sprintf(\n            'SELECT DISTINCT %s FROM (%s) dctrn_result',\n            implode(', ', $sqlIdentifier),\n            $innerSql,\n        );\n\n        // https://github.com/doctrine/orm/issues/2630\n        $sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause);\n\n        // Add the columns to the ResultSetMapping. It's not really nice but\n        // it works. Preferably I'd clear the RSM or simply create a new one\n        // but that is not possible from inside the output walker, so we dirty\n        // up the one we have.\n        foreach ($sqlIdentifier as $property => $alias) {\n            $this->rsm->addScalarResult($alias, $property);\n        }\n\n        // Restore orderByClause\n        $AST->orderByClause = $orderByClause;\n\n        return $sql;\n    }\n\n    /**\n     * Finds all PathExpressions in an AST's OrderByClause, and ensures that\n     * the referenced fields are present in the SelectClause of the passed AST.\n     */\n    private function addMissingItemsFromOrderByToSelect(SelectStatement $AST): void\n    {\n        $this->orderByPathExpressions = [];\n\n        // We need to do this in another walker because otherwise we'll end up\n        // polluting the state of this one.\n        $walker = clone $this;\n\n        // This will populate $orderByPathExpressions via\n        // LimitSubqueryOutputWalker::walkPathExpression, which will be called\n        // as the select statement is walked. We'll end up with an array of all\n        // path expressions referenced in the query.\n        $walker->walkSelectStatementWithoutRowNumber($AST, false);\n        $orderByPathExpressions = $walker->getOrderByPathExpressions();\n\n        // Get a map of referenced identifiers to field names.\n        $selects = [];\n        foreach ($orderByPathExpressions as $pathExpression) {\n            assert($pathExpression->field !== null);\n            $idVar = $pathExpression->identificationVariable;\n            $field = $pathExpression->field;\n            if (! isset($selects[$idVar])) {\n                $selects[$idVar] = [];\n            }\n\n            $selects[$idVar][$field] = true;\n        }\n\n        // Loop the select clause of the AST and exclude items from $select\n        // that are already being selected in the query.\n        foreach ($AST->selectClause->selectExpressions as $selectExpression) {\n            if ($selectExpression instanceof SelectExpression) {\n                $idVar = $selectExpression->expression;\n                if (! is_string($idVar)) {\n                    continue;\n                }\n\n                $field = $selectExpression->fieldIdentificationVariable;\n                if ($field === null) {\n                    // No need to add this select, as we're already fetching the whole object.\n                    unset($selects[$idVar]);\n                } else {\n                    unset($selects[$idVar][$field]);\n                }\n            }\n        }\n\n        // Add select items which were not excluded to the AST's select clause.\n        foreach ($selects as $idVar => $fields) {\n            $AST->selectClause->selectExpressions[] = new SelectExpression($idVar, null, true);\n        }\n    }\n\n    /**\n     * Generates new SQL for statements with an order by clause\n     *\n     * @param mixed[] $sqlIdentifier\n     */\n    private function preserveSqlOrdering(\n        array $sqlIdentifier,\n        string $innerSql,\n        string $sql,\n        OrderByClause|null $orderByClause,\n    ): string {\n        // If the sql statement has an order by clause, we need to wrap it in a new select distinct statement\n        if (! $orderByClause) {\n            return $sql;\n        }\n\n        // now only select distinct identifier\n        return sprintf(\n            'SELECT DISTINCT %s FROM (%s) dctrn_result',\n            implode(', ', $sqlIdentifier),\n            $this->recreateInnerSql($orderByClause, $sqlIdentifier, $innerSql),\n        );\n    }\n\n    /**\n     * Generates a new SQL statement for the inner query to keep the correct sorting\n     *\n     * @param mixed[] $identifiers\n     */\n    private function recreateInnerSql(\n        OrderByClause $orderByClause,\n        array $identifiers,\n        string $innerSql,\n    ): string {\n        [$searchPatterns, $replacements] = $this->generateSqlAliasReplacements();\n        $orderByItems                    = [];\n\n        foreach ($orderByClause->orderByItems as $orderByItem) {\n            // Walk order by item to get string representation of it and\n            // replace path expressions in the order by clause with their column alias\n            $orderByItemString = preg_replace(\n                $searchPatterns,\n                $replacements,\n                $this->walkOrderByItem($orderByItem),\n            );\n\n            $orderByItems[] = $orderByItemString;\n            $identifier     = substr($orderByItemString, 0, strrpos($orderByItemString, ' '));\n\n            if (! in_array($identifier, $identifiers, true)) {\n                $identifiers[] = $identifier;\n            }\n        }\n\n        return $sql = sprintf(\n            'SELECT DISTINCT %s FROM (%s) dctrn_result_inner ORDER BY %s',\n            implode(', ', $identifiers),\n            $innerSql,\n            implode(', ', $orderByItems),\n        );\n    }\n\n    /**\n     * @return string[][]\n     * @phpstan-return array{0: list<non-empty-string>, 1: list<string>}\n     */\n    private function generateSqlAliasReplacements(): array\n    {\n        $aliasMap = $searchPatterns = $replacements = $metadataList = [];\n\n        // Generate DQL alias -> SQL table alias mapping\n        foreach (array_keys($this->rsm->aliasMap) as $dqlAlias) {\n            $metadataList[$dqlAlias] = $class = $this->getMetadataForDqlAlias($dqlAlias);\n            $aliasMap[$dqlAlias]     = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);\n        }\n\n        // Generate search patterns for each field's path expression in the order by clause\n        foreach ($this->rsm->fieldMappings as $fieldAlias => $fieldName) {\n            $dqlAliasForFieldAlias = $this->rsm->columnOwnerMap[$fieldAlias];\n            $class                 = $metadataList[$dqlAliasForFieldAlias];\n\n            // If the field is from a joined child table, we won't be ordering on it.\n            if (! isset($class->fieldMappings[$fieldName])) {\n                continue;\n            }\n\n            $fieldMapping = $class->fieldMappings[$fieldName];\n\n            // Get the proper column name as will appear in the select list\n            $columnName = $this->quoteStrategy->getColumnName(\n                $fieldName,\n                $metadataList[$dqlAliasForFieldAlias],\n                $this->em->getConnection()->getDatabasePlatform(),\n            );\n\n            // Get the SQL table alias for the entity and field\n            $sqlTableAliasForFieldAlias = $aliasMap[$dqlAliasForFieldAlias];\n\n            if (isset($fieldMapping->declared) && $fieldMapping->declared !== $class->name) {\n                // Field was declared in a parent class, so we need to get the proper SQL table alias\n                // for the joined parent table.\n                $otherClassMetadata = $this->em->getClassMetadata($fieldMapping->declared);\n\n                if (! $otherClassMetadata->isMappedSuperclass) {\n                    $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias);\n                }\n            }\n\n            // Compose search and replace patterns\n            $searchPatterns[] = sprintf(self::ORDER_BY_PATH_EXPRESSION, $sqlTableAliasForFieldAlias, $columnName);\n            $replacements[]   = $fieldAlias;\n        }\n\n        return [$searchPatterns, $replacements];\n    }\n\n    /**\n     * getter for $orderByPathExpressions\n     *\n     * @return list<PathExpression>\n     */\n    public function getOrderByPathExpressions(): array\n    {\n        return $this->orderByPathExpressions;\n    }\n\n    /**\n     * @throws OptimisticLockException\n     * @throws QueryException\n     */\n    private function getInnerSQL(SelectStatement $AST): string\n    {\n        // Set every select expression as visible(hidden = false) to\n        // make $AST have scalar mappings properly - this is relevant for referencing selected\n        // fields from outside the subquery, for example in the ORDER BY segment\n        $hiddens = [];\n\n        foreach ($AST->selectClause->selectExpressions as $idx => $expr) {\n            $hiddens[$idx]                   = $expr->hiddenAliasResultVariable;\n            $expr->hiddenAliasResultVariable = false;\n        }\n\n        $innerSql = parent::walkSelectStatement($AST);\n\n        // Restore hiddens\n        foreach ($AST->selectClause->selectExpressions as $idx => $expr) {\n            $expr->hiddenAliasResultVariable = $hiddens[$idx];\n        }\n\n        return $innerSql;\n    }\n\n    /** @return string[] */\n    private function getSQLIdentifier(SelectStatement $AST): array\n    {\n        // Find out the SQL alias of the identifier column of the root entity.\n        // It may be possible to make this work with multiple root entities but that\n        // would probably require issuing multiple queries or doing a UNION SELECT.\n        // So for now, it's not supported.\n\n        // Get the root entity and alias from the AST fromClause.\n        $from = $AST->fromClause->identificationVariableDeclarations;\n        if (count($from) !== 1) {\n            throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');\n        }\n\n        $fromRoot       = reset($from);\n        $rootAlias      = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass      = $this->getMetadataForDqlAlias($rootAlias);\n        $rootIdentifier = $rootClass->identifier;\n\n        // For every identifier, find out the SQL alias by combing through the ResultSetMapping\n        $sqlIdentifier = [];\n        foreach ($rootIdentifier as $property) {\n            if (isset($rootClass->fieldMappings[$property])) {\n                foreach (array_keys($this->rsm->fieldMappings, $property, true) as $alias) {\n                    if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {\n                        $sqlIdentifier[$property] = $alias;\n                    }\n                }\n            }\n\n            if (isset($rootClass->associationMappings[$property])) {\n                $association = $rootClass->associationMappings[$property];\n                assert($association->isToOneOwningSide());\n                $joinColumn = $association->joinColumns[0]->name;\n\n                foreach (array_keys($this->rsm->metaMappings, $joinColumn, true) as $alias) {\n                    if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {\n                        $sqlIdentifier[$property] = $alias;\n                    }\n                }\n            }\n        }\n\n        if (count($sqlIdentifier) === 0) {\n            throw new RuntimeException('The Paginator does not support Queries which only yield ScalarResults.');\n        }\n\n        if (count($rootIdentifier) !== count($sqlIdentifier)) {\n            throw new RuntimeException(sprintf(\n                'Not all identifier properties can be found in the ResultSetMapping: %s',\n                implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))),\n            ));\n        }\n\n        return $sqlIdentifier;\n    }\n\n    public function walkPathExpression(PathExpression $pathExpr): string\n    {\n        if (! $this->inSubSelect && ! $this->platformSupportsRowNumber() && ! in_array($pathExpr, $this->orderByPathExpressions, true)) {\n            $this->orderByPathExpressions[] = $pathExpr;\n        }\n\n        return parent::walkPathExpression($pathExpr);\n    }\n\n    public function walkSubSelect(Subselect $subselect): string\n    {\n        $this->inSubSelect = true;\n\n        $sql = parent::walkSubselect($subselect);\n\n        $this->inSubSelect = false;\n\n        return $sql;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/LimitSubqueryWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\IdentityFunction;\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse RuntimeException;\n\nuse function count;\nuse function is_string;\nuse function reset;\n\n/**\n * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent.\n */\nclass LimitSubqueryWalker extends TreeWalkerAdapter\n{\n    public const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';\n\n    public const FORCE_DBAL_TYPE_CONVERSION = 'doctrine_paginator.scalar_result.force_dbal_type_conversion';\n\n    /**\n     * Counter for generating unique order column aliases.\n     */\n    private int $aliasCounter = 0;\n\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        // Get the root entity and alias from the AST fromClause\n        $from      = $selectStatement->fromClause->identificationVariableDeclarations;\n        $fromRoot  = reset($from);\n        $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass = $this->getMetadataForDqlAlias($rootAlias);\n\n        $this->validate($selectStatement);\n        $identifier = $rootClass->getSingleIdentifierFieldName();\n\n        if (isset($rootClass->associationMappings[$identifier])) {\n            throw new RuntimeException('Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.');\n        }\n\n        $query = $this->_getQuery();\n\n        $query->setHint(\n            self::IDENTIFIER_TYPE,\n            Type::getType($rootClass->fieldMappings[$identifier]->type),\n        );\n\n        $query->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true);\n\n        $pathExpression = new PathExpression(\n            PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,\n            $rootAlias,\n            $identifier,\n        );\n\n        $pathExpression->type = PathExpression::TYPE_STATE_FIELD;\n\n        $selectStatement->selectClause->selectExpressions = [new SelectExpression($pathExpression, '_dctrn_id')];\n        $selectStatement->selectClause->isDistinct        = ($query->getHints()[Paginator::HINT_ENABLE_DISTINCT] ?? true) === true;\n\n        if (! isset($selectStatement->orderByClause)) {\n            return;\n        }\n\n        $queryComponents = $this->getQueryComponents();\n        foreach ($selectStatement->orderByClause->orderByItems as $item) {\n            if ($item->expression instanceof PathExpression) {\n                $selectStatement->selectClause->selectExpressions[] = new SelectExpression(\n                    $this->createSelectExpressionItem($item->expression),\n                    '_dctrn_ord' . $this->aliasCounter++,\n                );\n\n                continue;\n            }\n\n            if (is_string($item->expression) && isset($queryComponents[$item->expression])) {\n                $qComp = $queryComponents[$item->expression];\n\n                if (isset($qComp['resultVariable'])) {\n                    $selectStatement->selectClause->selectExpressions[] = new SelectExpression(\n                        $qComp['resultVariable'],\n                        $item->expression,\n                    );\n                }\n            }\n        }\n    }\n\n    /**\n     * Validate the AST to ensure that this walker is able to properly manipulate it.\n     */\n    private function validate(SelectStatement $AST): void\n    {\n        // Prevent LimitSubqueryWalker from being used with queries that include\n        // a limit, a fetched to-many join, and an order by condition that\n        // references a column from the fetch joined table.\n        $queryComponents = $this->getQueryComponents();\n        $query           = $this->_getQuery();\n        $from            = $AST->fromClause->identificationVariableDeclarations;\n        $fromRoot        = reset($from);\n\n        if (\n            $query instanceof Query\n            && $query->getMaxResults() !== null\n            && $AST->orderByClause\n            && count($fromRoot->joins)\n        ) {\n            // Check each orderby item.\n            // TODO: check complex orderby items too...\n            foreach ($AST->orderByClause->orderByItems as $orderByItem) {\n                $expression = $orderByItem->expression;\n                if (\n                    $orderByItem->expression instanceof PathExpression\n                    && isset($queryComponents[$expression->identificationVariable])\n                ) {\n                    $queryComponent = $queryComponents[$expression->identificationVariable];\n                    if (\n                        isset($queryComponent['parent'])\n                        && isset($queryComponent['relation'])\n                        && $queryComponent['relation']->isToMany()\n                    ) {\n                        throw new RuntimeException('Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers.');\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Retrieve either an IdentityFunction (IDENTITY(u.assoc)) or a state field (u.name).\n     *\n     * @return IdentityFunction|PathExpression\n     */\n    private function createSelectExpressionItem(PathExpression $pathExpression): Node\n    {\n        if ($pathExpression->type === PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {\n            $identity = new IdentityFunction('identity');\n\n            $identity->pathExpression = clone $pathExpression;\n\n            return $identity;\n        }\n\n        return clone $pathExpression;\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/Paginator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse ArrayIterator;\nuse Countable;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\NoResultException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\QueryBuilder;\nuse IteratorAggregate;\nuse Traversable;\n\nuse function array_key_exists;\nuse function array_map;\nuse function array_sum;\nuse function assert;\nuse function is_string;\n\n/**\n * The paginator can handle various complex scenarios with DQL.\n *\n * @template-covariant T\n * @implements IteratorAggregate<array-key, T>\n */\nclass Paginator implements Countable, IteratorAggregate\n{\n    use SQLResultCasing;\n\n    public const HINT_ENABLE_DISTINCT = 'paginator.distinct.enable';\n\n    private readonly Query $query;\n    private bool|null $useOutputWalkers = null;\n    private int|null $count             = null;\n\n    /** @param bool $fetchJoinCollection Whether the query joins a collection (true by default). */\n    public function __construct(\n        Query|QueryBuilder $query,\n        private readonly bool $fetchJoinCollection = true,\n    ) {\n        if ($query instanceof QueryBuilder) {\n            $query = $query->getQuery();\n        }\n\n        $this->query = $query;\n    }\n\n    /**\n     * Returns the query.\n     */\n    public function getQuery(): Query\n    {\n        return $this->query;\n    }\n\n    /**\n     * Returns whether the query joins a collection.\n     *\n     * @return bool Whether the query joins a collection.\n     */\n    public function getFetchJoinCollection(): bool\n    {\n        return $this->fetchJoinCollection;\n    }\n\n    /**\n     * Returns whether the paginator will use an output walker.\n     */\n    public function getUseOutputWalkers(): bool|null\n    {\n        return $this->useOutputWalkers;\n    }\n\n    /**\n     * Sets whether the paginator will use an output walker.\n     *\n     * @return $this\n     */\n    public function setUseOutputWalkers(bool|null $useOutputWalkers): static\n    {\n        $this->useOutputWalkers = $useOutputWalkers;\n\n        return $this;\n    }\n\n    public function count(): int\n    {\n        if ($this->count === null) {\n            try {\n                $this->count = (int) array_sum(array_map('current', $this->getCountQuery()->getScalarResult()));\n            } catch (NoResultException) {\n                $this->count = 0;\n            }\n        }\n\n        return $this->count;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @phpstan-return Traversable<array-key, T>\n     */\n    public function getIterator(): Traversable\n    {\n        $offset = $this->query->getFirstResult();\n        $length = $this->query->getMaxResults();\n\n        if ($this->fetchJoinCollection && $length !== null) {\n            $subQuery = $this->cloneQuery($this->query);\n\n            if ($this->useOutputWalker($subQuery)) {\n                $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);\n            } else {\n                $this->appendTreeWalker($subQuery, LimitSubqueryWalker::class);\n                $this->unbindUnusedQueryParams($subQuery);\n            }\n\n            $subQuery->setFirstResult($offset)->setMaxResults($length);\n\n            $foundIdRows = $subQuery->getScalarResult();\n\n            // don't do this for an empty id array\n            if ($foundIdRows === []) {\n                return new ArrayIterator([]);\n            }\n\n            $whereInQuery = $this->cloneQuery($this->query);\n            $ids          = array_map('current', $foundIdRows);\n\n            $this->appendTreeWalker($whereInQuery, WhereInWalker::class);\n            $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_HAS_IDS, true);\n            $whereInQuery->setFirstResult(0)->setMaxResults(null);\n            $whereInQuery->setCacheable($this->query->isCacheable());\n\n            $databaseIds = $this->convertWhereInIdentifiersToDatabaseValues($ids);\n            $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $databaseIds);\n\n            $result = $whereInQuery->getResult($this->query->getHydrationMode());\n        } else {\n            $result = $this->cloneQuery($this->query)\n                ->setMaxResults($length)\n                ->setFirstResult($offset)\n                ->setCacheable($this->query->isCacheable())\n                ->getResult($this->query->getHydrationMode());\n        }\n\n        return new ArrayIterator($result);\n    }\n\n    private function cloneQuery(Query $query): Query\n    {\n        $cloneQuery = clone $query;\n\n        $cloneQuery->setParameters(clone $query->getParameters());\n        $cloneQuery->setCacheable(false);\n\n        foreach ($query->getHints() as $name => $value) {\n            $cloneQuery->setHint($name, $value);\n        }\n\n        return $cloneQuery;\n    }\n\n    /**\n     * Determines whether to use an output walker for the query.\n     */\n    private function useOutputWalker(Query $query): bool\n    {\n        if ($this->useOutputWalkers === null) {\n            return (bool) $query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER) === false;\n        }\n\n        return $this->useOutputWalkers;\n    }\n\n    /**\n     * Appends a custom tree walker to the tree walkers hint.\n     *\n     * @param class-string $walkerClass\n     */\n    private function appendTreeWalker(Query $query, string $walkerClass): void\n    {\n        $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);\n\n        if ($hints === false) {\n            $hints = [];\n        }\n\n        $hints[] = $walkerClass;\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints);\n    }\n\n    /**\n     * Returns Query prepared to count.\n     */\n    private function getCountQuery(): Query\n    {\n        /*\n            As opposed to using self::cloneQuery, the following code does not transfer\n            a potentially existing result set mapping (either set directly by the user,\n            or taken from the parser result from a previous invocation of Query::parse())\n            to the new query object. This is fine, since we are going to completely change the\n            select clause, so a previously existing result set mapping (RSM) is probably wrong anyway.\n            In the case of using output walkers, we are even creating a new RSM down below.\n            In the case of using a tree walker, we want to have a new RSM created by the parser.\n        */\n        $countQuery = new Query($this->query->getEntityManager());\n        $countQuery->setDQL($this->query->getDQL());\n        $countQuery->setParameters(clone $this->query->getParameters());\n        $countQuery->setCacheable(false);\n        foreach ($this->query->getHints() as $name => $value) {\n            $countQuery->setHint($name, $value);\n        }\n\n        if (! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) {\n            $countQuery->setHint(CountWalker::HINT_DISTINCT, true);\n        }\n\n        if ($this->useOutputWalker($countQuery)) {\n            $platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win\n\n            $rsm = new ResultSetMapping();\n            $rsm->addScalarResult($this->getSQLResultCasing($platform, 'dctrn_count'), 'count');\n\n            $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n            $countQuery->setResultSetMapping($rsm);\n        } else {\n            $this->appendTreeWalker($countQuery, CountWalker::class);\n            $this->unbindUnusedQueryParams($countQuery);\n        }\n\n        $countQuery->setFirstResult(0)->setMaxResults(null);\n\n        return $countQuery;\n    }\n\n    private function unbindUnusedQueryParams(Query $query): void\n    {\n        $parser            = new Parser($query);\n        $parameterMappings = $parser->parse()->getParameterMappings();\n        /** @var Collection|Parameter[] $parameters */\n        $parameters = $query->getParameters();\n\n        foreach ($parameters as $key => $parameter) {\n            $parameterName = $parameter->getName();\n\n            if (! (isset($parameterMappings[$parameterName]) || array_key_exists($parameterName, $parameterMappings))) {\n                unset($parameters[$key]);\n            }\n        }\n\n        $query->setParameters($parameters);\n    }\n\n    /**\n     * @param mixed[] $identifiers\n     *\n     * @return mixed[]\n     */\n    private function convertWhereInIdentifiersToDatabaseValues(array $identifiers): array\n    {\n        $query = $this->cloneQuery($this->query);\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, RootTypeWalker::class);\n\n        $connection = $this->query->getEntityManager()->getConnection();\n        $type       = $query->getSQL();\n        assert(is_string($type));\n\n        return array_map(static fn ($id): mixed => $connection->convertToDatabaseValue($id, $type), $identifiers);\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/RootTypeWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\Exec\\FinalizedSelectExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\PreparedExecutorFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\nuse Doctrine\\ORM\\Utility\\PersisterHelper;\nuse RuntimeException;\n\nuse function count;\nuse function reset;\n\n/**\n * Infers the DBAL type of the #Id (identifier) column of the given query's root entity, and\n * returns it in place of a real SQL statement.\n *\n * Obtaining this type is a necessary intermediate step for \\Doctrine\\ORM\\Tools\\Pagination\\Paginator.\n * We can best do this from a tree walker because it gives us access to the AST.\n *\n * Returning the type instead of a \"real\" SQL statement is a slight hack. However, it has the\n * benefit that the DQL -> root entity id type resolution can be cached in the query cache.\n */\nfinal class RootTypeWalker extends SqlOutputWalker\n{\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): string\n    {\n        // Get the root entity and alias from the AST fromClause\n        $from = $selectStatement->fromClause->identificationVariableDeclarations;\n\n        if (count($from) > 1) {\n            throw new RuntimeException('Can only process queries that select only one FROM component');\n        }\n\n        $fromRoot            = reset($from);\n        $rootAlias           = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass           = $this->getMetadataForDqlAlias($rootAlias);\n        $identifierFieldName = $rootClass->getSingleIdentifierFieldName();\n\n        return PersisterHelper::getTypeOfField(\n            $identifierFieldName,\n            $rootClass,\n            $this->getQuery()\n                ->getEntityManager(),\n        )[0];\n    }\n\n    public function getFinalizer(AST\\DeleteStatement|AST\\UpdateStatement|AST\\SelectStatement $AST): SqlFinalizer\n    {\n        if (! $AST instanceof AST\\SelectStatement) {\n            throw new RuntimeException(self::class . ' is to be used on SelectStatements only');\n        }\n\n        return new PreparedExecutorFinalizer(new FinalizedSelectExecutor($this->walkSelectStatement($AST)));\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/RowNumberOverFunction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\AST\\OrderByClause;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Tools\\Pagination\\Exception\\RowNumberOverFunctionNotEnabled;\n\nuse function trim;\n\n/**\n * RowNumberOverFunction\n *\n * Provides ROW_NUMBER() OVER(ORDER BY...) construct for use in LimitSubqueryOutputWalker\n */\nclass RowNumberOverFunction extends FunctionNode\n{\n    public OrderByClause $orderByClause;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return 'ROW_NUMBER() OVER(' . trim($sqlWalker->walkOrderByClause(\n            $this->orderByClause,\n        )) . ')';\n    }\n\n    /**\n     * @throws RowNumberOverFunctionNotEnabled\n     *\n     * @inheritdoc\n     */\n    public function parse(Parser $parser): void\n    {\n        throw RowNumberOverFunctionNotEnabled::create();\n    }\n}\n"
  },
  {
    "path": "src/Tools/Pagination/WhereInWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query\\AST\\ArithmeticExpression;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalExpression;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalPrimary;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalTerm;\nuse Doctrine\\ORM\\Query\\AST\\InListExpression;\nuse Doctrine\\ORM\\Query\\AST\\InputParameter;\nuse Doctrine\\ORM\\Query\\AST\\NullComparisonExpression;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression;\nuse Doctrine\\ORM\\Query\\AST\\WhereClause;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse RuntimeException;\n\nuse function count;\nuse function reset;\n\n/**\n * Appends a condition equivalent to \"WHERE IN (:dpid_1, :dpid_2, ...)\" to the whereClause of the AST.\n *\n * The parameter namespace (dpid) is defined by\n * the PAGINATOR_ID_ALIAS\n *\n * The HINT_PAGINATOR_HAS_IDS query hint indicates whether there are\n * any ids in the parameter at all.\n */\nclass WhereInWalker extends TreeWalkerAdapter\n{\n    /**\n     * ID Count hint name.\n     */\n    public const HINT_PAGINATOR_HAS_IDS = 'doctrine.paginator_has_ids';\n\n    /**\n     * Primary key alias for query.\n     */\n    public const PAGINATOR_ID_ALIAS = 'dpid';\n\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        // Get the root entity and alias from the AST fromClause\n        $from = $selectStatement->fromClause->identificationVariableDeclarations;\n\n        if (count($from) > 1) {\n            throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');\n        }\n\n        $fromRoot            = reset($from);\n        $rootAlias           = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass           = $this->getMetadataForDqlAlias($rootAlias);\n        $identifierFieldName = $rootClass->getSingleIdentifierFieldName();\n\n        $pathType = PathExpression::TYPE_STATE_FIELD;\n        if (isset($rootClass->associationMappings[$identifierFieldName])) {\n            $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;\n        }\n\n        $pathExpression       = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName);\n        $pathExpression->type = $pathType;\n\n        $hasIds = $this->_getQuery()->getHint(self::HINT_PAGINATOR_HAS_IDS);\n\n        if ($hasIds) {\n            $arithmeticExpression                             = new ArithmeticExpression();\n            $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(\n                [$pathExpression],\n            );\n            $expression                                       = new InListExpression(\n                $arithmeticExpression,\n                [new InputParameter(':' . self::PAGINATOR_ID_ALIAS)],\n            );\n        } else {\n            $expression = new NullComparisonExpression($pathExpression);\n        }\n\n        $conditionalPrimary                              = new ConditionalPrimary();\n        $conditionalPrimary->simpleConditionalExpression = $expression;\n        if ($selectStatement->whereClause) {\n            if ($selectStatement->whereClause->conditionalExpression instanceof ConditionalTerm) {\n                $selectStatement->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;\n            } elseif ($selectStatement->whereClause->conditionalExpression instanceof ConditionalPrimary) {\n                $selectStatement->whereClause->conditionalExpression = new ConditionalExpression(\n                    [\n                        new ConditionalTerm(\n                            [\n                                $selectStatement->whereClause->conditionalExpression,\n                                $conditionalPrimary,\n                            ],\n                        ),\n                    ],\n                );\n            } else {\n                $tmpPrimary                                          = new ConditionalPrimary();\n                $tmpPrimary->conditionalExpression                   = $selectStatement->whereClause->conditionalExpression;\n                $selectStatement->whereClause->conditionalExpression = new ConditionalTerm(\n                    [\n                        $tmpPrimary,\n                        $conditionalPrimary,\n                    ],\n                );\n            }\n        } else {\n            $selectStatement->whereClause = new WhereClause(\n                new ConditionalExpression(\n                    [new ConditionalTerm([$conditionalPrimary])],\n                ),\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tools/ResolveTargetEntityListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse Doctrine\\Common\\EventSubscriber;\nuse Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\nuse Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\nuse function array_key_exists;\nuse function array_replace_recursive;\nuse function ltrim;\n\n/**\n * ResolveTargetEntityListener\n *\n * Mechanism to overwrite interfaces or classes specified as association\n * targets.\n */\nclass ResolveTargetEntityListener implements EventSubscriber\n{\n    /** @var mixed[][] indexed by original entity name */\n    private array $resolveTargetEntities = [];\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSubscribedEvents(): array\n    {\n        return [\n            Events::loadClassMetadata,\n            Events::onClassMetadataNotFound,\n        ];\n    }\n\n    /**\n     * Adds a target-entity class name to resolve to a new class name.\n     *\n     * @phpstan-param array<string, mixed> $mapping\n     */\n    public function addResolveTargetEntity(string $originalEntity, string $newEntity, array $mapping): void\n    {\n        $mapping['targetEntity']                                   = ltrim($newEntity, '\\\\');\n        $this->resolveTargetEntities[ltrim($originalEntity, '\\\\')] = $mapping;\n    }\n\n    /** @internal this is an event callback, and should not be called directly */\n    public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args): void\n    {\n        if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) {\n            $args->setFoundMetadata(\n                $args\n                    ->getObjectManager()\n                    ->getClassMetadata($this->resolveTargetEntities[$args->getClassName()]['targetEntity']),\n            );\n        }\n    }\n\n    /**\n     * Processes event and resolves new target entity names.\n     *\n     * @internal this is an event callback, and should not be called directly\n     */\n    public function loadClassMetadata(LoadClassMetadataEventArgs $args): void\n    {\n        $cm = $args->getClassMetadata();\n\n        foreach ($cm->associationMappings as $mapping) {\n            if (isset($this->resolveTargetEntities[$mapping->targetEntity])) {\n                $this->remapAssociation($cm, $mapping);\n            }\n        }\n\n        foreach ($cm->discriminatorMap as $value => $class) {\n            if (isset($this->resolveTargetEntities[$class])) {\n                $cm->addDiscriminatorMapClass($value, $this->resolveTargetEntities[$class]['targetEntity']);\n            }\n        }\n    }\n\n    private function remapAssociation(ClassMetadata $classMetadata, AssociationMapping $mapping): void\n    {\n        $newMapping              = $this->resolveTargetEntities[$mapping->targetEntity];\n        $newMapping              = array_replace_recursive(\n            $mapping->toArray(),\n            $newMapping,\n        );\n        $newMapping['fieldName'] = $mapping->fieldName;\n\n        unset($classMetadata->associationMappings[$mapping->fieldName]);\n\n        switch ($mapping->type()) {\n            case ClassMetadata::MANY_TO_MANY:\n                $classMetadata->mapManyToMany($newMapping);\n                break;\n            case ClassMetadata::MANY_TO_ONE:\n                $classMetadata->mapManyToOne($newMapping);\n                break;\n            case ClassMetadata::ONE_TO_MANY:\n                $classMetadata->mapOneToMany($newMapping);\n                break;\n            case ClassMetadata::ONE_TO_ONE:\n                $classMetadata->mapOneToOne($newMapping);\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tools/SchemaTool.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse BackedEnum;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Schema\\AbstractAsset;\nuse Doctrine\\DBAL\\Schema\\AbstractSchemaManager;\nuse Doctrine\\DBAL\\Schema\\ComparatorConfig;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp;\nuse Doctrine\\DBAL\\Schema\\ForeignKeyConstraintEditor;\nuse Doctrine\\DBAL\\Schema\\Index;\nuse Doctrine\\DBAL\\Schema\\Index\\IndexedColumn;\nuse Doctrine\\DBAL\\Schema\\Name\\Identifier;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\NamedObject;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\Deprecation;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping;\nuse Doctrine\\ORM\\Mapping\\FieldMapping;\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\Tools\\Event\\GenerateSchemaEventArgs;\nuse Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs;\nuse Doctrine\\ORM\\Tools\\Exception\\MissingColumnException;\nuse Doctrine\\ORM\\Tools\\Exception\\NotSupported;\nuse Throwable;\n\nuse function array_diff;\nuse function array_diff_key;\nuse function array_filter;\nuse function array_flip;\nuse function array_intersect_key;\nuse function array_map;\nuse function array_values;\nuse function assert;\nuse function class_exists;\nuse function count;\nuse function current;\nuse function implode;\nuse function in_array;\nuse function interface_exists;\nuse function is_numeric;\nuse function method_exists;\nuse function preg_match;\nuse function strtolower;\n\n/**\n * The SchemaTool is a tool to create/drop/update database schemas based on\n * <tt>ClassMetadata</tt> class descriptors.\n *\n * @link    www.doctrine-project.org\n */\nclass SchemaTool\n{\n    private const KNOWN_COLUMN_OPTIONS = ['comment', 'unsigned', 'fixed', 'default', 'values'];\n\n    private readonly AbstractPlatform $platform;\n    private readonly QuoteStrategy $quoteStrategy;\n    private readonly AbstractSchemaManager $schemaManager;\n\n    /**\n     * Initializes a new SchemaTool instance that uses the connection of the\n     * provided EntityManager.\n     */\n    public function __construct(private readonly EntityManagerInterface $em)\n    {\n        $this->platform      = $em->getConnection()->getDatabasePlatform();\n        $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();\n        $this->schemaManager = $em->getConnection()->createSchemaManager();\n    }\n\n    /**\n     * Creates the database schema for the given array of ClassMetadata instances.\n     *\n     * @phpstan-param list<ClassMetadata> $classes\n     *\n     * @throws ToolsException\n     */\n    public function createSchema(array $classes): void\n    {\n        $createSchemaSql = $this->getCreateSchemaSql($classes);\n        $conn            = $this->em->getConnection();\n\n        foreach ($createSchemaSql as $sql) {\n            try {\n                $conn->executeStatement($sql);\n            } catch (Throwable $e) {\n                throw ToolsException::schemaToolFailure($sql, $e);\n            }\n        }\n    }\n\n    /**\n     * Gets the list of DDL statements that are required to create the database schema for\n     * the given list of ClassMetadata instances.\n     *\n     * @phpstan-param list<ClassMetadata> $classes\n     *\n     * @return list<string> The SQL statements needed to create the schema for the classes.\n     */\n    public function getCreateSchemaSql(array $classes): array\n    {\n        $schema = $this->getSchemaFromMetadata($classes);\n\n        return $schema->toSql($this->platform);\n    }\n\n    /**\n     * Detects instances of ClassMetadata that don't need to be processed in the SchemaTool context.\n     *\n     * @phpstan-param array<string, bool> $processedClasses\n     */\n    private function processingNotRequired(\n        ClassMetadata $class,\n        array $processedClasses,\n    ): bool {\n        return isset($processedClasses[$class->name]) ||\n            $class->isMappedSuperclass ||\n            $class->isEmbeddedClass ||\n            ($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName) ||\n            in_array($class->name, $this->em->getConfiguration()->getSchemaIgnoreClasses());\n    }\n\n    /**\n     * Resolves fields in index mapping to column names\n     *\n     * @param mixed[] $indexData index or unique constraint data\n     *\n     * @return list<string> Column names from combined fields and columns mappings\n     */\n    private function getIndexColumns(ClassMetadata $class, array $indexData): array\n    {\n        $columns = [];\n\n        if (\n            isset($indexData['columns'], $indexData['fields'])\n            || (\n                ! isset($indexData['columns'])\n                && ! isset($indexData['fields'])\n            )\n        ) {\n            throw MappingException::invalidIndexConfiguration(\n                (string) $class,\n                $indexData['name'] ?? 'unnamed',\n            );\n        }\n\n        if (isset($indexData['columns'])) {\n            $columns = $indexData['columns'];\n        }\n\n        if (isset($indexData['fields'])) {\n            foreach ($indexData['fields'] as $fieldName) {\n                if ($class->hasField($fieldName)) {\n                    $columns[] = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);\n                } elseif ($class->hasAssociation($fieldName)) {\n                    $assoc = $class->getAssociationMapping($fieldName);\n                    assert($assoc->isToOneOwningSide());\n                    foreach ($assoc->joinColumns as $joinColumn) {\n                        $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n                    }\n                }\n            }\n        }\n\n        return $columns;\n    }\n\n    /**\n     * Creates a Schema instance from a given set of metadata classes.\n     *\n     * @phpstan-param list<ClassMetadata> $classes\n     *\n     * @throws NotSupported\n     */\n    public function getSchemaFromMetadata(array $classes): Schema\n    {\n        // Reminder for processed classes, used for hierarchies\n        $processedClasses     = [];\n        $eventManager         = $this->em->getEventManager();\n        $metadataSchemaConfig = $this->schemaManager->createSchemaConfig();\n\n        $schema = new Schema([], [], $metadataSchemaConfig);\n\n        $addedFks       = [];\n        $blacklistedFks = [];\n\n        foreach ($classes as $class) {\n            if ($this->processingNotRequired($class, $processedClasses)) {\n                continue;\n            }\n\n            $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform));\n\n            if ($class->isInheritanceTypeSingleTable()) {\n                $this->gatherColumns($class, $table);\n                $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);\n\n                // Add the discriminator column\n                $this->addDiscriminatorColumnDefinition($class, $table);\n\n                // Aggregate all the information from all classes in the hierarchy\n                foreach ($class->parentClasses as $parentClassName) {\n                    // Parent class information is already contained in this class\n                    $processedClasses[$parentClassName] = true;\n                }\n\n                foreach ($class->subClasses as $subClassName) {\n                    $subClass = $this->em->getClassMetadata($subClassName);\n                    $this->gatherColumns($subClass, $table);\n                    $this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks);\n                    $processedClasses[$subClassName] = true;\n                }\n            } elseif ($class->isInheritanceTypeJoined()) {\n                // Add all non-inherited fields as columns\n                foreach ($class->fieldMappings as $fieldName => $mapping) {\n                    if (! isset($mapping->inherited)) {\n                        $this->gatherColumn($class, $mapping, $table);\n                    }\n                }\n\n                $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);\n\n                // Add the discriminator column only to the root table\n                if ($class->name === $class->rootEntityName) {\n                    $this->addDiscriminatorColumnDefinition($class, $table);\n                } else {\n                    // Add an ID FK column to child tables\n                    $pkColumns           = [];\n                    $inheritedKeyColumns = [];\n\n                    foreach ($class->identifier as $identifierField) {\n                        if (isset($class->fieldMappings[$identifierField]->inherited)) {\n                            $idMapping = $class->fieldMappings[$identifierField];\n                            $this->gatherColumn($class, $idMapping, $table);\n                            $columnName = $this->quoteStrategy->getColumnName(\n                                $identifierField,\n                                $class,\n                                $this->platform,\n                            );\n                            // TODO: This seems rather hackish, can we optimize it?\n                            $table->getColumn($columnName)->setAutoincrement(false);\n\n                            $pkColumns[]           = $columnName;\n                            $inheritedKeyColumns[] = $columnName;\n\n                            continue;\n                        }\n\n                        if (isset($class->associationMappings[$identifierField]->inherited)) {\n                            $idMapping = $class->associationMappings[$identifierField];\n                            assert($idMapping->isToOneOwningSide());\n\n                            $targetEntity = current(\n                                array_filter(\n                                    $classes,\n                                    static fn (ClassMetadata $class): bool => $class->name === $idMapping->targetEntity,\n                                ),\n                            );\n\n                            foreach ($idMapping->joinColumns as $joinColumn) {\n                                if (isset($targetEntity->fieldMappings[$joinColumn->referencedColumnName])) {\n                                    $columnName = $this->quoteStrategy->getJoinColumnName(\n                                        $joinColumn,\n                                        $class,\n                                        $this->platform,\n                                    );\n\n                                    $pkColumns[]           = $columnName;\n                                    $inheritedKeyColumns[] = $columnName;\n                                }\n                            }\n                        }\n                    }\n\n                    if ($inheritedKeyColumns !== []) {\n                        // Add a FK constraint on the ID column\n                        $table->addForeignKeyConstraint(\n                            $this->quoteStrategy->getTableName(\n                                $this->em->getClassMetadata($class->rootEntityName),\n                                $this->platform,\n                            ),\n                            $inheritedKeyColumns,\n                            $inheritedKeyColumns,\n                            ['onDelete' => 'CASCADE'],\n                        );\n                    }\n\n                    if ($pkColumns !== []) {\n                        self::addPrimaryKeyConstraint($table, $pkColumns);\n                    }\n                }\n            } else {\n                $this->gatherColumns($class, $table);\n                $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);\n            }\n\n            $pkColumns = [];\n\n            foreach ($class->identifier as $identifierField) {\n                if (isset($class->fieldMappings[$identifierField])) {\n                    $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform);\n                } elseif (isset($class->associationMappings[$identifierField])) {\n                    $assoc = $class->associationMappings[$identifierField];\n                    assert($assoc->isToOneOwningSide());\n\n                    foreach ($assoc->joinColumns as $joinColumn) {\n                        $pkColumns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n                    }\n                }\n            }\n\n            if (! $table->hasIndex('primary')) {\n                self::addPrimaryKeyConstraint($table, $pkColumns);\n            }\n\n            // there can be unique indexes automatically created for join column\n            // if join column is also primary key we should keep only primary key on this column\n            // so, remove indexes overruled by primary key\n            $primaryKey = $table->getIndex('primary');\n\n            foreach ($table->getIndexes() as $idxKey => $existingIndex) {\n                if ($existingIndex !== $primaryKey && $primaryKey->spansColumns(self::getIndexedColumns($existingIndex))) {\n                    $table->dropIndex($idxKey);\n                }\n            }\n\n            if (isset($class->table['indexes'])) {\n                foreach ($class->table['indexes'] as $indexName => $indexData) {\n                    if (! isset($indexData['flags'])) {\n                        $indexData['flags'] = [];\n                    }\n\n                    $table->addIndex(\n                        $this->getIndexColumns($class, $indexData),\n                        is_numeric($indexName) ? null : $indexName,\n                        (array) $indexData['flags'],\n                        $indexData['options'] ?? [],\n                    );\n                }\n            }\n\n            if (isset($class->table['uniqueConstraints'])) {\n                foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) {\n                    $uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);\n\n                    foreach ($table->getIndexes() as $tableIndexName => $tableIndex) {\n                        if ($tableIndex->isFulfilledBy($uniqIndex)) {\n                            $table->dropIndex($tableIndexName);\n                            break;\n                        }\n                    }\n\n                    $table->addUniqueIndex(self::getIndexedColumns($uniqIndex), is_numeric($indexName) ? null : $indexName, $indexData['options'] ?? []);\n                }\n            }\n\n            if (isset($class->table['options'])) {\n                foreach ($class->table['options'] as $key => $val) {\n                    $table->addOption($key, $val);\n                }\n            }\n\n            $processedClasses[$class->name] = true;\n\n            if ($class->isIdGeneratorSequence() && $class->name === $class->rootEntityName) {\n                $seqDef     = $class->sequenceGeneratorDefinition;\n                $quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform);\n                if (! $schema->hasSequence($quotedName)) {\n                    $schema->createSequence(\n                        $quotedName,\n                        (int) $seqDef['allocationSize'],\n                        (int) $seqDef['initialValue'],\n                    );\n                }\n            }\n\n            if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) {\n                $eventManager->dispatchEvent(\n                    ToolEvents::postGenerateSchemaTable,\n                    new GenerateSchemaTableEventArgs($class, $schema, $table),\n                );\n            }\n        }\n\n        if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) {\n            $eventManager->dispatchEvent(\n                ToolEvents::postGenerateSchema,\n                new GenerateSchemaEventArgs($this->em, $schema),\n            );\n        }\n\n        return $schema;\n    }\n\n    /**\n     * Gets a portable column definition as required by the DBAL for the discriminator\n     * column of a class.\n     */\n    private function addDiscriminatorColumnDefinition(ClassMetadata $class, Table $table): void\n    {\n        $discrColumn = $class->discriminatorColumn;\n        assert($discrColumn !== null);\n\n        if (strtolower($discrColumn->type) === 'string' && ! isset($discrColumn->length)) {\n            $discrColumn->type   = 'string';\n            $discrColumn->length = 255;\n        }\n\n        $options = [\n            'length'    => $discrColumn->length ?? null,\n            'notnull'   => true,\n        ];\n\n        if (isset($discrColumn->columnDefinition)) {\n            $options['columnDefinition'] = $discrColumn->columnDefinition;\n        }\n\n        $options = $this->gatherColumnOptions($discrColumn) + $options;\n        $table->addColumn($discrColumn->name, $discrColumn->type, $options);\n    }\n\n    /**\n     * Gathers the column definitions as required by the DBAL of all field mappings\n     * found in the given class.\n     */\n    private function gatherColumns(ClassMetadata $class, Table $table): void\n    {\n        foreach ($class->fieldMappings as $mapping) {\n            if ($class->isInheritanceTypeSingleTable() && isset($mapping->inherited)) {\n                continue;\n            }\n\n            $this->gatherColumn($class, $mapping, $table);\n        }\n    }\n\n    /**\n     * Creates a column definition as required by the DBAL from an ORM field mapping definition.\n     *\n     * @param ClassMetadata $class The class that owns the field mapping.\n     * @phpstan-param FieldMapping $mapping The field mapping.\n     */\n    private function gatherColumn(\n        ClassMetadata $class,\n        FieldMapping $mapping,\n        Table $table,\n    ): void {\n        $columnName = $this->quoteStrategy->getColumnName($mapping->fieldName, $class, $this->platform);\n        $columnType = $mapping->type;\n\n        $options            = [];\n        $options['length']  = $mapping->length ?? null;\n        $options['notnull'] = isset($mapping->nullable) ? ! $mapping->nullable : true;\n        if ($class->isInheritanceTypeSingleTable() && $class->parentClasses) {\n            $options['notnull'] = false;\n        }\n\n        $options['platformOptions']            = [];\n        $options['platformOptions']['version'] = $class->isVersioned && $class->versionField === $mapping->fieldName;\n\n        if (strtolower($columnType) === 'string' && $options['length'] === null) {\n            $options['length'] = 255;\n        }\n\n        if (isset($mapping->precision)) {\n            $options['precision'] = $mapping->precision;\n        }\n\n        if (isset($mapping->scale)) {\n            $options['scale'] = $mapping->scale;\n        }\n\n        /** @phpstan-ignore property.deprecated */\n        if (isset($mapping->default)) {\n            /** @phpstan-ignore property.deprecated */\n            $options['default'] = $mapping->default;\n        }\n\n        if (isset($mapping->columnDefinition)) {\n            $options['columnDefinition'] = $mapping->columnDefinition;\n        }\n\n        // the 'default' option can be overwritten here\n        $options = $this->gatherColumnOptions($mapping) + $options;\n\n        if (isset($options['default']) && interface_exists(DefaultExpression::class)) {\n            if (\n                in_array($mapping->type, [\n                    Types::DATETIME_MUTABLE,\n                    Types::DATETIME_IMMUTABLE,\n                    Types::DATETIMETZ_MUTABLE,\n                    Types::DATETIMETZ_IMMUTABLE,\n                ], true)\n                && $options['default'] === $this->platform->getCurrentTimestampSQL()\n            ) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/issues/12252',\n                    <<<'DEPRECATION'\n                    Using \"%s\" as a default value for datetime fields is deprecated and\n                    will not be supported in Doctrine ORM 4.0.\n                    Pass a `Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp` instance instead.\n                    DEPRECATION,\n                    $this->platform->getCurrentTimestampSQL(),\n                );\n                $options['default'] = new CurrentTimestamp();\n            }\n\n            if (\n                in_array($mapping->type, [Types::TIME_MUTABLE, Types::TIME_IMMUTABLE], true)\n                && $options['default'] === $this->platform->getCurrentTimeSQL()\n            ) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/issues/12252',\n                    <<<'DEPRECATION'\n                    Using \"%s\" as a default value for time fields is deprecated and\n                    will not be supported in Doctrine ORM 4.0.\n                    Pass a `Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime` instance instead.\n                    DEPRECATION,\n                    $this->platform->getCurrentTimeSQL(),\n                );\n                $options['default'] = new CurrentTime();\n            }\n\n            if (\n                in_array($mapping->type, [Types::DATE_MUTABLE, Types::DATE_IMMUTABLE], true)\n                && $options['default'] === $this->platform->getCurrentDateSQL()\n            ) {\n                Deprecation::trigger(\n                    'doctrine/orm',\n                    'https://github.com/doctrine/orm/issues/12252',\n                    <<<'DEPRECATION'\n                    Using \"%s\" as a default value for date fields is deprecated and\n                    will not be supported in Doctrine ORM 4.0.\n                    Pass a `Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate` instance instead.\n                    DEPRECATION,\n                    $this->platform->getCurrentDateSQL(),\n                );\n                $options['default'] = new CurrentDate();\n            }\n        }\n\n        if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() === [$mapping->fieldName]) {\n            $options['autoincrement'] = true;\n        }\n\n        if ($class->isInheritanceTypeJoined() && $class->name !== $class->rootEntityName) {\n            $options['autoincrement'] = false;\n        }\n\n        if ($table->hasColumn($columnName)) {\n            // required in some inheritance scenarios\n            $table->modifyColumn($columnName, $options);\n        } else {\n            $table->addColumn($columnName, $columnType, $options);\n        }\n\n        $isUnique = $mapping->unique ?? false;\n        if ($isUnique) {\n            $table->addUniqueIndex([$columnName]);\n        }\n\n        $isIndex = $mapping->index ?? false;\n        if ($isIndex) {\n            $table->addIndex([$columnName]);\n        }\n    }\n\n    /**\n     * Gathers the SQL for properly setting up the relations of the given class.\n     * This includes the SQL for foreign key constraints and join tables.\n     *\n     * @phpstan-param array<string, array{\n     *                  foreignTableName: string,\n     *                  foreignColumns: list<string>\n     *              }>                               $addedFks\n     * @phpstan-param array<string, bool>              $blacklistedFks\n     *\n     * @throws NotSupported\n     */\n    private function gatherRelationsSql(\n        ClassMetadata $class,\n        Table $table,\n        Schema $schema,\n        array &$addedFks,\n        array &$blacklistedFks,\n    ): void {\n        foreach ($class->associationMappings as $id => $mapping) {\n            if (isset($mapping->inherited) && ! in_array($id, $class->identifier, true)) {\n                continue;\n            }\n\n            $foreignClass = $this->em->getClassMetadata($mapping->targetEntity);\n\n            if ($mapping->isToOneOwningSide()) {\n                $primaryKeyColumns = []; // PK is unnecessary for this relation-type\n\n                $this->gatherRelationJoinColumns(\n                    $mapping->joinColumns,\n                    $table,\n                    $foreignClass,\n                    $mapping,\n                    $primaryKeyColumns,\n                    $addedFks,\n                    $blacklistedFks,\n                );\n            } elseif ($mapping instanceof ManyToManyOwningSideMapping) {\n                // create join table\n                $joinTable = $mapping->joinTable;\n\n                $theJoinTable = $schema->createTable(\n                    $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform),\n                );\n\n                foreach ($joinTable->options as $key => $val) {\n                    $theJoinTable->addOption($key, $val);\n                }\n\n                $primaryKeyColumns = [];\n\n                // Build first FK constraint (relation table => source table)\n                $this->gatherRelationJoinColumns(\n                    $joinTable->joinColumns,\n                    $theJoinTable,\n                    $class,\n                    $mapping,\n                    $primaryKeyColumns,\n                    $addedFks,\n                    $blacklistedFks,\n                );\n\n                // Build second FK constraint (relation table => target table)\n                $this->gatherRelationJoinColumns(\n                    $joinTable->inverseJoinColumns,\n                    $theJoinTable,\n                    $foreignClass,\n                    $mapping,\n                    $primaryKeyColumns,\n                    $addedFks,\n                    $blacklistedFks,\n                );\n\n                self::addPrimaryKeyConstraint($theJoinTable, $primaryKeyColumns);\n            }\n        }\n    }\n\n    /**\n     * Gets the class metadata that is responsible for the definition of the referenced column name.\n     *\n     * Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its\n     * not a simple field, go through all identifier field names that are associations recursively and\n     * find that referenced column name.\n     *\n     * TODO: Is there any way to make this code more pleasing?\n     *\n     * @phpstan-return array{ClassMetadata, string}|null\n     */\n    private function getDefiningClass(ClassMetadata $class, string $referencedColumnName): array|null\n    {\n        $referencedFieldName = $class->getFieldName($referencedColumnName);\n\n        if ($class->hasField($referencedFieldName)) {\n            return [$class, $referencedFieldName];\n        }\n\n        if (in_array($referencedColumnName, $class->getIdentifierColumnNames(), true)) {\n            // it seems to be an entity as foreign key\n            foreach ($class->getIdentifierFieldNames() as $fieldName) {\n                if (\n                    $class->hasAssociation($fieldName)\n                    && $class->getSingleAssociationJoinColumnName($fieldName) === $referencedColumnName\n                ) {\n                    return $this->getDefiningClass(\n                        $this->em->getClassMetadata($class->associationMappings[$fieldName]->targetEntity),\n                        $class->getSingleAssociationReferencedJoinColumnName($fieldName),\n                    );\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Gathers columns and fk constraints that are required for one part of relationship.\n     *\n     * @phpstan-param list<JoinColumnMapping>          $joinColumns\n     * @phpstan-param list<string>                     $primaryKeyColumns\n     * @phpstan-param array<string, array{\n     *                  foreignTableName: string,\n     *                  foreignColumns: list<string>\n     *              }>                               $addedFks\n     * @phpstan-param array<string,bool>               $blacklistedFks\n     *\n     * @throws MissingColumnException\n     */\n    private function gatherRelationJoinColumns(\n        array $joinColumns,\n        Table $theJoinTable,\n        ClassMetadata $class,\n        AssociationMapping $mapping,\n        array &$primaryKeyColumns,\n        array &$addedFks,\n        array &$blacklistedFks,\n    ): void {\n        $localColumns      = [];\n        $foreignColumns    = [];\n        $fkOptions         = [];\n        $foreignTableName  = $this->quoteStrategy->getTableName($class, $this->platform);\n        $uniqueConstraints = [];\n\n        foreach ($joinColumns as $joinColumn) {\n            [$definingClass, $referencedFieldName] = $this->getDefiningClass(\n                $class,\n                $joinColumn->referencedColumnName,\n            );\n\n            if (! $definingClass) {\n                throw MissingColumnException::fromColumnSourceAndTarget(\n                    $joinColumn->referencedColumnName,\n                    $mapping->sourceEntity,\n                    $mapping->targetEntity,\n                );\n            }\n\n            $quotedColumnName    = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);\n            $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName(\n                $joinColumn,\n                $class,\n                $this->platform,\n            );\n\n            $primaryKeyColumns[] = $quotedColumnName;\n            $localColumns[]      = $quotedColumnName;\n            $foreignColumns[]    = $quotedRefColumnName;\n\n            if (! $theJoinTable->hasColumn($quotedColumnName)) {\n                // Only add the column to the table if it does not exist already.\n                // It might exist already if the foreign key is mapped into a regular\n                // property as well.\n\n                $fieldMapping = $definingClass->getFieldMapping($referencedFieldName);\n\n                $columnOptions = ['notnull' => false];\n\n                if (isset($joinColumn->columnDefinition)) {\n                    $columnOptions['columnDefinition'] = $joinColumn->columnDefinition;\n                } elseif (isset($fieldMapping->columnDefinition)) {\n                    $columnOptions['columnDefinition'] = $fieldMapping->columnDefinition;\n                }\n\n                if (isset($joinColumn->nullable)) {\n                    $columnOptions['notnull'] = ! $joinColumn->nullable;\n                }\n\n                $columnOptions += $this->gatherColumnOptions($fieldMapping);\n\n                if (isset($fieldMapping->length)) {\n                    $columnOptions['length'] = $fieldMapping->length;\n                }\n\n                if ($fieldMapping->type === 'decimal') {\n                    $columnOptions['scale']     = $fieldMapping->scale;\n                    $columnOptions['precision'] = $fieldMapping->precision;\n                }\n\n                $columnOptions = $this->gatherColumnOptions($joinColumn) + $columnOptions;\n\n                $theJoinTable->addColumn($quotedColumnName, $fieldMapping->type, $columnOptions);\n            }\n\n            if (isset($joinColumn->unique) && $joinColumn->unique === true) {\n                $uniqueConstraints[] = ['columns' => [$quotedColumnName]];\n            }\n\n            if (isset($joinColumn->onDelete)) {\n                $fkOptions['onDelete'] = $joinColumn->onDelete;\n            }\n\n            if (isset($joinColumn->deferrable)) {\n                $fkOptions['deferrable'] = $joinColumn->deferrable;\n            }\n        }\n\n        // Prefer unique constraints over implicit simple indexes created for foreign keys.\n        // Also avoids index duplication.\n        foreach ($uniqueConstraints as $indexName => $unique) {\n            $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName);\n        }\n\n        $compositeName = $this->getAssetName($theJoinTable) . '.' . implode('', $localColumns);\n        if (\n            isset($addedFks[$compositeName])\n            && ($foreignTableName !== $addedFks[$compositeName]['foreignTableName']\n            || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns'])))\n        ) {\n            foreach ($theJoinTable->getForeignKeys() as $fkName => $key) {\n                if (\n                    class_exists(ForeignKeyConstraintEditor::class)\n                    && count(array_diff(array_map(static fn (UnqualifiedName $name) => $name->toString(), $key->getReferencingColumnNames()), $localColumns)) === 0\n                    && (($key->getReferencedTableName()->toString() !== $foreignTableName)\n                    || 0 < count(array_diff(array_map(static fn (UnqualifiedName $name) => $name->toString(), $key->getReferencedColumnNames()), $foreignColumns)))\n                ) {\n                    $theJoinTable->dropForeignKey($fkName);\n                    break;\n                }\n\n                if (\n                    ! class_exists(ForeignKeyConstraintEditor::class)\n                    && count(array_diff($key->getLocalColumns(), $localColumns)) === 0\n                    && (($key->getForeignTableName() !== $foreignTableName)\n                    || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns)))\n                ) {\n                    $theJoinTable->removeForeignKey($fkName);\n                    break;\n                }\n            }\n\n            $blacklistedFks[$compositeName] = true;\n        } elseif (! isset($blacklistedFks[$compositeName])) {\n            $addedFks[$compositeName] = ['foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns];\n            $theJoinTable->addForeignKeyConstraint(\n                $foreignTableName,\n                $localColumns,\n                $foreignColumns,\n                $fkOptions,\n            );\n        }\n    }\n\n    /** @return mixed[] */\n    private function gatherColumnOptions(JoinColumnMapping|FieldMapping|DiscriminatorColumnMapping $mapping): array\n    {\n        $mappingOptions = $mapping->options ?? [];\n\n        if (isset($mapping->enumType)) {\n            $mappingOptions['enumType'] = $mapping->enumType;\n        }\n\n        if (($mappingOptions['default'] ?? null) instanceof BackedEnum) {\n            $mappingOptions['default'] = $mappingOptions['default']->value;\n        }\n\n        if (empty($mappingOptions)) {\n            return [];\n        }\n\n        $options                    = array_intersect_key($mappingOptions, array_flip(self::KNOWN_COLUMN_OPTIONS));\n        $options['platformOptions'] = array_diff_key($mappingOptions, $options);\n\n        return $options;\n    }\n\n    /**\n     * Drops the database schema for the given classes.\n     *\n     * In any way when an exception is thrown it is suppressed since drop was\n     * issued for all classes of the schema and some probably just don't exist.\n     *\n     * @phpstan-param list<ClassMetadata> $classes\n     */\n    public function dropSchema(array $classes): void\n    {\n        $dropSchemaSql = $this->getDropSchemaSQL($classes);\n        $conn          = $this->em->getConnection();\n\n        foreach ($dropSchemaSql as $sql) {\n            try {\n                $conn->executeStatement($sql);\n            } catch (Throwable) {\n                // ignored\n            }\n        }\n    }\n\n    /**\n     * Drops all elements in the database of the current connection.\n     */\n    public function dropDatabase(): void\n    {\n        $dropSchemaSql = $this->getDropDatabaseSQL();\n        $conn          = $this->em->getConnection();\n\n        foreach ($dropSchemaSql as $sql) {\n            $conn->executeStatement($sql);\n        }\n    }\n\n    /**\n     * Gets the SQL needed to drop the database schema for the connections database.\n     *\n     * @return list<string>\n     */\n    public function getDropDatabaseSQL(): array\n    {\n        return $this->schemaManager\n            ->introspectSchema()\n            ->toDropSql($this->platform);\n    }\n\n    /**\n     * Gets SQL to drop the tables defined by the passed classes.\n     *\n     * @phpstan-param list<ClassMetadata> $classes\n     *\n     * @return list<string>\n     */\n    public function getDropSchemaSQL(array $classes): array\n    {\n        $schema = $this->getSchemaFromMetadata($classes);\n\n        $deployedSchema = $this->schemaManager->introspectSchema();\n\n        foreach ($schema->getTables() as $table) {\n            if (! $deployedSchema->hasTable($this->getAssetName($table))) {\n                $schema->dropTable($this->getAssetName($table));\n            }\n        }\n\n        if ($this->platform->supportsSequences()) {\n            foreach ($schema->getSequences() as $sequence) {\n                if (! $deployedSchema->hasSequence($this->getAssetName($sequence))) {\n                    $schema->dropSequence($this->getAssetName($sequence));\n                }\n            }\n\n            foreach ($schema->getTables() as $table) {\n                if (method_exists($table, 'getPrimaryKeyConstraint')) {\n                    $primaryKey = $table->getPrimaryKeyConstraint();\n                } else {\n                    $primaryKey = $table->getPrimaryKey();\n                }\n\n                if ($primaryKey === null) {\n                    continue;\n                }\n\n                if ($primaryKey instanceof PrimaryKeyConstraint) {\n                    $columns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $primaryKey->getColumnNames());\n                } else {\n                    $columns = self::getIndexedColumns($primaryKey);\n                }\n\n                if (count($columns) === 1) {\n                    $checkSequence = $this->getAssetName($table) . '_' . $columns[0] . '_seq';\n                    if ($deployedSchema->hasSequence($checkSequence) && ! $schema->hasSequence($checkSequence)) {\n                        $schema->createSequence($checkSequence);\n                    }\n                }\n            }\n        }\n\n        return $schema->toDropSql($this->platform);\n    }\n\n    /**\n     * Updates the database schema of the given classes by comparing the ClassMetadata\n     * instances to the current database schema that is inspected.\n     *\n     * @param mixed[] $classes\n     */\n    public function updateSchema(array $classes): void\n    {\n        $conn = $this->em->getConnection();\n\n        foreach ($this->getUpdateSchemaSql($classes) as $sql) {\n            $conn->executeStatement($sql);\n        }\n    }\n\n    /**\n     * Gets the sequence of SQL statements that need to be performed in order\n     * to bring the given class mappings in-synch with the relational schema.\n     *\n     * @param list<ClassMetadata> $classes The classes to consider.\n     *\n     * @return list<string> The sequence of SQL statements.\n     */\n    public function getUpdateSchemaSql(array $classes): array\n    {\n        $toSchema   = $this->getSchemaFromMetadata($classes);\n        $fromSchema = $this->createSchemaForComparison($toSchema);\n\n        if (class_exists(ComparatorConfig::class)) {\n            $comparator = $this->schemaManager->createComparator((new ComparatorConfig())->withReportModifiedIndexes(false));\n        } else {\n            $comparator = $this->schemaManager->createComparator();\n        }\n\n        $schemaDiff = $comparator->compareSchemas($fromSchema, $toSchema);\n\n        return $this->platform->getAlterSchemaSQL($schemaDiff);\n    }\n\n    /**\n     * Creates the schema from the database, ensuring tables from the target schema are whitelisted for comparison.\n     */\n    private function createSchemaForComparison(Schema $toSchema): Schema\n    {\n        $connection = $this->em->getConnection();\n\n        // backup schema assets filter\n        $config         = $connection->getConfiguration();\n        $previousFilter = $config->getSchemaAssetsFilter();\n\n        if ($previousFilter === null) {\n            return $this->schemaManager->introspectSchema();\n        }\n\n        // whitelist assets we already know about in $toSchema, use the existing filter otherwise\n        $getAssetName = $this->getAssetName(...);\n        $config->setSchemaAssetsFilter(static function ($asset) use ($previousFilter, $toSchema, $getAssetName): bool {\n            $assetName = $asset instanceof AbstractAsset ? $getAssetName($asset) : $asset;\n\n            return $toSchema->hasTable($assetName) || $toSchema->hasSequence($assetName) || $previousFilter($asset);\n        });\n\n        try {\n            return $this->schemaManager->introspectSchema();\n        } finally {\n            // restore schema assets filter\n            $config->setSchemaAssetsFilter($previousFilter);\n        }\n    }\n\n    /** @param non-empty-array<non-empty-string> $primaryKeyColumns */\n    private function addPrimaryKeyConstraint(Table $table, array $primaryKeyColumns): void\n    {\n        if (! class_exists(PrimaryKeyConstraint::class)) {\n            $table->setPrimaryKey(array_values($primaryKeyColumns));\n\n            return;\n        }\n\n        $primaryKeyColumnNames = [];\n\n        foreach ($primaryKeyColumns as $primaryKeyColumn) {\n            if (preg_match('/^\"(.+)\"$/', $primaryKeyColumn, $matches) === 1) {\n                $primaryKeyColumnNames[] = new UnqualifiedName(Identifier::quoted($matches[1]));\n            } else {\n                $primaryKeyColumnNames[] = new UnqualifiedName(Identifier::unquoted($primaryKeyColumn));\n            }\n        }\n\n        $table->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, $primaryKeyColumnNames, true));\n    }\n\n    /** @return string[] */\n    private static function getIndexedColumns(Index $index): array\n    {\n        if (method_exists(Index::class, 'getIndexedColumns')) {\n            return array_map(static fn (IndexedColumn $indexedColumn) => $indexedColumn->getColumnName()->toString(), $index->getIndexedColumns());\n        }\n\n        return $index->getColumns();\n    }\n\n    private function getAssetName(AbstractAsset $asset): string\n    {\n        return $asset instanceof NamedObject\n            ? $asset->getObjectName()->toString()\n            // @phpstan-ignore method.deprecated (DBAL < 4.4)\n            : $asset->getName();\n    }\n}\n"
  },
  {
    "path": "src/Tools/SchemaValidator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse BackedEnum;\nuse Doctrine\\DBAL\\Types\\AsciiStringType;\nuse Doctrine\\DBAL\\Types\\BigIntType;\nuse Doctrine\\DBAL\\Types\\BooleanType;\nuse Doctrine\\DBAL\\Types\\DecimalType;\nuse Doctrine\\DBAL\\Types\\FloatType;\nuse Doctrine\\DBAL\\Types\\GuidType;\nuse Doctrine\\DBAL\\Types\\IntegerType;\nuse Doctrine\\DBAL\\Types\\JsonType;\nuse Doctrine\\DBAL\\Types\\SimpleArrayType;\nuse Doctrine\\DBAL\\Types\\SmallIntType;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\TextType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\FieldMapping;\nuse ReflectionEnum;\nuse ReflectionNamedType;\n\nuse function array_diff;\nuse function array_filter;\nuse function array_key_exists;\nuse function array_map;\nuse function array_push;\nuse function array_search;\nuse function array_values;\nuse function class_exists;\nuse function class_parents;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function interface_exists;\nuse function is_a;\nuse function sprintf;\n\n/**\n * Performs strict validation of the mapping schema\n *\n * @link        www.doctrine-project.com\n */\nclass SchemaValidator\n{\n    /**\n     * It maps built-in Doctrine types to PHP types\n     */\n    private const BUILTIN_TYPES_MAP = [\n        AsciiStringType::class => ['string'],\n        BigIntType::class => ['int', 'string'],\n        BooleanType::class => ['bool'],\n        DecimalType::class => ['string'],\n        FloatType::class => ['float'],\n        GuidType::class => ['string'],\n        IntegerType::class => ['int'],\n        JsonType::class => ['array'],\n        SimpleArrayType::class => ['array'],\n        SmallIntType::class => ['int'],\n        StringType::class => ['string'],\n        TextType::class => ['string'],\n    ];\n\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n        private readonly bool $validatePropertyTypes = true,\n    ) {\n    }\n\n    /**\n     * Checks the internal consistency of all mapping files.\n     *\n     * There are several checks that can't be done at runtime or are too expensive, which can be verified\n     * with this command. For example:\n     *\n     * 1. Check if a relation with \"mappedBy\" is actually connected to that specified field.\n     * 2. Check if \"mappedBy\" and \"inversedBy\" are consistent to each other.\n     * 3. Check if \"referencedColumnName\" attributes are really pointing to primary key columns.\n     *\n     * @phpstan-return array<string, list<string>>\n     */\n    public function validateMapping(): array\n    {\n        $errors  = [];\n        $cmf     = $this->em->getMetadataFactory();\n        $classes = $cmf->getAllMetadata();\n\n        foreach ($classes as $class) {\n            $ce = $this->validateClass($class);\n            if ($ce) {\n                $errors[$class->name] = $ce;\n            }\n        }\n\n        return $errors;\n    }\n\n    /**\n     * Validates a single class of the current.\n     *\n     * @return string[]\n     * @phpstan-return list<string>\n     */\n    public function validateClass(ClassMetadata $class): array\n    {\n        $ce  = [];\n        $cmf = $this->em->getMetadataFactory();\n\n        foreach ($class->fieldMappings as $fieldName => $mapping) {\n            if (! Type::hasType($mapping->type)) {\n                $ce[] = \"The field '\" . $class->name . '#' . $fieldName . \"' uses a non-existent type '\" . $mapping->type . \"'.\";\n            }\n        }\n\n        if ($this->validatePropertyTypes) {\n            array_push($ce, ...$this->validatePropertiesTypes($class));\n        }\n\n        foreach ($class->associationMappings as $fieldName => $assoc) {\n            if (! class_exists($assoc->targetEntity) || $cmf->isTransient($assoc->targetEntity)) {\n                $ce[] = \"The target entity '\" . $assoc->targetEntity . \"' specified on \" . $class->name . '#' . $fieldName . ' is unknown or not an entity.';\n\n                return $ce;\n            }\n\n            $targetMetadata = $cmf->getMetadataFor($assoc->targetEntity);\n\n            if ($targetMetadata->isMappedSuperclass) {\n                $ce[] = \"The target entity '\" . $assoc->targetEntity . \"' specified on \" . $class->name . '#' . $fieldName . ' is a mapped superclass. This is not possible since there is no table that a foreign key could refer to.';\n\n                return $ce;\n            }\n\n            if (isset($assoc->id) && $targetMetadata->containsForeignIdentifier) {\n                $ce[] = \"Cannot map association '\" . $class->name . '#' . $fieldName . ' as identifier, because ' .\n                        \"the target entity '\" . $targetMetadata->name . \"' also maps an association as identifier.\";\n            }\n\n            if (! $assoc->isOwningSide()) {\n                if ($targetMetadata->hasField($assoc->mappedBy)) {\n                    $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' .\n                            'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which is not defined as association, but as field.';\n                }\n\n                if (! $targetMetadata->hasAssociation($assoc->mappedBy)) {\n                    $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' .\n                            'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which does not exist.';\n                } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy === null) {\n                    $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the inverse side of a ' .\n                            'bi-directional relationship, but the specified mappedBy association on the target-entity ' .\n                            $assoc->targetEntity . '#' . $assoc->mappedBy . ' does not contain the required ' .\n                            \"'inversedBy: \\\"\" . $fieldName . \"\\\"' attribute.\";\n                } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy !== $fieldName) {\n                    $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' .\n                            $assoc->targetEntity . '#' . $assoc->mappedBy . ' are ' .\n                            'inconsistent with each other.';\n                }\n            }\n\n            if ($assoc->isOwningSide() && $assoc->inversedBy !== null) {\n                if ($targetMetadata->hasField($assoc->inversedBy)) {\n                    $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' .\n                            'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which is not defined as association.';\n                }\n\n                if (! $targetMetadata->hasAssociation($assoc->inversedBy)) {\n                    $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' .\n                            'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which does not exist.';\n                } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->isOwningSide()) {\n                    $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the owning side of a ' .\n                            'bi-directional relationship, but the specified inversedBy association on the target-entity ' .\n                            $assoc->targetEntity . '#' . $assoc->inversedBy . ' does not contain the required ' .\n                            \"'mappedBy: \\\"\" . $fieldName . \"\\\"' attribute.\";\n                } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy !== $fieldName) {\n                    $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' .\n                            $assoc->targetEntity . '#' . $assoc->inversedBy . ' are ' .\n                            'inconsistent with each other.';\n                }\n\n                // Verify inverse side/owning side match each other\n                if (array_key_exists($assoc->inversedBy, $targetMetadata->associationMappings)) {\n                    $targetAssoc = $targetMetadata->associationMappings[$assoc->inversedBy];\n                    if ($assoc->isOneToOne() && ! $targetAssoc->isOneToOne()) {\n                        $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is one-to-one, then the inversed ' .\n                                'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-one as well.';\n                    } elseif ($assoc->isManyToOne() && ! $targetAssoc->isOneToMany()) {\n                        $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-one, then the inversed ' .\n                                'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-many.';\n                    } elseif ($assoc->isManyToMany() && ! $targetAssoc->isManyToMany()) {\n                        $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-many, then the inversed ' .\n                                'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be many-to-many as well.';\n                    }\n                }\n            }\n\n            if ($assoc->isOwningSide()) {\n                if ($assoc->isManyToManyOwningSide()) {\n                    $identifierColumns = $class->getIdentifierColumnNames();\n                    foreach ($assoc->joinTable->joinColumns as $joinColumn) {\n                        if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) {\n                            $ce[] = \"The referenced column name '\" . $joinColumn->referencedColumnName . \"' \" .\n                                \"has to be a primary key column on the target entity class '\" . $class->name . \"'.\";\n                            break;\n                        }\n                    }\n\n                    $identifierColumns = $targetMetadata->getIdentifierColumnNames();\n                    foreach ($assoc->joinTable->inverseJoinColumns as $inverseJoinColumn) {\n                        if (! in_array($inverseJoinColumn->referencedColumnName, $identifierColumns, true)) {\n                            $ce[] = \"The referenced column name '\" . $inverseJoinColumn->referencedColumnName . \"' \" .\n                                \"has to be a primary key column on the target entity class '\" . $targetMetadata->name . \"'.\";\n                            break;\n                        }\n                    }\n\n                    if (count($targetMetadata->getIdentifierColumnNames()) !== count($assoc->joinTable->inverseJoinColumns)) {\n                        $ce[] = \"The inverse join columns of the many-to-many table '\" . $assoc->joinTable->name . \"' \" .\n                                \"have to contain to ALL identifier columns of the target entity '\" . $targetMetadata->name . \"', \" .\n                                \"however '\" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc->relationToTargetKeyColumns))) .\n                                \"' are missing.\";\n                    }\n\n                    if (count($class->getIdentifierColumnNames()) !== count($assoc->joinTable->joinColumns)) {\n                        $ce[] = \"The join columns of the many-to-many table '\" . $assoc->joinTable->name . \"' \" .\n                                \"have to contain to ALL identifier columns of the source entity '\" . $class->name . \"', \" .\n                                \"however '\" . implode(', ', array_diff($class->getIdentifierColumnNames(), array_values($assoc->relationToSourceKeyColumns))) .\n                                \"' are missing.\";\n                    }\n                } elseif ($assoc->isToOneOwningSide()) {\n                    $identifierColumns = $targetMetadata->getIdentifierColumnNames();\n                    foreach ($assoc->joinColumns as $joinColumn) {\n                        if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) {\n                            $ce[] = \"The referenced column name '\" . $joinColumn->referencedColumnName . \"' \" .\n                                    \"has to be a primary key column on the target entity class '\" . $targetMetadata->name . \"'.\";\n                        }\n                    }\n\n                    if (count($identifierColumns) !== count($assoc->joinColumns)) {\n                        $ids = [];\n\n                        foreach ($assoc->joinColumns as $joinColumn) {\n                            $ids[] = $joinColumn->name;\n                        }\n\n                        $ce[] = \"The join columns of the association '\" . $assoc->fieldName . \"' \" .\n                                \"have to match to ALL identifier columns of the target entity '\" . $targetMetadata->name . \"', \" .\n                                \"however '\" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) .\n                                \"' are missing.\";\n                    }\n                }\n            }\n\n            if ($assoc->isOrdered()) {\n                foreach ($assoc->orderBy() as $orderField => $orientation) {\n                    if (! $targetMetadata->hasField($orderField) && ! $targetMetadata->hasAssociation($orderField)) {\n                        $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a foreign field ' .\n                                $orderField . ' that is not a field on the target entity ' . $targetMetadata->name . '.';\n                        continue;\n                    }\n\n                    if ($targetMetadata->isCollectionValuedAssociation($orderField)) {\n                        $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a field ' .\n                                $orderField . ' on ' . $targetMetadata->name . ' that is a collection-valued association.';\n                        continue;\n                    }\n\n                    if ($targetMetadata->isAssociationInverseSide($orderField)) {\n                        $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a field ' .\n                                $orderField . ' on ' . $targetMetadata->name . ' that is the inverse side of an association.';\n                        continue;\n                    }\n                }\n            }\n        }\n\n        if (\n            ! $class->isInheritanceTypeNone()\n            && ! $class->isRootEntity()\n            && ($class->reflClass !== null && ! $class->reflClass->isAbstract())\n            && ! $class->isMappedSuperclass\n            && array_search($class->name, $class->discriminatorMap, true) === false\n        ) {\n            $ce[] = \"Entity class '\" . $class->name . \"' is part of inheritance hierarchy, but is \" .\n                \"not mapped in the root entity '\" . $class->rootEntityName . \"' discriminator map. \" .\n                'All subclasses must be listed in the discriminator map.';\n        }\n\n        foreach ($class->subClasses as $subClass) {\n            if (! in_array($class->name, class_parents($subClass), true)) {\n                $ce[] = \"According to the discriminator map class '\" . $subClass . \"' has to be a child \" .\n                        \"of '\" . $class->name . \"' but these entities are not related through inheritance.\";\n            }\n        }\n\n        return $ce;\n    }\n\n    /**\n     * Checks if the Database Schema is in sync with the current metadata state.\n     */\n    public function schemaInSyncWithMetadata(): bool\n    {\n        return count($this->getUpdateSchemaList()) === 0;\n    }\n\n    /**\n     * Returns the list of missing Database Schema updates.\n     *\n     * @return array<string>\n     */\n    public function getUpdateSchemaList(): array\n    {\n        $schemaTool = new SchemaTool($this->em);\n\n        $allMetadata = $this->em->getMetadataFactory()->getAllMetadata();\n\n        return $schemaTool->getUpdateSchemaSql($allMetadata);\n    }\n\n    /** @return list<string> containing the found issues */\n    private function validatePropertiesTypes(ClassMetadata $class): array\n    {\n        return array_values(\n            array_filter(\n                array_map(\n                    function (FieldMapping $fieldMapping) use ($class): string|null {\n                        $fieldName    = $fieldMapping->fieldName;\n                        $propertyType = $class->propertyAccessors[$fieldName]->getUnderlyingReflector()->getType();\n\n                        // If the field type is not a built-in type, we cannot check it\n                        if (! Type::hasType($fieldMapping->type)) {\n                            return null;\n                        }\n\n                        // If the property type is not a named type, we cannot check it\n                        if (! ($propertyType instanceof ReflectionNamedType) || $propertyType->getName() === 'mixed') {\n                            return null;\n                        }\n\n                        $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping->type));\n\n                        //If the metadata field type is not a mapped built-in type, we cannot check it\n                        if ($metadataFieldType === null) {\n                            return null;\n                        }\n\n                        $propertyType = $propertyType->getName();\n\n                        // If the property type is the same as the metadata field type, we are ok\n                        if (in_array($propertyType, $metadataFieldType, true)) {\n                            return null;\n                        }\n\n                        if (is_a($propertyType, BackedEnum::class, true)) {\n                            $backingType = (string) (new ReflectionEnum($propertyType))->getBackingType();\n\n                            if (! in_array($backingType, $metadataFieldType, true)) {\n                                return sprintf(\n                                    \"The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.\",\n                                    $class->name,\n                                    $fieldName,\n                                    $propertyType,\n                                    $backingType,\n                                    implode('|', $metadataFieldType),\n                                );\n                            }\n\n                            if (! isset($fieldMapping->enumType) || $propertyType === $fieldMapping->enumType) {\n                                return null;\n                            }\n\n                            return sprintf(\n                                \"The field '%s#%s' has the property type '%s' that differs from the metadata enumType '%s'.\",\n                                $class->name,\n                                $fieldName,\n                                $propertyType,\n                                $fieldMapping->enumType,\n                            );\n                        }\n\n                        if (\n                            isset($fieldMapping->enumType)\n                            && $propertyType !== $fieldMapping->enumType\n                            && interface_exists($propertyType)\n                            && is_a($fieldMapping->enumType, $propertyType, true)\n                        ) {\n                            $backingType = (string) (new ReflectionEnum($fieldMapping->enumType))->getBackingType();\n\n                            if (in_array($backingType, $metadataFieldType, true)) {\n                                return null;\n                            }\n\n                            return sprintf(\n                                \"The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.\",\n                                $class->name,\n                                $fieldName,\n                                $fieldMapping->enumType,\n                                $backingType,\n                                implode('|', $metadataFieldType),\n                            );\n                        }\n\n                        if (\n                            $fieldMapping->type === 'json'\n                            && in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true)\n                        ) {\n                            return null;\n                        }\n\n                        return sprintf(\n                            \"The field '%s#%s' has the property type '%s' that differs from the metadata field type '%s' returned by the '%s' DBAL type.\",\n                            $class->name,\n                            $fieldName,\n                            $propertyType,\n                            implode('|', $metadataFieldType),\n                            $fieldMapping->type,\n                        );\n                    },\n                    $class->fieldMappings,\n                ),\n            ),\n        );\n    }\n\n    /**\n     * The exact DBAL type must be used (no subclasses), since consumers of doctrine/orm may have their own\n     * customization around field types.\n     *\n     * @return list<string>|null\n     */\n    private function findBuiltInType(Type $type): array|null\n    {\n        $typeName = $type::class;\n\n        return self::BUILTIN_TYPES_MAP[$typeName] ?? null;\n    }\n}\n"
  },
  {
    "path": "src/Tools/ToolEvents.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nclass ToolEvents\n{\n    /**\n     * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata()\n     * whenever an entity class is transformed into its table representation. It receives\n     * the current non-complete Schema instance, the Entity Metadata Class instance and\n     * the Schema Table instance of this entity.\n     */\n    public const postGenerateSchemaTable = 'postGenerateSchemaTable';\n\n    /**\n     * The postGenerateSchema event is triggered in SchemaTool#getSchemaFromMetadata()\n     * after all entity classes have been transformed into the related Schema structure.\n     * The EventArgs contain the EntityManager and the created Schema instance.\n     */\n    public const postGenerateSchema = 'postGenerateSchema';\n}\n"
  },
  {
    "path": "src/Tools/ToolsException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Tools;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse RuntimeException;\nuse Throwable;\n\n/**\n * Tools related Exceptions.\n */\nclass ToolsException extends RuntimeException implements ORMException\n{\n    public static function schemaToolFailure(string $sql, Throwable $e): self\n    {\n        return new self(\n            \"Schema-Tool failed with Error '\" . $e->getMessage() . \"' while executing DDL: \" . $sql,\n            0,\n            $e,\n        );\n    }\n}\n"
  },
  {
    "path": "src/TransactionRequiredException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse LogicException;\n\n/**\n * Is thrown when a transaction is required for the current operation, but there is none open.\n *\n * @link        www.doctrine-project.com\n */\nclass TransactionRequiredException extends LogicException implements ORMException\n{\n    public static function transactionRequired(): self\n    {\n        return new self('An open transaction is required for this operation.');\n    }\n}\n"
  },
  {
    "path": "src/UnexpectedResultException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse RuntimeException;\n\n/**\n * Exception for a unexpected query result.\n */\nclass UnexpectedResultException extends RuntimeException implements ORMException\n{\n}\n"
  },
  {
    "path": "src/UnitOfWork.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM;\n\nuse BackedEnum;\nuse DateTimeInterface;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL;\nuse Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Event\\ListenersInvoker;\nuse Doctrine\\ORM\\Event\\OnClearEventArgs;\nuse Doctrine\\ORM\\Event\\OnFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PostFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PostPersistEventArgs;\nuse Doctrine\\ORM\\Event\\PostRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PostUpdateEventArgs;\nuse Doctrine\\ORM\\Event\\PreFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PrePersistEventArgs;\nuse Doctrine\\ORM\\Event\\PreRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PreUpdateEventArgs;\nuse Doctrine\\ORM\\Exception\\EntityIdentityCollisionException;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Exception\\UnexpectedAssociationValue;\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Internal\\HydrationCompleteHandler;\nuse Doctrine\\ORM\\Internal\\StronglyConnectedComponents;\nuse Doctrine\\ORM\\Internal\\TopologicalSort;\nuse Doctrine\\ORM\\Internal\\UnitOfWork\\InsertBatch;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\ToManyInverseSideMapping;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister;\nuse Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\SingleTablePersister;\nuse Doctrine\\ORM\\Proxy\\InternalProxy;\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\nuse Doctrine\\Persistence\\PropertyChangedListener;\nuse Exception;\nuse InvalidArgumentException;\nuse RuntimeException;\nuse Stringable;\nuse Symfony\\Component\\VarExporter\\Hydrator;\nuse UnexpectedValueException;\n\nuse function array_chunk;\nuse function array_combine;\nuse function array_diff_key;\nuse function array_filter;\nuse function array_key_exists;\nuse function array_map;\nuse function array_sum;\nuse function array_values;\nuse function assert;\nuse function count;\nuse function current;\nuse function get_debug_type;\nuse function implode;\nuse function in_array;\nuse function is_array;\nuse function is_object;\nuse function reset;\nuse function spl_object_id;\nuse function sprintf;\nuse function strtolower;\n\n/**\n * The UnitOfWork is responsible for tracking changes to objects during an\n * \"object-level\" transaction and for writing out changes to the database\n * in the correct order.\n *\n * Internal note: This class contains highly performance-sensitive code.\n */\nclass UnitOfWork implements PropertyChangedListener\n{\n    /**\n     * An entity is in MANAGED state when its persistence is managed by an EntityManager.\n     */\n    public const STATE_MANAGED = 1;\n\n    /**\n     * An entity is new if it has just been instantiated (i.e. using the \"new\" operator)\n     * and is not (yet) managed by an EntityManager.\n     */\n    public const STATE_NEW = 2;\n\n    /**\n     * A detached entity is an instance with persistent state and identity that is not\n     * (or no longer) associated with an EntityManager (and a UnitOfWork).\n     */\n    public const STATE_DETACHED = 3;\n\n    /**\n     * A removed entity instance is an instance with a persistent identity,\n     * associated with an EntityManager, whose persistent state will be deleted\n     * on commit.\n     */\n    public const STATE_REMOVED = 4;\n\n    /**\n     * Hint used to collect all primary keys of associated entities during hydration\n     * and execute it in a dedicated query afterwards\n     *\n     * @see https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql\n     */\n    public const HINT_DEFEREAGERLOAD = 'deferEagerLoad';\n\n    /**\n     * The identity map that holds references to all managed entities that have\n     * an identity. The entities are grouped by their class name.\n     * Since all classes in a hierarchy must share the same identifier set,\n     * we always take the root class name of the hierarchy.\n     *\n     * @var array<class-string, array<string, object>>\n     */\n    private array $identityMap = [];\n\n    /**\n     * Map of all identifiers of managed entities.\n     * Keys are object ids (spl_object_id).\n     *\n     * @phpstan-var array<int, array<string, mixed>>\n     */\n    private array $entityIdentifiers = [];\n\n    /**\n     * Map of the original entity data of managed entities.\n     * Keys are object ids (spl_object_id). This is used for calculating changesets\n     * at commit time.\n     *\n     * Internal note: Note that PHPs \"copy-on-write\" behavior helps a lot with memory usage.\n     *                A value will only really be copied if the value in the entity is modified\n     *                by the user.\n     *\n     * @phpstan-var array<int, array<string, mixed>>\n     */\n    private array $originalEntityData = [];\n\n    /**\n     * Map of entity changes. Keys are object ids (spl_object_id).\n     * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end.\n     *\n     * @phpstan-var array<int, array<string, array{mixed, mixed}>>\n     */\n    private array $entityChangeSets = [];\n\n    /**\n     * The (cached) states of any known entities.\n     * Keys are object ids (spl_object_id).\n     *\n     * @phpstan-var array<int, self::STATE_*>\n     */\n    private array $entityStates = [];\n\n    /**\n     * Map of entities that are scheduled for dirty checking at commit time.\n     * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT.\n     * Keys are object ids (spl_object_id).\n     *\n     * @var array<class-string, array<int, mixed>>\n     */\n    private array $scheduledForSynchronization = [];\n\n    /**\n     * A list of all pending entity insertions.\n     *\n     * @phpstan-var array<int, object>\n     */\n    private array $entityInsertions = [];\n\n    /**\n     * A list of all pending entity updates.\n     *\n     * @phpstan-var array<int, object>\n     */\n    private array $entityUpdates = [];\n\n    /**\n     * Any pending extra updates that have been scheduled by persisters.\n     *\n     * @phpstan-var array<int, array{object, array<string, array{mixed, mixed}>}>\n     */\n    private array $extraUpdates = [];\n\n    /**\n     * A list of all pending entity deletions.\n     *\n     * @phpstan-var array<int, object>\n     */\n    private array $entityDeletions = [];\n\n    /**\n     * New entities that were discovered through relationships that were not\n     * marked as cascade-persist. During flush, this array is populated and\n     * then pruned of any entities that were discovered through a valid\n     * cascade-persist path. (Leftovers cause an error.)\n     *\n     * Keys are OIDs, payload is a two-item array describing the association\n     * and the entity.\n     *\n     * @var array<int, array{AssociationMapping, object}> indexed by respective object spl_object_id()\n     */\n    private array $nonCascadedNewDetectedEntities = [];\n\n    /**\n     * All pending collection deletions.\n     *\n     * @phpstan-var array<int, PersistentCollection<array-key, object>>\n     */\n    private array $collectionDeletions = [];\n\n    /**\n     * All pending collection updates.\n     *\n     * @phpstan-var array<int, PersistentCollection<array-key, object>>\n     */\n    private array $collectionUpdates = [];\n\n    /**\n     * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork.\n     * At the end of the UnitOfWork all these collections will make new snapshots\n     * of their data.\n     *\n     * @phpstan-var array<int, PersistentCollection<array-key, object>>\n     */\n    private array $visitedCollections = [];\n\n    /**\n     * List of collections visited during the changeset calculation that contain to-be-removed\n     * entities and need to have keys removed post commit.\n     *\n     * Indexed by Collection object ID, which also serves as the key in self::$visitedCollections;\n     * values are the key names that need to be removed.\n     *\n     * @phpstan-var array<int, array<array-key, true>>\n     */\n    private array $pendingCollectionElementRemovals = [];\n\n    /**\n     * The entity persister instances used to persist entity instances.\n     *\n     * @phpstan-var array<string, EntityPersister>\n     */\n    private array $persisters = [];\n\n    /**\n     * The collection persister instances used to persist collections.\n     *\n     * @phpstan-var array<array-key, CollectionPersister>\n     */\n    private array $collectionPersisters = [];\n\n    /**\n     * The EventManager used for dispatching events.\n     */\n    private readonly EventManager $evm;\n\n    /**\n     * The ListenersInvoker used for dispatching events.\n     */\n    private readonly ListenersInvoker $listenersInvoker;\n\n    /**\n     * The IdentifierFlattener used for manipulating identifiers\n     */\n    private readonly IdentifierFlattener $identifierFlattener;\n\n    /**\n     * Orphaned entities that are scheduled for removal.\n     *\n     * @phpstan-var array<int, object>\n     */\n    private array $orphanRemovals = [];\n\n    /**\n     * Read-Only objects are never evaluated\n     *\n     * @var array<int, true>\n     */\n    private array $readOnlyObjects = [];\n\n    /**\n     * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.\n     *\n     * @var array<class-string, array<string, mixed>>\n     */\n    private array $eagerLoadingEntities = [];\n\n    /** @var array<string, array<string, mixed>> */\n    private array $eagerLoadingCollections = [];\n\n    protected bool $hasCache = false;\n\n    /**\n     * Helper for handling completion of hydration\n     */\n    private readonly HydrationCompleteHandler $hydrationCompleteHandler;\n\n    /**\n     * Initializes a new UnitOfWork instance, bound to the given EntityManager.\n     *\n     * @param EntityManagerInterface $em The EntityManager that \"owns\" this UnitOfWork instance.\n     */\n    public function __construct(\n        private readonly EntityManagerInterface $em,\n    ) {\n        $this->evm                      = $em->getEventManager();\n        $this->listenersInvoker         = new ListenersInvoker($em);\n        $this->hasCache                 = $em->getConfiguration()->isSecondLevelCacheEnabled();\n        $this->identifierFlattener      = new IdentifierFlattener($this, $em->getMetadataFactory());\n        $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em);\n    }\n\n    /**\n     * Commits the UnitOfWork, executing all operations that have been postponed\n     * up to this point. The state of all managed entities will be synchronized with\n     * the database.\n     *\n     * The operations are executed in the following order:\n     *\n     * 1) All entity insertions\n     * 2) All entity updates\n     * 3) All collection deletions\n     * 4) All collection updates\n     * 5) All entity deletions\n     *\n     * @throws Exception\n     */\n    public function commit(): void\n    {\n        $connection = $this->em->getConnection();\n\n        if ($connection instanceof PrimaryReadReplicaConnection) {\n            $connection->ensureConnectedToPrimary();\n        }\n\n        // Raise preFlush\n        if ($this->evm->hasListeners(Events::preFlush)) {\n            $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));\n        }\n\n        // Compute changes done since last commit.\n        $this->computeChangeSets();\n\n        if (\n            ! ($this->entityInsertions ||\n                $this->entityDeletions ||\n                $this->entityUpdates ||\n                $this->collectionUpdates ||\n                $this->collectionDeletions ||\n                $this->orphanRemovals)\n        ) {\n            $this->dispatchOnFlushEvent();\n            $this->dispatchPostFlushEvent();\n\n            $this->postCommitCleanup();\n\n            return; // Nothing to do.\n        }\n\n        $this->assertThatThereAreNoUnintentionallyNonPersistedAssociations();\n\n        if ($this->orphanRemovals) {\n            foreach ($this->orphanRemovals as $orphan) {\n                $this->remove($orphan);\n            }\n        }\n\n        $this->dispatchOnFlushEvent();\n\n        $conn = $this->em->getConnection();\n        $conn->beginTransaction();\n\n        $successful = false;\n\n        try {\n            // Collection deletions (deletions of complete collections)\n            foreach ($this->collectionDeletions as $collectionToDelete) {\n                // Deferred explicit tracked collections can be removed only when owning relation was persisted\n                $owner = $collectionToDelete->getOwner();\n\n                if ($this->em->getClassMetadata($owner::class)->isChangeTrackingDeferredImplicit() || $this->isScheduledForDirtyCheck($owner)) {\n                    $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);\n                }\n            }\n\n            if ($this->entityInsertions) {\n                // Perform entity insertions first, so that all new entities have their rows in the database\n                // and can be referred to by foreign keys. The commit order only needs to take new entities\n                // into account (new entities referring to other new entities), since all other types (entities\n                // with updates or scheduled deletions) are currently not a problem, since they are already\n                // in the database.\n                $this->executeInserts();\n            }\n\n            if ($this->entityUpdates) {\n                // Updates do not need to follow a particular order\n                $this->executeUpdates();\n            }\n\n            // Extra updates that were requested by persisters.\n            // This may include foreign keys that could not be set when an entity was inserted,\n            // which may happen in the case of circular foreign key relationships.\n            if ($this->extraUpdates) {\n                $this->executeExtraUpdates();\n            }\n\n            // Collection updates (deleteRows, updateRows, insertRows)\n            // No particular order is necessary, since all entities themselves are already\n            // in the database\n            foreach ($this->collectionUpdates as $collectionToUpdate) {\n                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);\n            }\n\n            // Entity deletions come last. Their order only needs to take care of other deletions\n            // (first delete entities depending upon others, before deleting depended-upon entities).\n            if ($this->entityDeletions) {\n                $this->executeDeletions();\n            }\n\n            $commitFailed = false;\n            try {\n                if ($conn->commit() === false) {\n                    $commitFailed = true;\n                }\n            } catch (DBAL\\Exception $e) {\n                $commitFailed = true;\n            }\n\n            if ($commitFailed) {\n                throw new OptimisticLockException('Commit failed', null, $e ?? null);\n            }\n\n            $successful = true;\n        } finally {\n            if (! $successful) {\n                $this->em->close();\n\n                if ($conn->isTransactionActive()) {\n                    $conn->rollBack();\n                }\n\n                $this->afterTransactionRolledBack();\n            }\n        }\n\n        $this->afterTransactionComplete();\n\n        // Unset removed entities from collections, and take new snapshots from\n        // all visited collections.\n        foreach ($this->visitedCollections as $coid => $coll) {\n            if (isset($this->pendingCollectionElementRemovals[$coid])) {\n                foreach ($this->pendingCollectionElementRemovals[$coid] as $key => $valueIgnored) {\n                    unset($coll[$key]);\n                }\n            }\n\n            $coll->takeSnapshot();\n        }\n\n        $this->dispatchPostFlushEvent();\n\n        $this->postCommitCleanup();\n    }\n\n    private function postCommitCleanup(): void\n    {\n        $this->entityInsertions                 =\n        $this->entityUpdates                    =\n        $this->entityDeletions                  =\n        $this->extraUpdates                     =\n        $this->collectionUpdates                =\n        $this->nonCascadedNewDetectedEntities   =\n        $this->collectionDeletions              =\n        $this->pendingCollectionElementRemovals =\n        $this->visitedCollections               =\n        $this->orphanRemovals                   =\n        $this->entityChangeSets                 =\n        $this->scheduledForSynchronization      = [];\n    }\n\n    /**\n     * Computes the changesets of all entities scheduled for insertion.\n     */\n    private function computeScheduleInsertsChangeSets(): void\n    {\n        foreach ($this->entityInsertions as $entity) {\n            $class = $this->em->getClassMetadata($entity::class);\n\n            $this->computeChangeSet($class, $entity);\n        }\n    }\n\n    /**\n     * Executes any extra updates that have been scheduled.\n     */\n    private function executeExtraUpdates(): void\n    {\n        foreach ($this->extraUpdates as $oid => $update) {\n            [$entity, $changeset] = $update;\n\n            $this->entityChangeSets[$oid] = $changeset;\n            $this->getEntityPersister($entity::class)->update($entity);\n        }\n\n        $this->extraUpdates = [];\n    }\n\n    /**\n     * Gets the changeset for an entity.\n     *\n     * @return mixed[][]\n     * @phpstan-return array<string, array{mixed, mixed}|PersistentCollection>\n     */\n    public function & getEntityChangeSet(object $entity): array\n    {\n        $oid  = spl_object_id($entity);\n        $data = [];\n\n        if (! isset($this->entityChangeSets[$oid])) {\n            return $data;\n        }\n\n        return $this->entityChangeSets[$oid];\n    }\n\n    /**\n     * Computes the changes that happened to a single entity.\n     *\n     * Modifies/populates the following properties:\n     *\n     * {@link _originalEntityData}\n     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)\n     * then it was not fetched from the database and therefore we have no original\n     * entity data yet. All of the current entity data is stored as the original entity data.\n     *\n     * {@link _entityChangeSets}\n     * The changes detected on all properties of the entity are stored there.\n     * A change is a tuple array where the first entry is the old value and the second\n     * entry is the new value of the property. Changesets are used by persisters\n     * to INSERT/UPDATE the persistent entity state.\n     *\n     * {@link _entityUpdates}\n     * If the entity is already fully MANAGED (has been fetched from the database before)\n     * and any changes to its properties are detected, then a reference to the entity is stored\n     * there to mark it for an update.\n     *\n     * {@link _collectionDeletions}\n     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,\n     * then this collection is marked for deletion.\n     *\n     * @param ClassMetadata $class  The class descriptor of the entity.\n     * @param object        $entity The entity for which to compute the changes.\n     * @phpstan-param ClassMetadata<T> $class\n     * @phpstan-param T $entity\n     *\n     * @template T of object\n     *\n     * @ignore\n     */\n    public function computeChangeSet(ClassMetadata $class, object $entity): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($this->readOnlyObjects[$oid])) {\n            return;\n        }\n\n        if (! $class->isInheritanceTypeNone()) {\n            $class = $this->em->getClassMetadata($entity::class);\n        }\n\n        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER;\n\n        if ($invoke !== ListenersInvoker::INVOKE_NONE) {\n            $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke);\n        }\n\n        $actualData = [];\n\n        foreach ($class->propertyAccessors as $name => $refProp) {\n            $value = $refProp->getValue($entity);\n\n            if ($class->isCollectionValuedAssociation($name) && $value !== null) {\n                if ($value instanceof PersistentCollection) {\n                    if ($value->getOwner() === $entity) {\n                        $actualData[$name] = $value;\n                        continue;\n                    }\n\n                    $value = new ArrayCollection($value->getValues());\n                }\n\n                // If $value is not a Collection then use an ArrayCollection.\n                if (! $value instanceof Collection) {\n                    $value = new ArrayCollection($value);\n                }\n\n                $assoc = $class->associationMappings[$name];\n                assert($assoc->isToMany());\n\n                // Inject PersistentCollection\n                $value = new PersistentCollection(\n                    $this->em,\n                    $this->em->getClassMetadata($assoc->targetEntity),\n                    $value,\n                );\n                $value->setOwner($entity, $assoc);\n                $value->setDirty(! $value->isEmpty());\n\n                $refProp->setValue($entity, $value);\n\n                $actualData[$name] = $value;\n\n                continue;\n            }\n\n            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) {\n                $actualData[$name] = $value;\n            }\n        }\n\n        if (! isset($this->originalEntityData[$oid])) {\n            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).\n            // These result in an INSERT.\n            $this->originalEntityData[$oid] = $actualData;\n            $changeSet                      = [];\n\n            foreach ($actualData as $propName => $actualValue) {\n                if (! isset($class->associationMappings[$propName])) {\n                    $changeSet[$propName] = [null, $actualValue];\n\n                    continue;\n                }\n\n                $assoc = $class->associationMappings[$propName];\n\n                if ($assoc->isToOneOwningSide()) {\n                    $changeSet[$propName] = [null, $actualValue];\n                }\n            }\n\n            $this->entityChangeSets[$oid] = $changeSet;\n        } else {\n            // Entity is \"fully\" MANAGED: it was already fully persisted before\n            // and we have a copy of the original data\n            $originalData = $this->originalEntityData[$oid];\n            $changeSet    = [];\n\n            foreach ($actualData as $propName => $actualValue) {\n                // skip field, its a partially omitted one!\n                if (! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {\n                    continue;\n                }\n\n                $orgValue = $originalData[$propName];\n\n                if (! empty($class->fieldMappings[$propName]->enumType)) {\n                    if (is_array($orgValue)) {\n                        foreach ($orgValue as $id => $val) {\n                            if ($val instanceof BackedEnum) {\n                                $orgValue[$id] = $val->value;\n                            }\n                        }\n                    } else {\n                        if ($orgValue instanceof BackedEnum) {\n                            $orgValue = $orgValue->value;\n                        }\n                    }\n                }\n\n                // skip if value haven't changed\n                if ($orgValue === $actualValue) {\n                    continue;\n                }\n\n                // if regular field\n                if (! isset($class->associationMappings[$propName])) {\n                    $changeSet[$propName] = [$orgValue, $actualValue];\n\n                    continue;\n                }\n\n                $assoc = $class->associationMappings[$propName];\n\n                // Persistent collection was exchanged with the \"originally\"\n                // created one. This can only mean it was cloned and replaced\n                // on another entity.\n                if ($actualValue instanceof PersistentCollection) {\n                    assert($assoc->isToMany());\n                    $owner = $actualValue->getOwner();\n                    if ($owner === null) { // cloned\n                        $actualValue->setOwner($entity, $assoc);\n                    } elseif ($owner !== $entity) { // no clone, we have to fix\n                        if (! $actualValue->isInitialized()) {\n                            $actualValue->initialize(); // we have to do this otherwise the cols share state\n                        }\n\n                        $newValue = clone $actualValue;\n                        $newValue->setOwner($entity, $assoc);\n                        $class->propertyAccessors[$propName]->setValue($entity, $newValue);\n                    }\n                }\n\n                if ($orgValue instanceof PersistentCollection) {\n                    // A PersistentCollection was de-referenced, so delete it.\n                    $coid = spl_object_id($orgValue);\n\n                    if (isset($this->collectionDeletions[$coid])) {\n                        continue;\n                    }\n\n                    $this->collectionDeletions[$coid] = $orgValue;\n                    $changeSet[$propName]             = $orgValue; // Signal changeset, to-many assocs will be ignored.\n\n                    continue;\n                }\n\n                if ($assoc->isToOne()) {\n                    if ($assoc->isOwningSide()) {\n                        $changeSet[$propName] = [$orgValue, $actualValue];\n                    }\n\n                    if ($orgValue !== null && $assoc->orphanRemoval) {\n                        assert(is_object($orgValue));\n                        $this->scheduleOrphanRemoval($orgValue);\n                    }\n                }\n            }\n\n            if ($changeSet) {\n                $this->entityChangeSets[$oid]   = $changeSet;\n                $this->originalEntityData[$oid] = $actualData;\n                $this->entityUpdates[$oid]      = $entity;\n            }\n        }\n\n        // Look for changes in associations of the entity\n        foreach ($class->associationMappings as $field => $assoc) {\n            $val = $class->propertyAccessors[$field]->getValue($entity);\n            if ($val === null) {\n                continue;\n            }\n\n            $this->computeAssociationChanges($assoc, $val);\n\n            if (\n                ! isset($this->entityChangeSets[$oid]) &&\n                $assoc->isManyToManyOwningSide() &&\n                $val instanceof PersistentCollection &&\n                $val->isDirty()\n            ) {\n                $this->entityChangeSets[$oid]   = [];\n                $this->originalEntityData[$oid] = $actualData;\n                $this->entityUpdates[$oid]      = $entity;\n            }\n        }\n    }\n\n    /**\n     * Computes all the changes that have been done to entities and collections\n     * since the last commit and stores these changes in the _entityChangeSet map\n     * temporarily for access by the persisters, until the UoW commit is finished.\n     */\n    public function computeChangeSets(): void\n    {\n        // Compute changes for INSERTed entities first. This must always happen.\n        $this->computeScheduleInsertsChangeSets();\n\n        // Compute changes for other MANAGED entities. Change tracking policies take effect here.\n        foreach ($this->identityMap as $className => $entities) {\n            $class = $this->em->getClassMetadata($className);\n\n            // Skip class if instances are read-only\n            if ($class->isReadOnly) {\n                continue;\n            }\n\n            $entitiesToProcess = match (true) {\n                $class->isChangeTrackingDeferredImplicit() => $entities,\n                isset($this->scheduledForSynchronization[$className]) => $this->scheduledForSynchronization[$className],\n                default => [],\n            };\n\n            foreach ($entitiesToProcess as $entity) {\n                // Ignore uninitialized proxy objects\n                if ($this->isUninitializedObject($entity)) {\n                    continue;\n                }\n\n                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.\n                $oid = spl_object_id($entity);\n\n                if (! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {\n                    $this->computeChangeSet($class, $entity);\n                }\n            }\n        }\n    }\n\n    /**\n     * Computes the changes of an association.\n     *\n     * @param mixed $value The value of the association.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws ORMException\n     */\n    private function computeAssociationChanges(AssociationMapping $assoc, mixed $value): void\n    {\n        if ($this->isUninitializedObject($value)) {\n            return;\n        }\n\n        // If this collection is dirty, schedule it for updates\n        if ($value instanceof PersistentCollection && $value->isDirty()) {\n            $coid = spl_object_id($value);\n\n            $this->collectionUpdates[$coid]  = $value;\n            $this->visitedCollections[$coid] = $value;\n        }\n\n        // Look through the entities, and in any of their associations,\n        // for transient (new) entities, recursively. (\"Persistence by reachability\")\n        // Unwrap. Uninitialized collections will simply be empty.\n        $unwrappedValue = $assoc->isToOne() ? [$value] : $value->unwrap();\n        $targetClass    = $this->em->getClassMetadata($assoc->targetEntity);\n\n        foreach ($unwrappedValue as $key => $entry) {\n            if (! ($entry instanceof $targetClass->name)) {\n                throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry);\n            }\n\n            $state = $this->getEntityState($entry, self::STATE_NEW);\n\n            if (! ($entry instanceof $assoc->targetEntity)) {\n                throw UnexpectedAssociationValue::create(\n                    $assoc->sourceEntity,\n                    $assoc->fieldName,\n                    get_debug_type($entry),\n                    $assoc->targetEntity,\n                );\n            }\n\n            switch ($state) {\n                case self::STATE_NEW:\n                    if (! $assoc->isCascadePersist()) {\n                        /*\n                         * For now just record the details, because this may\n                         * not be an issue if we later discover another pathway\n                         * through the object-graph where cascade-persistence\n                         * is enabled for this object.\n                         */\n                        $this->nonCascadedNewDetectedEntities[spl_object_id($entry)] = [$assoc, $entry];\n\n                        break;\n                    }\n\n                    $this->persistNew($targetClass, $entry);\n                    $this->computeChangeSet($targetClass, $entry);\n\n                    break;\n\n                case self::STATE_REMOVED:\n                    // Consume the $value as array (it's either an array or an ArrayAccess)\n                    // and remove the element from Collection.\n                    if (! $assoc->isToMany()) {\n                        break;\n                    }\n\n                    $coid                            = spl_object_id($value);\n                    $this->visitedCollections[$coid] = $value;\n\n                    if (! isset($this->pendingCollectionElementRemovals[$coid])) {\n                        $this->pendingCollectionElementRemovals[$coid] = [];\n                    }\n\n                    $this->pendingCollectionElementRemovals[$coid][$key] = true;\n                    break;\n\n                case self::STATE_DETACHED:\n                    // Can actually not happen right now as we assume STATE_NEW,\n                    // so the exception will be raised from the DBAL layer (constraint violation).\n                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);\n\n                default:\n                    // MANAGED associated entities are already taken into account\n                    // during changeset calculation anyway, since they are in the identity map.\n            }\n        }\n    }\n\n    /**\n     * @phpstan-param ClassMetadata<T> $class\n     * @phpstan-param T $entity\n     *\n     * @template T of object\n     */\n    private function persistNew(ClassMetadata $class, object $entity): void\n    {\n        $oid    = spl_object_id($entity);\n        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);\n\n        if ($invoke !== ListenersInvoker::INVOKE_NONE) {\n            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new PrePersistEventArgs($entity, $this->em), $invoke);\n        }\n\n        $idGen = $class->idGenerator;\n\n        if (! $idGen->isPostInsertGenerator()) {\n            $idValue = $idGen->generateId($this->em, $entity);\n\n            if (! $idGen instanceof AssignedGenerator) {\n                $idValue = [$class->getSingleIdentifierFieldName() => $this->convertSingleFieldIdentifierToPHPValue($class, $idValue)];\n\n                $class->setIdentifierValues($entity, $idValue);\n            }\n\n            // Some identifiers may be foreign keys to new entities.\n            // In this case, we don't have the value yet and should treat it as if we have a post-insert generator\n            if (! $this->hasMissingIdsWhichAreForeignKeys($class, $idValue)) {\n                $this->entityIdentifiers[$oid] = $idValue;\n            }\n        }\n\n        $this->entityStates[$oid] = self::STATE_MANAGED;\n\n        if (! isset($this->entityInsertions[$oid])) {\n            $this->scheduleForInsert($entity);\n        }\n    }\n\n    /** @param mixed[] $idValue */\n    private function hasMissingIdsWhichAreForeignKeys(ClassMetadata $class, array $idValue): bool\n    {\n        foreach ($idValue as $idField => $idFieldValue) {\n            if ($idFieldValue === null && isset($class->associationMappings[$idField])) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * INTERNAL:\n     * Computes the changeset of an individual entity, independently of the\n     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().\n     *\n     * The passed entity must be a managed entity. If the entity already has a change set\n     * because this method is invoked during a commit cycle then the change sets are added.\n     * whereby changes detected in this method prevail.\n     *\n     * @param ClassMetadata $class  The class descriptor of the entity.\n     * @param object        $entity The entity for which to (re)calculate the change set.\n     * @phpstan-param ClassMetadata<T> $class\n     * @phpstan-param T $entity\n     *\n     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.\n     *\n     * @template T of object\n     * @ignore\n     */\n    public function recomputeSingleEntityChangeSet(ClassMetadata $class, object $entity): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (! isset($this->entityStates[$oid]) || $this->entityStates[$oid] !== self::STATE_MANAGED) {\n            throw ORMInvalidArgumentException::entityNotManaged($entity);\n        }\n\n        if (! $class->isInheritanceTypeNone()) {\n            $class = $this->em->getClassMetadata($entity::class);\n        }\n\n        $actualData = [];\n\n        foreach ($class->propertyAccessors as $name => $refProp) {\n            if (\n                ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())\n                && ($name !== $class->versionField)\n                && ! $class->isCollectionValuedAssociation($name)\n            ) {\n                $actualData[$name] = $refProp->getValue($entity);\n            }\n        }\n\n        if (! isset($this->originalEntityData[$oid])) {\n            throw new RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');\n        }\n\n        $originalData = $this->originalEntityData[$oid];\n        $changeSet    = [];\n\n        foreach ($actualData as $propName => $actualValue) {\n            $orgValue = $originalData[$propName] ?? null;\n\n            if (isset($class->fieldMappings[$propName]->enumType)) {\n                if (is_array($orgValue)) {\n                    foreach ($orgValue as $id => $val) {\n                        if ($val instanceof BackedEnum) {\n                            $orgValue[$id] = $val->value;\n                        }\n                    }\n                } else {\n                    if ($orgValue instanceof BackedEnum) {\n                        $orgValue = $orgValue->value;\n                    }\n                }\n            }\n\n            if ($orgValue !== $actualValue) {\n                $changeSet[$propName] = [$orgValue, $actualValue];\n            }\n        }\n\n        if ($changeSet) {\n            if (isset($this->entityChangeSets[$oid])) {\n                $this->entityChangeSets[$oid] = [...$this->entityChangeSets[$oid], ...$changeSet];\n            } elseif (! isset($this->entityInsertions[$oid])) {\n                $this->entityChangeSets[$oid] = $changeSet;\n                $this->entityUpdates[$oid]    = $entity;\n            }\n\n            $this->originalEntityData[$oid] = $actualData;\n        }\n    }\n\n    /**\n     * Executes entity insertions\n     */\n    private function executeInserts(): void\n    {\n        $batchedByType    = InsertBatch::batchByEntityType($this->em, $this->computeInsertExecutionOrder());\n        $eventsToDispatch = [];\n\n        foreach ($batchedByType as $batch) {\n            $class     = $batch->class;\n            $invoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);\n            $persister = $this->getEntityPersister($class->name);\n\n            foreach ($batch->entities as $entity) {\n                $oid = spl_object_id($entity);\n\n                $persister->addInsert($entity);\n\n                unset($this->entityInsertions[$oid]);\n            }\n\n            $persister->executeInserts();\n\n            foreach ($batch->entities as $entity) {\n                $oid = spl_object_id($entity);\n\n                if (! isset($this->entityIdentifiers[$oid])) {\n                    //entity was not added to identity map because some identifiers are foreign keys to new entities.\n                    //add it now\n                    $this->addToEntityIdentifiersAndEntityMap($class, $oid, $entity);\n                }\n\n                if ($invoke !== ListenersInvoker::INVOKE_NONE) {\n                    $eventsToDispatch[] = ['class' => $class, 'entity' => $entity, 'invoke' => $invoke];\n                }\n            }\n        }\n\n        // Defer dispatching `postPersist` events to until all entities have been inserted and post-insert\n        // IDs have been assigned.\n        foreach ($eventsToDispatch as $event) {\n            $this->listenersInvoker->invoke(\n                $event['class'],\n                Events::postPersist,\n                $event['entity'],\n                new PostPersistEventArgs($event['entity'], $this->em),\n                $event['invoke'],\n            );\n        }\n    }\n\n    /**\n     * @phpstan-param ClassMetadata<T> $class\n     * @phpstan-param T $entity\n     *\n     * @template T of object\n     */\n    private function addToEntityIdentifiersAndEntityMap(\n        ClassMetadata $class,\n        int $oid,\n        object $entity,\n    ): void {\n        $identifier = [];\n\n        foreach ($class->getIdentifierFieldNames() as $idField) {\n            $origValue = $class->getFieldValue($entity, $idField);\n\n            $value = null;\n            if (isset($class->associationMappings[$idField])) {\n                // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.\n                $value = $this->getSingleIdentifierValue($origValue);\n            }\n\n            $identifier[$idField]                     = $value ?? $origValue;\n            $this->originalEntityData[$oid][$idField] = $origValue;\n        }\n\n        $this->entityStates[$oid]      = self::STATE_MANAGED;\n        $this->entityIdentifiers[$oid] = $identifier;\n\n        $this->addToIdentityMap($entity);\n    }\n\n    /**\n     * Executes all entity updates\n     */\n    private function executeUpdates(): void\n    {\n        foreach ($this->entityUpdates as $oid => $entity) {\n            $class            = $this->em->getClassMetadata($entity::class);\n            $persister        = $this->getEntityPersister($class->name);\n            $preUpdateInvoke  = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);\n            $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);\n\n            if ($preUpdateInvoke !== ListenersInvoker::INVOKE_NONE) {\n                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);\n\n                $this->recomputeSingleEntityChangeSet($class, $entity);\n            }\n\n            if (! empty($this->entityChangeSets[$oid])) {\n                $persister->update($entity);\n            }\n\n            unset($this->entityUpdates[$oid]);\n\n            if ($postUpdateInvoke !== ListenersInvoker::INVOKE_NONE) {\n                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new PostUpdateEventArgs($entity, $this->em), $postUpdateInvoke);\n            }\n        }\n    }\n\n    /**\n     * Executes all entity deletions\n     */\n    private function executeDeletions(): void\n    {\n        $entities         = $this->computeDeleteExecutionOrder();\n        $eventsToDispatch = [];\n\n        foreach ($entities as $entity) {\n            $this->removeFromIdentityMap($entity);\n\n            $oid       = spl_object_id($entity);\n            $class     = $this->em->getClassMetadata($entity::class);\n            $persister = $this->getEntityPersister($class->name);\n            $invoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);\n\n            $persister->delete($entity);\n\n            unset(\n                $this->entityDeletions[$oid],\n                $this->entityIdentifiers[$oid],\n                $this->originalEntityData[$oid],\n                $this->entityStates[$oid],\n            );\n\n            // Entity with this $oid after deletion treated as NEW, even if the $oid\n            // is obtained by a new entity because the old one went out of scope.\n            //$this->entityStates[$oid] = self::STATE_NEW;\n            if (! $class->isIdentifierNatural()) {\n                $class->propertyAccessors[$class->identifier[0]]->setValue($entity, null);\n            }\n\n            if ($invoke !== ListenersInvoker::INVOKE_NONE) {\n                $eventsToDispatch[] = ['class' => $class, 'entity' => $entity, 'invoke' => $invoke];\n            }\n        }\n\n        // Defer dispatching `postRemove` events to until all entities have been removed.\n        foreach ($eventsToDispatch as $event) {\n            $this->listenersInvoker->invoke(\n                $event['class'],\n                Events::postRemove,\n                $event['entity'],\n                new PostRemoveEventArgs($event['entity'], $this->em),\n                $event['invoke'],\n            );\n        }\n    }\n\n    /** @return list<object> */\n    private function computeInsertExecutionOrder(): array\n    {\n        $sort = new TopologicalSort();\n\n        // First make sure we have all the nodes\n        foreach ($this->entityInsertions as $entity) {\n            $sort->addNode($entity);\n        }\n\n        // Now add edges\n        foreach ($this->entityInsertions as $entity) {\n            $class = $this->em->getClassMetadata($entity::class);\n\n            foreach ($class->associationMappings as $assoc) {\n                // We only need to consider the owning sides of to-one associations,\n                // since many-to-many associations are persisted at a later step and\n                // have no insertion order problems (all entities already in the database\n                // at that time).\n                if (! $assoc->isToOneOwningSide()) {\n                    continue;\n                }\n\n                $targetEntity = $class->getFieldValue($entity, $assoc->fieldName);\n\n                // If there is no entity that we need to refer to, or it is already in the\n                // database (i. e. does not have to be inserted), no need to consider it.\n                if ($targetEntity === null || ! $sort->hasNode($targetEntity)) {\n                    continue;\n                }\n\n                // An entity that references back to itself _and_ uses an application-provided ID\n                // (the \"NONE\" generator strategy) can be exempted from commit order computation.\n                // See https://github.com/doctrine/orm/pull/10735/ for more details on this edge case.\n                // A non-NULLable self-reference would be a cycle in the graph.\n                if ($targetEntity === $entity && $class->isIdentifierNatural()) {\n                    continue;\n                }\n\n                // According to https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/annotations-reference.html#annref_joincolumn,\n                // the default for \"nullable\" is true. Unfortunately, it seems this default is not applied at the metadata driver, factory or other\n                // level, but in fact we may have an undefined 'nullable' key here, so we must assume that default here as well.\n                //\n                // Same in \\Doctrine\\ORM\\Tools\\EntityGenerator::isAssociationIsNullable or \\Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister::getJoinSQLForJoinColumns,\n                // to give two examples.\n                $joinColumns = reset($assoc->joinColumns);\n                $isNullable  = ! isset($joinColumns->nullable) || $joinColumns->nullable;\n\n                // Add dependency. The dependency direction implies that \"$entity depends on $targetEntity\". The\n                // topological sort result will output the depended-upon nodes first, which means we can insert\n                // entities in that order.\n                $sort->addEdge($entity, $targetEntity, $isNullable);\n            }\n        }\n\n        return $sort->sort();\n    }\n\n    /** @return list<object> */\n    private function computeDeleteExecutionOrder(): array\n    {\n        $stronglyConnectedComponents = new StronglyConnectedComponents();\n        $sort                        = new TopologicalSort();\n\n        foreach ($this->entityDeletions as $entity) {\n            $stronglyConnectedComponents->addNode($entity);\n            $sort->addNode($entity);\n        }\n\n        // First, consider only \"on delete cascade\" associations between entities\n        // and find strongly connected groups. Once we delete any one of the entities\n        // in such a group, _all_ of the other entities will be removed as well. So,\n        // we need to treat those groups like a single entity when performing delete\n        // order topological sorting.\n        foreach ($this->entityDeletions as $entity) {\n            $class = $this->em->getClassMetadata($entity::class);\n\n            foreach ($class->associationMappings as $assoc) {\n                // We only need to consider the owning sides of to-one associations,\n                // since many-to-many associations can always be (and have already been)\n                // deleted in a preceding step.\n                if (! $assoc->isToOneOwningSide()) {\n                    continue;\n                }\n\n                $joinColumns = reset($assoc->joinColumns);\n                if (! isset($joinColumns->onDelete)) {\n                    continue;\n                }\n\n                $onDeleteOption = strtolower($joinColumns->onDelete);\n                if ($onDeleteOption !== 'cascade') {\n                    continue;\n                }\n\n                $targetEntity = $class->getFieldValue($entity, $assoc->fieldName);\n\n                // If the association does not refer to another entity or that entity\n                // is not to be deleted, there is no ordering problem and we can\n                // skip this particular association.\n                if ($targetEntity === null || ! $stronglyConnectedComponents->hasNode($targetEntity)) {\n                    continue;\n                }\n\n                $stronglyConnectedComponents->addEdge($entity, $targetEntity);\n            }\n        }\n\n        $stronglyConnectedComponents->findStronglyConnectedComponents();\n\n        // Now do the actual topological sorting to find the delete order.\n        foreach ($this->entityDeletions as $entity) {\n            $class = $this->em->getClassMetadata($entity::class);\n\n            // Get the entities representing the SCC\n            $entityComponent = $stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($entity);\n\n            // When $entity is part of a non-trivial strongly connected component group\n            // (a group containing not only those entities alone), make sure we process it _after_ the\n            // entity representing the group.\n            // The dependency direction implies that \"$entity depends on $entityComponent\n            // being deleted first\". The topological sort will output the depended-upon nodes first.\n            if ($entityComponent !== $entity) {\n                $sort->addEdge($entity, $entityComponent, false);\n            }\n\n            foreach ($class->associationMappings as $assoc) {\n                // We only need to consider the owning sides of to-one associations,\n                // since many-to-many associations can always be (and have already been)\n                // deleted in a preceding step.\n                if (! $assoc->isToOneOwningSide()) {\n                    continue;\n                }\n\n                // For associations that implement a database-level set null operation,\n                // we do not have to follow a particular order: If the referred-to entity is\n                // deleted first, the DBMS will temporarily set the foreign key to NULL (SET NULL).\n                // So, we can skip it in the computation.\n                $joinColumns = reset($assoc->joinColumns);\n                if (isset($joinColumns->onDelete)) {\n                    $onDeleteOption = strtolower($joinColumns->onDelete);\n                    if ($onDeleteOption === 'set null') {\n                        continue;\n                    }\n                }\n\n                $targetEntity = $class->getFieldValue($entity, $assoc->fieldName);\n\n                // If the association does not refer to another entity or that entity\n                // is not to be deleted, there is no ordering problem and we can\n                // skip this particular association.\n                if ($targetEntity === null || ! $sort->hasNode($targetEntity)) {\n                    continue;\n                }\n\n                // Get the entities representing the SCC\n                $targetEntityComponent = $stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($targetEntity);\n\n                // When we have a dependency between two different groups of strongly connected nodes,\n                // add it to the computation.\n                // The dependency direction implies that \"$targetEntityComponent depends on $entityComponent\n                // being deleted first\". The topological sort will output the depended-upon nodes first,\n                // so we can work through the result in the returned order.\n                if ($targetEntityComponent !== $entityComponent) {\n                    $sort->addEdge($targetEntityComponent, $entityComponent, false);\n                }\n            }\n        }\n\n        return $sort->sort();\n    }\n\n    /**\n     * Schedules an entity for insertion into the database.\n     * If the entity already has an identifier, it will be added to the identity map.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws InvalidArgumentException\n     */\n    public function scheduleForInsert(object $entity): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($this->entityUpdates[$oid])) {\n            throw new InvalidArgumentException('Dirty entity can not be scheduled for insertion.');\n        }\n\n        if (isset($this->entityDeletions[$oid])) {\n            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);\n        }\n\n        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {\n            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);\n        }\n\n        if (isset($this->entityInsertions[$oid])) {\n            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);\n        }\n\n        $this->entityInsertions[$oid] = $entity;\n\n        if (isset($this->entityIdentifiers[$oid])) {\n            $this->addToIdentityMap($entity);\n        }\n    }\n\n    /**\n     * Checks whether an entity is scheduled for insertion.\n     */\n    public function isScheduledForInsert(object $entity): bool\n    {\n        return isset($this->entityInsertions[spl_object_id($entity)]);\n    }\n\n    /**\n     * Schedules an entity for being updated.\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function scheduleForUpdate(object $entity): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (! isset($this->entityIdentifiers[$oid])) {\n            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, 'scheduling for update');\n        }\n\n        if (isset($this->entityDeletions[$oid])) {\n            throw ORMInvalidArgumentException::entityIsRemoved($entity, 'schedule for update');\n        }\n\n        if (! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {\n            $this->entityUpdates[$oid] = $entity;\n        }\n    }\n\n    /**\n     * INTERNAL:\n     * Schedules an extra update that will be executed immediately after the\n     * regular entity updates within the currently running commit cycle.\n     *\n     * Extra updates for entities are stored as (entity, changeset) tuples.\n     *\n     * @phpstan-param array<string, array{mixed, mixed}>  $changeset The changeset of the entity (what to update).\n     *\n     * @ignore\n     */\n    public function scheduleExtraUpdate(object $entity, array $changeset): void\n    {\n        $oid         = spl_object_id($entity);\n        $extraUpdate = [$entity, $changeset];\n\n        if (isset($this->extraUpdates[$oid])) {\n            [, $changeset2] = $this->extraUpdates[$oid];\n\n            $extraUpdate = [$entity, $changeset + $changeset2];\n        }\n\n        $this->extraUpdates[$oid] = $extraUpdate;\n    }\n\n    /**\n     * Checks whether an entity is registered as dirty in the unit of work.\n     * Note: Is not very useful currently as dirty entities are only registered\n     * at commit time.\n     */\n    public function isScheduledForUpdate(object $entity): bool\n    {\n        return isset($this->entityUpdates[spl_object_id($entity)]);\n    }\n\n    /**\n     * Checks whether an entity is registered to be checked in the unit of work.\n     */\n    public function isScheduledForDirtyCheck(object $entity): bool\n    {\n        $rootEntityName = $this->em->getClassMetadata($entity::class)->rootEntityName;\n\n        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_id($entity)]);\n    }\n\n    /**\n     * INTERNAL:\n     * Schedules an entity for deletion.\n     */\n    public function scheduleForDelete(object $entity): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($this->entityInsertions[$oid])) {\n            if ($this->isInIdentityMap($entity)) {\n                $this->removeFromIdentityMap($entity);\n            }\n\n            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);\n\n            return; // entity has not been persisted yet, so nothing more to do.\n        }\n\n        if (! $this->isInIdentityMap($entity)) {\n            return;\n        }\n\n        unset($this->entityUpdates[$oid]);\n\n        if (! isset($this->entityDeletions[$oid])) {\n            $this->entityDeletions[$oid] = $entity;\n            $this->entityStates[$oid]    = self::STATE_REMOVED;\n        }\n    }\n\n    /**\n     * Checks whether an entity is registered as removed/deleted with the unit\n     * of work.\n     */\n    public function isScheduledForDelete(object $entity): bool\n    {\n        return isset($this->entityDeletions[spl_object_id($entity)]);\n    }\n\n    /**\n     * Checks whether an entity is scheduled for insertion, update or deletion.\n     */\n    public function isEntityScheduled(object $entity): bool\n    {\n        $oid = spl_object_id($entity);\n\n        return isset($this->entityInsertions[$oid])\n            || isset($this->entityUpdates[$oid])\n            || isset($this->entityDeletions[$oid]);\n    }\n\n    /**\n     * INTERNAL:\n     * Registers an entity in the identity map.\n     * Note that entities in a hierarchy are registered with the class name of\n     * the root entity.\n     *\n     * @return bool TRUE if the registration was successful, FALSE if the identity of\n     * the entity in question is already managed.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws EntityIdentityCollisionException\n     *\n     * @ignore\n     */\n    public function addToIdentityMap(object $entity): bool\n    {\n        $classMetadata = $this->em->getClassMetadata($entity::class);\n        $idHash        = $this->getIdHashByEntity($entity);\n        $className     = $classMetadata->rootEntityName;\n\n        if (isset($this->identityMap[$className][$idHash])) {\n            if ($this->identityMap[$className][$idHash] !== $entity) {\n                throw EntityIdentityCollisionException::create($this->identityMap[$className][$idHash], $entity, $idHash);\n            }\n\n            return false;\n        }\n\n        $this->identityMap[$className][$idHash] = $entity;\n\n        return true;\n    }\n\n    /**\n     * Gets the id hash of an entity by its identifier.\n     *\n     * @param array<string|int, mixed> $identifier The identifier of an entity\n     *\n     * @return string The entity id hash.\n     */\n    final public static function getIdHashByIdentifier(array $identifier): string\n    {\n        foreach ($identifier as $k => $value) {\n            if ($value instanceof BackedEnum) {\n                $identifier[$k] = $value->value;\n            }\n        }\n\n        return implode(\n            ' ',\n            $identifier,\n        );\n    }\n\n    /**\n     * Gets the id hash of an entity.\n     *\n     * @param object $entity The entity managed by Unit Of Work\n     *\n     * @return string The entity id hash.\n     */\n    public function getIdHashByEntity(object $entity): string\n    {\n        $identifier = $this->entityIdentifiers[spl_object_id($entity)];\n\n        if (empty($identifier) || in_array(null, $identifier, true)) {\n            $classMetadata = $this->em->getClassMetadata($entity::class);\n\n            throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);\n        }\n\n        return self::getIdHashByIdentifier($identifier);\n    }\n\n    /**\n     * Gets the state of an entity with regard to the current unit of work.\n     *\n     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).\n     *                         This parameter can be set to improve performance of entity state detection\n     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED\n     *                         is either known or does not matter for the caller of the method.\n     * @phpstan-param self::STATE_*|null $assume\n     *\n     * @phpstan-return self::STATE_*\n     */\n    public function getEntityState(object $entity, int|null $assume = null): int\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($this->entityStates[$oid])) {\n            return $this->entityStates[$oid];\n        }\n\n        if ($assume !== null) {\n            return $assume;\n        }\n\n        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.\n        // Note that you can not remember the NEW or DETACHED state in _entityStates since\n        // the UoW does not hold references to such objects and the object hash can be reused.\n        // More generally because the state may \"change\" between NEW/DETACHED without the UoW being aware of it.\n        $class = $this->em->getClassMetadata($entity::class);\n        $id    = $class->getIdentifierValues($entity);\n\n        if (! $id) {\n            return self::STATE_NEW;\n        }\n\n        if ($class->containsForeignIdentifier || $class->containsEnumIdentifier) {\n            $id = $this->identifierFlattener->flattenIdentifier($class, $id);\n        }\n\n        switch (true) {\n            case $class->isIdentifierNatural():\n                // Check for a version field, if available, to avoid a db lookup.\n                if ($class->isVersioned) {\n                    assert($class->versionField !== null);\n\n                    return $class->getFieldValue($entity, $class->versionField)\n                        ? self::STATE_DETACHED\n                        : self::STATE_NEW;\n                }\n\n                // Last try before db lookup: check the identity map.\n                if ($this->tryGetById($id, $class->rootEntityName)) {\n                    return self::STATE_DETACHED;\n                }\n\n                // db lookup\n                if ($this->getEntityPersister($class->name)->exists($entity)) {\n                    return self::STATE_DETACHED;\n                }\n\n                return self::STATE_NEW;\n\n            case ! $class->idGenerator->isPostInsertGenerator():\n                // if we have a pre insert generator we can't be sure that having an id\n                // really means that the entity exists. We have to verify this through\n                // the last resort: a db lookup\n\n                // Last try before db lookup: check the identity map.\n                if ($this->tryGetById($id, $class->rootEntityName)) {\n                    return self::STATE_DETACHED;\n                }\n\n                // db lookup\n                if ($this->getEntityPersister($class->name)->exists($entity)) {\n                    return self::STATE_DETACHED;\n                }\n\n                return self::STATE_NEW;\n\n            default:\n                return self::STATE_DETACHED;\n        }\n    }\n\n    /**\n     * INTERNAL:\n     * Removes an entity from the identity map. This effectively detaches the\n     * entity from the persistence management of Doctrine.\n     *\n     * @throws ORMInvalidArgumentException\n     *\n     * @ignore\n     */\n    public function removeFromIdentityMap(object $entity): bool\n    {\n        $oid           = spl_object_id($entity);\n        $classMetadata = $this->em->getClassMetadata($entity::class);\n        $idHash        = self::getIdHashByIdentifier($this->entityIdentifiers[$oid]);\n\n        if ($idHash === '') {\n            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, 'remove from identity map');\n        }\n\n        $className = $classMetadata->rootEntityName;\n\n        if (isset($this->identityMap[$className][$idHash])) {\n            unset($this->identityMap[$className][$idHash], $this->readOnlyObjects[$oid]);\n\n            //$this->entityStates[$oid] = self::STATE_DETACHED;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * INTERNAL:\n     * Gets an entity in the identity map by its identifier hash.\n     *\n     * @ignore\n     */\n    public function getByIdHash(string $idHash, string $rootClassName): object|null\n    {\n        return $this->identityMap[$rootClassName][$idHash];\n    }\n\n    /**\n     * INTERNAL:\n     * Tries to get an entity by its identifier hash. If no entity is found for\n     * the given hash, FALSE is returned.\n     *\n     * @param mixed $idHash (must be possible to cast it to string)\n     *\n     * @return false|object The found entity or FALSE.\n     *\n     * @ignore\n     */\n    public function tryGetByIdHash(mixed $idHash, string $rootClassName): object|false\n    {\n        $stringIdHash = (string) $idHash;\n\n        return $this->identityMap[$rootClassName][$stringIdHash] ?? false;\n    }\n\n    /**\n     * Checks whether an entity is registered in the identity map of this UnitOfWork.\n     */\n    public function isInIdentityMap(object $entity): bool\n    {\n        $oid = spl_object_id($entity);\n\n        if (empty($this->entityIdentifiers[$oid])) {\n            return false;\n        }\n\n        $classMetadata = $this->em->getClassMetadata($entity::class);\n        $idHash        = self::getIdHashByIdentifier($this->entityIdentifiers[$oid]);\n\n        return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]);\n    }\n\n    /**\n     * Persists an entity as part of the current unit of work.\n     */\n    public function persist(object $entity): void\n    {\n        $visited = [];\n\n        $this->doPersist($entity, $visited);\n    }\n\n    /**\n     * Persists an entity as part of the current unit of work.\n     *\n     * This method is internally called during persist() cascades as it tracks\n     * the already visited entities to prevent infinite recursions.\n     *\n     * @phpstan-param array<int, object> $visited The already visited entities.\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws UnexpectedValueException\n     */\n    private function doPersist(object $entity, array &$visited): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($visited[$oid])) {\n            return; // Prevent infinite recursion\n        }\n\n        $visited[$oid] = $entity; // Mark visited\n\n        $class = $this->em->getClassMetadata($entity::class);\n\n        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).\n        // If we would detect DETACHED here we would throw an exception anyway with the same\n        // consequences (not recoverable/programming error), so just assuming NEW here\n        // lets us avoid some database lookups for entities with natural identifiers.\n        $entityState = $this->getEntityState($entity, self::STATE_NEW);\n\n        switch ($entityState) {\n            case self::STATE_MANAGED:\n                // Nothing to do, except if policy is \"deferred explicit\"\n                if ($class->isChangeTrackingDeferredExplicit()) {\n                    $this->scheduleForDirtyCheck($entity);\n                }\n\n                break;\n\n            case self::STATE_NEW:\n                $this->persistNew($class, $entity);\n                break;\n\n            case self::STATE_REMOVED:\n                // Entity becomes managed again\n                unset($this->entityDeletions[$oid]);\n                $this->addToIdentityMap($entity);\n\n                $this->entityStates[$oid] = self::STATE_MANAGED;\n\n                if ($class->isChangeTrackingDeferredExplicit()) {\n                    $this->scheduleForDirtyCheck($entity);\n                }\n\n                break;\n\n            case self::STATE_DETACHED:\n                // Can actually not happen right now since we assume STATE_NEW.\n                throw ORMInvalidArgumentException::detachedEntityCannot($entity, 'persisted');\n\n            default:\n                throw new UnexpectedValueException(sprintf(\n                    'Unexpected entity state: %s. %s',\n                    $entityState,\n                    self::objToStr($entity),\n                ));\n        }\n\n        $this->cascadePersist($entity, $visited);\n    }\n\n    /**\n     * Deletes an entity as part of the current unit of work.\n     */\n    public function remove(object $entity): void\n    {\n        $visited = [];\n\n        $this->doRemove($entity, $visited);\n    }\n\n    /**\n     * Deletes an entity as part of the current unit of work.\n     *\n     * This method is internally called during delete() cascades as it tracks\n     * the already visited entities to prevent infinite recursions.\n     *\n     * @phpstan-param array<int, object> $visited The map of the already visited entities.\n     *\n     * @throws ORMInvalidArgumentException If the instance is a detached entity.\n     * @throws UnexpectedValueException\n     */\n    private function doRemove(object $entity, array &$visited): void\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($visited[$oid])) {\n            return; // Prevent infinite recursion\n        }\n\n        $visited[$oid] = $entity; // mark visited\n\n        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which\n        // can cause problems when a lazy proxy has to be initialized for the cascade operation.\n        $this->cascadeRemove($entity, $visited);\n\n        $class       = $this->em->getClassMetadata($entity::class);\n        $entityState = $this->getEntityState($entity);\n\n        switch ($entityState) {\n            case self::STATE_NEW:\n            case self::STATE_REMOVED:\n                // nothing to do\n                break;\n\n            case self::STATE_MANAGED:\n                $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove);\n\n                if ($invoke !== ListenersInvoker::INVOKE_NONE) {\n                    $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new PreRemoveEventArgs($entity, $this->em), $invoke);\n                }\n\n                $this->scheduleForDelete($entity);\n                break;\n\n            case self::STATE_DETACHED:\n                throw ORMInvalidArgumentException::detachedEntityCannot($entity, 'removed');\n\n            default:\n                throw new UnexpectedValueException(sprintf(\n                    'Unexpected entity state: %s. %s',\n                    $entityState,\n                    self::objToStr($entity),\n                ));\n        }\n    }\n\n    /**\n     * Detaches an entity from the persistence management. It's persistence will\n     * no longer be managed by Doctrine.\n     */\n    public function detach(object $entity): void\n    {\n        $visited = [];\n\n        $this->doDetach($entity, $visited);\n    }\n\n    /**\n     * Executes a detach operation on the given entity.\n     *\n     * @param mixed[] $visited\n     * @param bool    $noCascade if true, don't cascade detach operation.\n     */\n    private function doDetach(\n        object $entity,\n        array &$visited,\n        bool $noCascade = false,\n    ): void {\n        $oid = spl_object_id($entity);\n\n        if (isset($visited[$oid])) {\n            return; // Prevent infinite recursion\n        }\n\n        $visited[$oid] = $entity; // mark visited\n\n        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {\n            case self::STATE_MANAGED:\n                if ($this->isInIdentityMap($entity)) {\n                    $this->removeFromIdentityMap($entity);\n                }\n\n                unset(\n                    $this->entityInsertions[$oid],\n                    $this->entityUpdates[$oid],\n                    $this->entityDeletions[$oid],\n                    $this->entityIdentifiers[$oid],\n                    $this->entityStates[$oid],\n                    $this->originalEntityData[$oid],\n                );\n                break;\n            case self::STATE_NEW:\n            case self::STATE_DETACHED:\n                return;\n        }\n\n        if (! $noCascade) {\n            $this->cascadeDetach($entity, $visited);\n        }\n    }\n\n    /**\n     * Refreshes the state of the given entity from the database, overwriting\n     * any local, unpersisted changes.\n     *\n     * @phpstan-param LockMode::*|null $lockMode\n     *\n     * @throws InvalidArgumentException If the entity is not MANAGED.\n     * @throws TransactionRequiredException\n     */\n    public function refresh(object $entity, LockMode|int|null $lockMode = null): void\n    {\n        $visited = [];\n\n        $this->doRefresh($entity, $visited, $lockMode);\n    }\n\n    /**\n     * Executes a refresh operation on an entity.\n     *\n     * @phpstan-param array<int, object>  $visited The already visited entities during cascades.\n     * @phpstan-param LockMode::*|null $lockMode\n     *\n     * @throws ORMInvalidArgumentException If the entity is not MANAGED.\n     * @throws TransactionRequiredException\n     */\n    private function doRefresh(object $entity, array &$visited, LockMode|int|null $lockMode = null): void\n    {\n        switch (true) {\n            case $lockMode === LockMode::PESSIMISTIC_READ:\n            case $lockMode === LockMode::PESSIMISTIC_WRITE:\n                if (! $this->em->getConnection()->isTransactionActive()) {\n                    throw TransactionRequiredException::transactionRequired();\n                }\n        }\n\n        $oid = spl_object_id($entity);\n\n        if (isset($visited[$oid])) {\n            return; // Prevent infinite recursion\n        }\n\n        $visited[$oid] = $entity; // mark visited\n\n        $class = $this->em->getClassMetadata($entity::class);\n\n        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {\n            throw ORMInvalidArgumentException::entityNotManaged($entity);\n        }\n\n        $this->cascadeRefresh($entity, $visited, $lockMode);\n\n        $this->getEntityPersister($class->name)->refresh(\n            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),\n            $entity,\n            $lockMode,\n        );\n    }\n\n    /**\n     * Cascades a refresh operation to associated entities.\n     *\n     * @phpstan-param array<int, object> $visited\n     * @phpstan-param LockMode::*|null $lockMode\n     */\n    private function cascadeRefresh(object $entity, array &$visited, LockMode|int|null $lockMode = null): void\n    {\n        $class = $this->em->getClassMetadata($entity::class);\n\n        $associationMappings = array_filter(\n            $class->associationMappings,\n            static fn (AssociationMapping $assoc): bool => $assoc->isCascadeRefresh(),\n        );\n\n        foreach ($associationMappings as $assoc) {\n            $relatedEntities = $class->propertyAccessors[$assoc->fieldName]->getValue($entity);\n\n            switch (true) {\n                case $relatedEntities instanceof PersistentCollection:\n                    // Unwrap so that foreach() does not initialize\n                    $relatedEntities = $relatedEntities->unwrap();\n                    // break; is commented intentionally!\n\n                case $relatedEntities instanceof Collection:\n                case is_array($relatedEntities):\n                    foreach ($relatedEntities as $relatedEntity) {\n                        $this->doRefresh($relatedEntity, $visited, $lockMode);\n                    }\n\n                    break;\n\n                case $relatedEntities !== null:\n                    $this->doRefresh($relatedEntities, $visited, $lockMode);\n                    break;\n\n                default:\n                    // Do nothing\n            }\n        }\n    }\n\n    /**\n     * Cascades a detach operation to associated entities.\n     *\n     * @param array<int, object> $visited\n     */\n    private function cascadeDetach(object $entity, array &$visited): void\n    {\n        $class = $this->em->getClassMetadata($entity::class);\n\n        $associationMappings = array_filter(\n            $class->associationMappings,\n            static fn (AssociationMapping $assoc): bool => $assoc->isCascadeDetach(),\n        );\n\n        foreach ($associationMappings as $assoc) {\n            $relatedEntities = $class->propertyAccessors[$assoc->fieldName]->getValue($entity);\n\n            switch (true) {\n                case $relatedEntities instanceof PersistentCollection:\n                    // Unwrap so that foreach() does not initialize\n                    $relatedEntities = $relatedEntities->unwrap();\n                    // break; is commented intentionally!\n\n                case $relatedEntities instanceof Collection:\n                case is_array($relatedEntities):\n                    foreach ($relatedEntities as $relatedEntity) {\n                        $this->doDetach($relatedEntity, $visited);\n                    }\n\n                    break;\n\n                case $relatedEntities !== null:\n                    $this->doDetach($relatedEntities, $visited);\n                    break;\n\n                default:\n                    // Do nothing\n            }\n        }\n    }\n\n    /**\n     * Cascades the save operation to associated entities.\n     *\n     * @phpstan-param array<int, object> $visited\n     */\n    private function cascadePersist(object $entity, array &$visited): void\n    {\n        if ($this->isUninitializedObject($entity)) {\n            // nothing to do - proxy is not initialized, therefore we don't do anything with it\n            return;\n        }\n\n        $class = $this->em->getClassMetadata($entity::class);\n\n        $associationMappings = array_filter(\n            $class->associationMappings,\n            static fn (AssociationMapping $assoc): bool => $assoc->isCascadePersist(),\n        );\n\n        foreach ($associationMappings as $assoc) {\n            $relatedEntities = $class->propertyAccessors[$assoc->fieldName]->getValue($entity);\n\n            switch (true) {\n                case $relatedEntities instanceof PersistentCollection:\n                    // Unwrap so that foreach() does not initialize\n                    $relatedEntities = $relatedEntities->unwrap();\n                    // break; is commented intentionally!\n\n                case $relatedEntities instanceof Collection:\n                case is_array($relatedEntities):\n                    if ($assoc->isToMany() <= 0) {\n                        throw ORMInvalidArgumentException::invalidAssociation(\n                            $this->em->getClassMetadata($assoc->targetEntity),\n                            $assoc,\n                            $relatedEntities,\n                        );\n                    }\n\n                    foreach ($relatedEntities as $relatedEntity) {\n                        $this->doPersist($relatedEntity, $visited);\n                    }\n\n                    break;\n\n                case $relatedEntities !== null:\n                    if (! $relatedEntities instanceof $assoc->targetEntity) {\n                        throw ORMInvalidArgumentException::invalidAssociation(\n                            $this->em->getClassMetadata($assoc->targetEntity),\n                            $assoc,\n                            $relatedEntities,\n                        );\n                    }\n\n                    $this->doPersist($relatedEntities, $visited);\n                    break;\n\n                default:\n                    // Do nothing\n            }\n        }\n    }\n\n    /**\n     * Cascades the delete operation to associated entities.\n     *\n     * @phpstan-param array<int, object> $visited\n     */\n    private function cascadeRemove(object $entity, array &$visited): void\n    {\n        $class = $this->em->getClassMetadata($entity::class);\n\n        $associationMappings = array_filter(\n            $class->associationMappings,\n            static fn (AssociationMapping $assoc): bool => $assoc->isCascadeRemove(),\n        );\n\n        if ($associationMappings) {\n            $this->initializeObject($entity);\n        }\n\n        $entitiesToCascade = [];\n\n        foreach ($associationMappings as $assoc) {\n            $relatedEntities = $class->propertyAccessors[$assoc->fieldName]->getValue($entity);\n\n            switch (true) {\n                case $relatedEntities instanceof Collection:\n                case is_array($relatedEntities):\n                    // If its a PersistentCollection initialization is intended! No unwrap!\n                    foreach ($relatedEntities as $relatedEntity) {\n                        $entitiesToCascade[] = $relatedEntity;\n                    }\n\n                    break;\n\n                case $relatedEntities !== null:\n                    $entitiesToCascade[] = $relatedEntities;\n                    break;\n\n                default:\n                    // Do nothing\n            }\n        }\n\n        foreach ($entitiesToCascade as $relatedEntity) {\n            $this->doRemove($relatedEntity, $visited);\n        }\n    }\n\n    /**\n     * Acquire a lock on the given entity.\n     *\n     * @phpstan-param LockMode::* $lockMode\n     *\n     * @throws ORMInvalidArgumentException\n     * @throws TransactionRequiredException\n     * @throws OptimisticLockException\n     */\n    public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void\n    {\n        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {\n            throw ORMInvalidArgumentException::entityNotManaged($entity);\n        }\n\n        $class = $this->em->getClassMetadata($entity::class);\n\n        switch (true) {\n            case $lockMode === LockMode::OPTIMISTIC:\n                if (! $class->isVersioned) {\n                    throw OptimisticLockException::notVersioned($class->name);\n                }\n\n                if ($lockVersion === null) {\n                    return;\n                }\n\n                $this->initializeObject($entity);\n\n                assert($class->versionField !== null);\n                $entityVersion = $class->propertyAccessors[$class->versionField]->getValue($entity);\n\n                // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator\n                if ($entityVersion != $lockVersion) {\n                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);\n                }\n\n                break;\n\n            case $lockMode === LockMode::NONE:\n            case $lockMode === LockMode::PESSIMISTIC_READ:\n            case $lockMode === LockMode::PESSIMISTIC_WRITE:\n                if (! $this->em->getConnection()->isTransactionActive()) {\n                    throw TransactionRequiredException::transactionRequired();\n                }\n\n                $oid = spl_object_id($entity);\n\n                $this->getEntityPersister($class->name)->lock(\n                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),\n                    $lockMode,\n                );\n                break;\n\n            default:\n                // Do nothing\n        }\n    }\n\n    /**\n     * Clears the UnitOfWork.\n     */\n    public function clear(): void\n    {\n        $this->identityMap                      =\n        $this->entityIdentifiers                =\n        $this->originalEntityData               =\n        $this->entityChangeSets                 =\n        $this->entityStates                     =\n        $this->scheduledForSynchronization      =\n        $this->entityInsertions                 =\n        $this->entityUpdates                    =\n        $this->entityDeletions                  =\n        $this->nonCascadedNewDetectedEntities   =\n        $this->collectionDeletions              =\n        $this->collectionUpdates                =\n        $this->extraUpdates                     =\n        $this->readOnlyObjects                  =\n        $this->pendingCollectionElementRemovals =\n        $this->visitedCollections               =\n        $this->eagerLoadingEntities             =\n        $this->eagerLoadingCollections          =\n        $this->orphanRemovals                   = [];\n\n        if ($this->evm->hasListeners(Events::onClear)) {\n            $this->evm->dispatchEvent(Events::onClear, new OnClearEventArgs($this->em));\n        }\n    }\n\n    /**\n     * INTERNAL:\n     * Schedules an orphaned entity for removal. The remove() operation will be\n     * invoked on that entity at the beginning of the next commit of this\n     * UnitOfWork.\n     *\n     * @ignore\n     */\n    public function scheduleOrphanRemoval(object $entity): void\n    {\n        $this->orphanRemovals[spl_object_id($entity)] = $entity;\n    }\n\n    /**\n     * INTERNAL:\n     * Cancels a previously scheduled orphan removal.\n     *\n     * @ignore\n     */\n    public function cancelOrphanRemoval(object $entity): void\n    {\n        unset($this->orphanRemovals[spl_object_id($entity)]);\n    }\n\n    /**\n     * INTERNAL:\n     * Schedules a complete collection for removal when this UnitOfWork commits.\n     */\n    public function scheduleCollectionDeletion(PersistentCollection $coll): void\n    {\n        $coid = spl_object_id($coll);\n\n        // TODO: if $coll is already scheduled for recreation ... what to do?\n        // Just remove $coll from the scheduled recreations?\n        unset($this->collectionUpdates[$coid]);\n\n        $this->collectionDeletions[$coid] = $coll;\n    }\n\n    public function isCollectionScheduledForDeletion(PersistentCollection $coll): bool\n    {\n        return isset($this->collectionDeletions[spl_object_id($coll)]);\n    }\n\n    /**\n     * INTERNAL:\n     * Creates an entity. Used for reconstitution of persistent entities.\n     *\n     * Internal note: Highly performance-sensitive method.\n     *\n     * @param class-string         $className The name of the entity class.\n     * @param mixed[]              $data      The data for the entity.\n     * @param array<string, mixed> $hints     Any hints to account for during reconstitution/lookup of the entity.\n     *\n     * @return object The managed entity instance.\n     *\n     * @ignore\n     * @todo Rename: getOrCreateEntity\n     */\n    public function createEntity(string $className, array $data, array &$hints = []): object\n    {\n        $class = $this->em->getClassMetadata($className);\n\n        $id     = $this->identifierFlattener->flattenIdentifier($class, $data);\n        $idHash = self::getIdHashByIdentifier($id);\n\n        if (isset($this->identityMap[$class->rootEntityName][$idHash])) {\n            $entity = $this->identityMap[$class->rootEntityName][$idHash];\n            $oid    = spl_object_id($entity);\n\n            if (\n                isset($hints[Query::HINT_REFRESH], $hints[Query::HINT_REFRESH_ENTITY])\n            ) {\n                $unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY];\n                if (\n                    $unmanagedProxy !== $entity\n                    && $this->isIdentifierEquals($unmanagedProxy, $entity)\n                ) {\n                    // We will hydrate the given un-managed proxy anyway:\n                    // continue work, but consider it the entity from now on\n                    $entity = $unmanagedProxy;\n                }\n            }\n\n            if ($this->isUninitializedObject($entity)) {\n                if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n                    $class->reflClass->markLazyObjectAsInitialized($entity);\n                } else {\n                    $entity->__setInitialized(true);\n\n                    Hydrator::hydrate($entity, (array) $class->reflClass->newInstanceWithoutConstructor());\n                }\n            } else {\n                if (\n                    ! isset($hints[Query::HINT_REFRESH])\n                    || (isset($hints[Query::HINT_REFRESH_ENTITY]) && $hints[Query::HINT_REFRESH_ENTITY] !== $entity)\n                ) {\n                    return $entity;\n                }\n            }\n\n            $this->originalEntityData[$oid] = $data;\n        } else {\n            $entity = $class->newInstance();\n            $oid    = spl_object_id($entity);\n            $this->registerManaged($entity, $id, $data);\n\n            if (isset($hints[Query::HINT_READ_ONLY]) && $hints[Query::HINT_READ_ONLY] === true) {\n                $this->readOnlyObjects[$oid] = true;\n            }\n        }\n\n        foreach ($data as $field => $value) {\n            if (isset($class->fieldMappings[$field])) {\n                $class->propertyAccessors[$field]->setValue($entity, $value);\n            }\n        }\n\n        // Loading the entity right here, if its in the eager loading map get rid of it there.\n        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);\n\n        if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) {\n            unset($this->eagerLoadingEntities[$class->rootEntityName]);\n        }\n\n        foreach ($class->associationMappings as $field => $assoc) {\n            // Check if the association is not among the fetch-joined associations already.\n            if (isset($hints['fetchAlias'], $hints['fetched'][$hints['fetchAlias']][$field])) {\n                continue;\n            }\n\n            if (! isset($hints['fetchMode'][$class->name][$field])) {\n                $hints['fetchMode'][$class->name][$field] = $assoc->fetch;\n            }\n\n            $targetClass = $this->em->getClassMetadata($assoc->targetEntity);\n\n            switch (true) {\n                case $assoc->isToOne():\n                    if (! $assoc->isOwningSide()) {\n                        // use the given entity association\n                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_id($data[$field])])) {\n                            $this->originalEntityData[$oid][$field] = $data[$field];\n\n                            $class->propertyAccessors[$field]->setValue($entity, $data[$field]);\n                            $targetClass->propertyAccessors[$assoc->mappedBy]->setValue($data[$field], $entity);\n\n                            continue 2;\n                        }\n\n                        // Inverse side of x-to-one can never be lazy\n                        $class->propertyAccessors[$field]->setValue($entity, $this->getEntityPersister($assoc->targetEntity)->loadOneToOneEntity($assoc, $entity));\n\n                        continue 2;\n                    }\n\n                    // use the entity association\n                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_id($data[$field])])) {\n                        $class->propertyAccessors[$field]->setValue($entity, $data[$field]);\n                        $this->originalEntityData[$oid][$field] = $data[$field];\n\n                        break;\n                    }\n\n                    $associatedId = [];\n\n                    assert($assoc->isToOneOwningSide());\n                    // TODO: Is this even computed right in all cases of composite keys?\n                    foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) {\n                        $joinColumnValue = $data[$srcColumn] ?? null;\n\n                        if ($joinColumnValue !== null) {\n                            if ($joinColumnValue instanceof BackedEnum) {\n                                $joinColumnValue = $joinColumnValue->value;\n                            }\n\n                            if ($targetClass->containsForeignIdentifier) {\n                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;\n                            } else {\n                                $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;\n                            }\n                        } elseif (in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)) {\n                            // the missing key is part of target's entity primary key\n                            $associatedId = [];\n                            break;\n                        }\n                    }\n\n                    if (! $associatedId) {\n                        // Foreign key is NULL\n                        $class->propertyAccessors[$field]->setValue($entity, null);\n                        $this->originalEntityData[$oid][$field] = null;\n\n                        break;\n                    }\n\n                    // Foreign key is set\n                    // Check identity map first\n                    // FIXME: Can break easily with composite keys if join column values are in\n                    //        wrong order. The correct order is the one in ClassMetadata#identifier.\n                    $relatedIdHash = self::getIdHashByIdentifier($associatedId);\n\n                    switch (true) {\n                        case isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash]):\n                            $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];\n\n                            // If this is an uninitialized proxy, we are deferring eager loads,\n                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)\n                            // then we can append this entity for eager loading!\n                            if (\n                                $hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER &&\n                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&\n                                ! $targetClass->isIdentifierComposite &&\n                                $this->isUninitializedObject($newValue)\n                            ) {\n                                $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);\n                            }\n\n                            break;\n\n                        case $targetClass->subClasses:\n                            // If it might be a subtype, it can not be lazy. There isn't even\n                            // a way to solve this with deferred eager loading, which means putting\n                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)\n                            $newValue = $this->getEntityPersister($assoc->targetEntity)->loadOneToOneEntity($assoc, $entity, $associatedId);\n                            break;\n\n                        default:\n                            $normalizedAssociatedId = $this->normalizeIdentifier($targetClass, $associatedId);\n\n                            switch (true) {\n                                // We are negating the condition here. Other cases will assume it is valid!\n                                case $hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER:\n                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc->targetEntity, $normalizedAssociatedId);\n                                    $this->registerManaged($newValue, $associatedId, []);\n                                    break;\n\n                                // Deferred eager load only works for single identifier classes\n                                case isset($hints[self::HINT_DEFEREAGERLOAD]) &&\n                                    $hints[self::HINT_DEFEREAGERLOAD] &&\n                                    ! $targetClass->isIdentifierComposite:\n                                    // TODO: Is there a faster approach?\n                                    $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($normalizedAssociatedId);\n\n                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc->targetEntity, $normalizedAssociatedId);\n                                    $this->registerManaged($newValue, $associatedId, []);\n                                    break;\n\n                                default:\n                                    // TODO: This is very imperformant, ignore it?\n                                    $newValue = $this->em->find($assoc->targetEntity, $normalizedAssociatedId);\n                                    break;\n                            }\n                    }\n\n                    $this->originalEntityData[$oid][$field] = $newValue;\n                    $class->propertyAccessors[$field]->setValue($entity, $newValue);\n\n                    if ($assoc->inversedBy !== null && $assoc->isOneToOne() && $newValue !== null) {\n                        $inverseAssoc = $targetClass->associationMappings[$assoc->inversedBy];\n                        $targetClass->propertyAccessors[$inverseAssoc->fieldName]->setValue($newValue, $entity);\n                    }\n\n                    break;\n\n                default:\n                    assert($assoc->isToMany());\n                    // Ignore if its a cached collection\n                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {\n                        break;\n                    }\n\n                    // use the given collection\n                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {\n                        $data[$field]->setOwner($entity, $assoc);\n\n                        $class->propertyAccessors[$field]->setValue($entity, $data[$field]);\n                        $this->originalEntityData[$oid][$field] = $data[$field];\n\n                        break;\n                    }\n\n                    // Inject collection\n                    $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection());\n                    $pColl->setOwner($entity, $assoc);\n                    $pColl->setInitialized(false);\n\n                    $reflField = $class->propertyAccessors[$field];\n                    $reflField->setValue($entity, $pColl);\n\n                    if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {\n                        if (\n                            $assoc->isOneToMany()\n                            // is iteration\n                            && ! (isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION])\n                            // is foreign key composite\n                            && ! ($targetClass->hasAssociation($assoc->mappedBy) && count($targetClass->getAssociationMapping($assoc->mappedBy)->joinColumns) > 1)\n                            && ! $assoc->isIndexed()\n                        ) {\n                            $this->scheduleCollectionForBatchLoading($pColl, $class);\n                        } else {\n                            $this->loadCollection($pColl);\n                            $pColl->takeSnapshot();\n                        }\n                    }\n\n                    $this->originalEntityData[$oid][$field] = $pColl;\n                    break;\n            }\n        }\n\n        // defer invoking of postLoad event to hydration complete step\n        $this->hydrationCompleteHandler->deferPostLoadInvoking($class, $entity);\n\n        return $entity;\n    }\n\n    public function triggerEagerLoads(): void\n    {\n        if (! $this->eagerLoadingEntities && ! $this->eagerLoadingCollections) {\n            return;\n        }\n\n        // avoid infinite recursion\n        $eagerLoadingEntities       = $this->eagerLoadingEntities;\n        $this->eagerLoadingEntities = [];\n\n        foreach ($eagerLoadingEntities as $entityName => $ids) {\n            if (! $ids) {\n                continue;\n            }\n\n            $class   = $this->em->getClassMetadata($entityName);\n            $batches = array_chunk($ids, $this->em->getConfiguration()->getEagerFetchBatchSize());\n\n            foreach ($batches as $batchedIds) {\n                $this->getEntityPersister($entityName)->loadAll(\n                    array_combine($class->identifier, [$batchedIds]),\n                );\n            }\n        }\n\n        $eagerLoadingCollections       = $this->eagerLoadingCollections; // avoid recursion\n        $this->eagerLoadingCollections = [];\n\n        foreach ($eagerLoadingCollections as $group) {\n            $this->eagerLoadCollections($group['items'], $group['mapping']);\n        }\n    }\n\n    /**\n     * Load all data into the given collections, according to the specified mapping\n     *\n     * @param PersistentCollection[] $collections\n     */\n    private function eagerLoadCollections(array $collections, ToManyInverseSideMapping $mapping): void\n    {\n        $targetEntity = $mapping->targetEntity;\n        $class        = $this->em->getClassMetadata($mapping->sourceEntity);\n        $mappedBy     = $mapping->mappedBy;\n\n        $batches = array_chunk($collections, $this->em->getConfiguration()->getEagerFetchBatchSize(), true);\n\n        foreach ($batches as $collectionBatch) {\n            $entities = [];\n\n            foreach ($collectionBatch as $collection) {\n                $entities[] = $collection->getOwner();\n            }\n\n            $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping->orderBy);\n\n            $targetClass    = $this->em->getClassMetadata($targetEntity);\n            $targetProperty = $targetClass->getPropertyAccessor($mappedBy);\n            assert($targetProperty !== null);\n\n            foreach ($found as $targetValue) {\n                $sourceEntity = $targetProperty->getValue($targetValue);\n\n                if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]->joinColumns)) {\n                    // case where the hydration $targetValue itself has not yet fully completed, for example\n                    // in case a bi-directional association is being hydrated and deferring eager loading is\n                    // not possible due to subclassing.\n                    $data = $this->getOriginalEntityData($targetValue);\n                    $id   = [];\n                    foreach ($targetClass->associationMappings[$mappedBy]->joinColumns as $joinColumn) {\n                        $id[] = $data[$joinColumn->name];\n                    }\n                } else {\n                    $id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));\n                }\n\n                $idHash = implode(' ', $id);\n\n                if ($mapping->indexBy !== null) {\n                    $indexByProperty = $targetClass->getPropertyAccessor($mapping->indexBy);\n                    assert($indexByProperty !== null);\n                    $collectionBatch[$idHash]->hydrateSet($indexByProperty->getValue($targetValue), $targetValue);\n                } else {\n                    $collectionBatch[$idHash]->add($targetValue);\n                }\n            }\n        }\n\n        foreach ($collections as $association) {\n            $association->setInitialized(true);\n            $association->takeSnapshot();\n        }\n    }\n\n    /**\n     * Initializes (loads) an uninitialized persistent collection of an entity.\n     *\n     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.\n     */\n    public function loadCollection(PersistentCollection $collection): void\n    {\n        $assoc     = $collection->getMapping();\n        $persister = $this->getEntityPersister($assoc->targetEntity);\n\n        switch ($assoc->type()) {\n            case ClassMetadata::ONE_TO_MANY:\n                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);\n                break;\n\n            case ClassMetadata::MANY_TO_MANY:\n                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);\n                break;\n        }\n\n        $collection->setInitialized(true);\n    }\n\n    /**\n     * Schedule this collection for batch loading at the end of the UnitOfWork\n     */\n    private function scheduleCollectionForBatchLoading(PersistentCollection $collection, ClassMetadata $sourceClass): void\n    {\n        $mapping = $collection->getMapping();\n        $name    = $mapping->sourceEntity . '#' . $mapping->fieldName;\n\n        if (! isset($this->eagerLoadingCollections[$name])) {\n            $this->eagerLoadingCollections[$name] = [\n                'items'   => [],\n                'mapping' => $mapping,\n            ];\n        }\n\n        $owner = $collection->getOwner();\n        assert($owner !== null);\n\n        $id     = $this->identifierFlattener->flattenIdentifier(\n            $sourceClass,\n            $sourceClass->getIdentifierValues($owner),\n        );\n        $idHash = implode(' ', $id);\n\n        $this->eagerLoadingCollections[$name]['items'][$idHash] = $collection;\n    }\n\n    /**\n     * Gets the identity map of the UnitOfWork.\n     *\n     * @return array<class-string, array<string, object>>\n     */\n    public function getIdentityMap(): array\n    {\n        return $this->identityMap;\n    }\n\n    /**\n     * Gets the original data of an entity. The original data is the data that was\n     * present at the time the entity was reconstituted from the database.\n     *\n     * @phpstan-return array<string, mixed>\n     */\n    public function getOriginalEntityData(object $entity): array\n    {\n        $oid = spl_object_id($entity);\n\n        return $this->originalEntityData[$oid] ?? [];\n    }\n\n    /**\n     * @param mixed[] $data\n     *\n     * @ignore\n     */\n    public function setOriginalEntityData(object $entity, array $data): void\n    {\n        $this->originalEntityData[spl_object_id($entity)] = $data;\n    }\n\n    /**\n     * INTERNAL:\n     * Sets a property value of the original data array of an entity.\n     *\n     * @ignore\n     */\n    public function setOriginalEntityProperty(int $oid, string $property, mixed $value): void\n    {\n        $this->originalEntityData[$oid][$property] = $value;\n    }\n\n    /**\n     * Gets the identifier of an entity.\n     * The returned value is always an array of identifier values. If the entity\n     * has a composite identifier then the identifier values are in the same\n     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().\n     *\n     * @return mixed[] The identifier values.\n     */\n    public function getEntityIdentifier(object $entity): array\n    {\n        return $this->entityIdentifiers[spl_object_id($entity)]\n            ?? throw EntityNotFoundException::noIdentifierFound(get_debug_type($entity));\n    }\n\n    /**\n     * Processes an entity instance to extract their identifier values.\n     *\n     * @return mixed A scalar value.\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function getSingleIdentifierValue(object $entity): mixed\n    {\n        $class = $this->em->getClassMetadata($entity::class);\n\n        if ($class->isIdentifierComposite) {\n            throw ORMInvalidArgumentException::invalidCompositeIdentifier();\n        }\n\n        $values = $this->isInIdentityMap($entity)\n            ? $this->getEntityIdentifier($entity)\n            : $class->getIdentifierValues($entity);\n\n        return $values[$class->identifier[0]] ?? null;\n    }\n\n    /**\n     * Tries to find an entity with the given identifier in the identity map of\n     * this UnitOfWork.\n     *\n     * @param mixed        $id            The entity identifier to look for.\n     * @param class-string $rootClassName The name of the root class of the mapped entity hierarchy.\n     *\n     * @return object|false Returns the entity with the specified identifier if it exists in\n     *                      this UnitOfWork, FALSE otherwise.\n     */\n    public function tryGetById(mixed $id, string $rootClassName): object|false\n    {\n        $idHash = self::getIdHashByIdentifier((array) $id);\n\n        return $this->identityMap[$rootClassName][$idHash] ?? false;\n    }\n\n    /**\n     * Schedules an entity for dirty-checking at commit-time.\n     *\n     * @todo Rename: scheduleForSynchronization\n     */\n    public function scheduleForDirtyCheck(object $entity): void\n    {\n        $rootClassName = $this->em->getClassMetadata($entity::class)->rootEntityName;\n\n        $this->scheduledForSynchronization[$rootClassName][spl_object_id($entity)] = $entity;\n    }\n\n    /**\n     * Checks whether the UnitOfWork has any pending insertions.\n     */\n    public function hasPendingInsertions(): bool\n    {\n        return ! empty($this->entityInsertions);\n    }\n\n    /**\n     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the\n     * number of entities in the identity map.\n     */\n    public function size(): int\n    {\n        return array_sum(array_map('count', $this->identityMap));\n    }\n\n    /**\n     * Gets the EntityPersister for an Entity.\n     *\n     * @param class-string $entityName The name of the Entity.\n     */\n    public function getEntityPersister(string $entityName): EntityPersister\n    {\n        if (isset($this->persisters[$entityName])) {\n            return $this->persisters[$entityName];\n        }\n\n        $class = $this->em->getClassMetadata($entityName);\n\n        $persister = match (true) {\n            $class->isInheritanceTypeNone() => new BasicEntityPersister($this->em, $class),\n            $class->isInheritanceTypeSingleTable() => new SingleTablePersister($this->em, $class),\n            $class->isInheritanceTypeJoined() => new JoinedSubclassPersister($this->em, $class),\n            default => throw new RuntimeException('No persister found for entity.'),\n        };\n\n        if ($this->hasCache && $class->cache !== null) {\n            $persister = $this->em->getConfiguration()\n                ->getSecondLevelCacheConfiguration()\n                ->getCacheFactory()\n                ->buildCachedEntityPersister($this->em, $persister, $class);\n        }\n\n        $this->persisters[$entityName] = $persister;\n\n        return $this->persisters[$entityName];\n    }\n\n    /** Gets a collection persister for a collection-valued association. */\n    public function getCollectionPersister(AssociationMapping $association): CollectionPersister\n    {\n        $role = isset($association->cache)\n            ? $association->sourceEntity . '::' . $association->fieldName\n            : $association->type();\n\n        if (isset($this->collectionPersisters[$role])) {\n            return $this->collectionPersisters[$role];\n        }\n\n        $persister = $association->type() === ClassMetadata::ONE_TO_MANY\n            ? new OneToManyPersister($this->em)\n            : new ManyToManyPersister($this->em);\n\n        if ($this->hasCache && isset($association->cache)) {\n            $persister = $this->em->getConfiguration()\n                ->getSecondLevelCacheConfiguration()\n                ->getCacheFactory()\n                ->buildCachedCollectionPersister($this->em, $persister, $association);\n        }\n\n        $this->collectionPersisters[$role] = $persister;\n\n        return $this->collectionPersisters[$role];\n    }\n\n    /**\n     * INTERNAL:\n     * Registers an entity as managed.\n     *\n     * @param mixed[] $id   The identifier values.\n     * @param mixed[] $data The original entity data.\n     */\n    public function registerManaged(object $entity, array $id, array $data): void\n    {\n        $oid = spl_object_id($entity);\n\n        $this->entityIdentifiers[$oid]  = $id;\n        $this->entityStates[$oid]       = self::STATE_MANAGED;\n        $this->originalEntityData[$oid] = $data;\n\n        $this->addToIdentityMap($entity);\n    }\n\n    /* PropertyChangedListener implementation */\n\n    /**\n     * Notifies this UnitOfWork of a property change in an entity.\n     *\n     * {@inheritDoc}\n     */\n    public function propertyChanged(object $sender, string $propertyName, mixed $oldValue, mixed $newValue): void\n    {\n        $oid   = spl_object_id($sender);\n        $class = $this->em->getClassMetadata($sender::class);\n\n        $isAssocField = isset($class->associationMappings[$propertyName]);\n\n        if (! $isAssocField && ! isset($class->fieldMappings[$propertyName])) {\n            return; // ignore non-persistent fields\n        }\n\n        // Update changeset and mark entity for synchronization\n        $this->entityChangeSets[$oid][$propertyName] = [$oldValue, $newValue];\n\n        if (! isset($this->scheduledForSynchronization[$class->rootEntityName][$oid])) {\n            $this->scheduleForDirtyCheck($sender);\n        }\n    }\n\n    /**\n     * Gets the currently scheduled entity insertions in this UnitOfWork.\n     *\n     * @phpstan-return array<int, object>\n     */\n    public function getScheduledEntityInsertions(): array\n    {\n        return $this->entityInsertions;\n    }\n\n    /**\n     * Gets the currently scheduled entity updates in this UnitOfWork.\n     *\n     * @phpstan-return array<int, object>\n     */\n    public function getScheduledEntityUpdates(): array\n    {\n        return $this->entityUpdates;\n    }\n\n    /**\n     * Gets the currently scheduled entity deletions in this UnitOfWork.\n     *\n     * @phpstan-return array<int, object>\n     */\n    public function getScheduledEntityDeletions(): array\n    {\n        return $this->entityDeletions;\n    }\n\n    /**\n     * Gets the currently scheduled complete collection deletions\n     *\n     * @phpstan-return array<int, PersistentCollection<array-key, object>>\n     */\n    public function getScheduledCollectionDeletions(): array\n    {\n        return $this->collectionDeletions;\n    }\n\n    /**\n     * Gets the currently scheduled collection inserts, updates and deletes.\n     *\n     * @phpstan-return array<int, PersistentCollection<array-key, object>>\n     */\n    public function getScheduledCollectionUpdates(): array\n    {\n        return $this->collectionUpdates;\n    }\n\n    /**\n     * Helper method to initialize a lazy loading proxy or persistent collection.\n     */\n    public function initializeObject(object $obj): void\n    {\n        if ($obj instanceof InternalProxy) {\n            $obj->__load();\n\n            return;\n        }\n\n        if ($obj instanceof PersistentCollection) {\n            $obj->initialize();\n\n            return;\n        }\n\n        if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            $reflection = $this->em->getClassMetadata($obj::class)->getReflectionClass();\n            $reflection->initializeLazyObject($obj);\n        }\n    }\n\n    /** Tests if a value is an uninitialized entity. */\n    public function isUninitializedObject(mixed $obj): bool\n    {\n        if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled() && ! ($obj instanceof Collection) && is_object($obj)) {\n            return $this->em->getClassMetadata($obj::class)->reflClass->isUninitializedLazyObject($obj);\n        }\n\n        return $obj instanceof InternalProxy && ! $obj->__isInitialized();\n    }\n\n    /**\n     * Helper method to show an object as string.\n     */\n    private static function objToStr(object $obj): string\n    {\n        return $obj instanceof Stringable ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj);\n    }\n\n    /**\n     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().\n     *\n     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information\n     * on this object that might be necessary to perform a correct update.\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function markReadOnly(object $object): void\n    {\n        if (! $this->isInIdentityMap($object)) {\n            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);\n        }\n\n        $this->readOnlyObjects[spl_object_id($object)] = true;\n    }\n\n    /**\n     * Is this entity read only?\n     *\n     * @throws ORMInvalidArgumentException\n     */\n    public function isReadOnly(object $object): bool\n    {\n        return isset($this->readOnlyObjects[spl_object_id($object)]);\n    }\n\n    /**\n     * Perform whatever processing is encapsulated here after completion of the transaction.\n     */\n    private function afterTransactionComplete(): void\n    {\n        $this->performCallbackOnCachedPersister(static function (CachedPersister $persister): void {\n            $persister->afterTransactionComplete();\n        });\n    }\n\n    /**\n     * Perform whatever processing is encapsulated here after completion of the rolled-back.\n     */\n    private function afterTransactionRolledBack(): void\n    {\n        $this->performCallbackOnCachedPersister(static function (CachedPersister $persister): void {\n            $persister->afterTransactionRolledBack();\n        });\n    }\n\n    /**\n     * Performs an action after the transaction.\n     */\n    private function performCallbackOnCachedPersister(callable $callback): void\n    {\n        if (! $this->hasCache) {\n            return;\n        }\n\n        foreach ([...$this->persisters, ...$this->collectionPersisters] as $persister) {\n            if ($persister instanceof CachedPersister) {\n                $callback($persister);\n            }\n        }\n    }\n\n    private function dispatchOnFlushEvent(): void\n    {\n        if ($this->evm->hasListeners(Events::onFlush)) {\n            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));\n        }\n    }\n\n    private function dispatchPostFlushEvent(): void\n    {\n        if ($this->evm->hasListeners(Events::postFlush)) {\n            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));\n        }\n    }\n\n    /**\n     * Verifies if two given entities actually are the same based on identifier comparison\n     */\n    private function isIdentifierEquals(object $entity1, object $entity2): bool\n    {\n        if ($entity1 === $entity2) {\n            return true;\n        }\n\n        $class = $this->em->getClassMetadata($entity1::class);\n\n        if ($class !== $this->em->getClassMetadata($entity2::class)) {\n            return false;\n        }\n\n        $oid1 = spl_object_id($entity1);\n        $oid2 = spl_object_id($entity2);\n\n        $id1 = $this->entityIdentifiers[$oid1] ?? $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity1));\n        $id2 = $this->entityIdentifiers[$oid2] ?? $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity2));\n\n        return $id1 === $id2 || self::getIdHashByIdentifier($id1) === self::getIdHashByIdentifier($id2);\n    }\n\n    /** @throws ORMInvalidArgumentException */\n    private function assertThatThereAreNoUnintentionallyNonPersistedAssociations(): void\n    {\n        $entitiesNeedingCascadePersist = array_diff_key($this->nonCascadedNewDetectedEntities, $this->entityInsertions);\n\n        $this->nonCascadedNewDetectedEntities = [];\n\n        if ($entitiesNeedingCascadePersist) {\n            throw ORMInvalidArgumentException::newEntitiesFoundThroughRelationships(\n                array_values($entitiesNeedingCascadePersist),\n            );\n        }\n    }\n\n    /**\n     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.\n     * Unit of work able to fire deferred events, related to loading events here.\n     *\n     * @internal should be called internally from object hydrators\n     */\n    public function hydrationComplete(): void\n    {\n        $this->hydrationCompleteHandler->hydrationComplete();\n    }\n\n    /** @throws MappingException if the entity has more than a single identifier. */\n    private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, mixed $identifierValue): mixed\n    {\n        return $this->em->getConnection()->convertToPHPValue(\n            $identifierValue,\n            $class->getTypeOfField($class->getSingleIdentifierFieldName()),\n        );\n    }\n\n    /**\n     * Given a flat identifier, this method will produce another flat identifier, but with all\n     * association fields that are mapped as identifiers replaced by entity references, recursively.\n     *\n     * @param mixed[] $flatIdentifier\n     *\n     * @return array<string, mixed>\n     */\n    private function normalizeIdentifier(ClassMetadata $targetClass, array $flatIdentifier): array\n    {\n        $normalizedAssociatedId = [];\n\n        foreach ($targetClass->getIdentifierFieldNames() as $name) {\n            if (! array_key_exists($name, $flatIdentifier)) {\n                continue;\n            }\n\n            if (! $targetClass->isSingleValuedAssociation($name)) {\n                $normalizedAssociatedId[$name] = $flatIdentifier[$name];\n                continue;\n            }\n\n            $targetIdMetadata = $this->em->getClassMetadata($targetClass->getAssociationTargetClass($name));\n\n            // Note: the ORM prevents using an entity with a composite identifier as an identifier association\n            //       therefore, reset($targetIdMetadata->identifier) is always correct\n            $normalizedAssociatedId[$name] = $this->em->getReference(\n                $targetIdMetadata->getName(),\n                $this->normalizeIdentifier(\n                    $targetIdMetadata,\n                    [(string) reset($targetIdMetadata->identifier) => $flatIdentifier[$name]],\n                ),\n            );\n        }\n\n        return $normalizedAssociatedId;\n    }\n\n    /**\n     * Assign a post-insert generated ID to an entity\n     *\n     * This is used by EntityPersisters after they inserted entities into the database.\n     * It will place the assigned ID values in the entity's fields and start tracking\n     * the entity in the identity map.\n     */\n    final public function assignPostInsertId(object $entity, mixed $generatedId): void\n    {\n        $class   = $this->em->getClassMetadata($entity::class);\n        $idField = $class->getSingleIdentifierFieldName();\n        $idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $generatedId);\n        $oid     = spl_object_id($entity);\n\n        $class->propertyAccessors[$idField]->setValue($entity, $idValue);\n\n        $this->entityIdentifiers[$oid]            = [$idField => $idValue];\n        $this->entityStates[$oid]                 = self::STATE_MANAGED;\n        $this->originalEntityData[$oid][$idField] = $idValue;\n\n        $this->addToIdentityMap($entity);\n    }\n}\n"
  },
  {
    "path": "src/Utility/HierarchyDiscriminatorResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Utility;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\n\n/** @internal This class exists only to avoid code duplication, do not reuse it externally */\nfinal class HierarchyDiscriminatorResolver\n{\n    private function __construct()\n    {\n    }\n\n    /**\n     * This method is needed to make INSTANCEOF work correctly with inheritance: if the class at hand has inheritance,\n     * it extracts all the discriminators from the child classes and returns them\n     *\n     * @return null[]\n     * @phpstan-return array<array-key, null>\n     */\n    public static function resolveDiscriminatorsForClass(\n        ClassMetadata $rootClassMetadata,\n        EntityManagerInterface $entityManager,\n    ): array {\n        $hierarchyClasses   = $rootClassMetadata->subClasses;\n        $hierarchyClasses[] = $rootClassMetadata->name;\n\n        $discriminators = [];\n\n        foreach ($hierarchyClasses as $class) {\n            $currentMetadata      = $entityManager->getClassMetadata($class);\n            $currentDiscriminator = $currentMetadata->discriminatorValue;\n\n            if ($currentDiscriminator !== null) {\n                $discriminators[$currentDiscriminator] = null;\n            }\n        }\n\n        return $discriminators;\n    }\n}\n"
  },
  {
    "path": "src/Utility/IdentifierFlattener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Utility;\n\nuse BackedEnum;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadataFactory;\n\nuse function assert;\nuse function implode;\nuse function is_a;\n\n/**\n * The IdentifierFlattener utility now houses some of the identifier manipulation logic from unit of work, so that it\n * can be re-used elsewhere.\n */\nfinal class IdentifierFlattener\n{\n    /**\n     * Initializes a new IdentifierFlattener instance, bound to the given EntityManager.\n     */\n    public function __construct(\n        /**\n         * The UnitOfWork used to coordinate object-level transactions.\n         */\n        private readonly UnitOfWork $unitOfWork,\n        /**\n         * The metadata factory, used to retrieve the ORM metadata of entity classes.\n         */\n        private readonly ClassMetadataFactory $metadataFactory,\n    ) {\n    }\n\n    /**\n     * convert foreign identifiers into scalar foreign key values to avoid object to string conversion failures.\n     *\n     * @param mixed[] $id\n     *\n     * @return mixed[]\n     * @phpstan-return array<string, mixed>\n     */\n    public function flattenIdentifier(ClassMetadata $class, array $id): array\n    {\n        $flatId = [];\n\n        foreach ($class->identifier as $field) {\n            if (isset($class->associationMappings[$field]) && isset($id[$field]) && is_a($id[$field], $class->associationMappings[$field]->targetEntity)) {\n                $targetClassMetadata = $this->metadataFactory->getMetadataFor(\n                    $class->associationMappings[$field]->targetEntity,\n                );\n                assert($targetClassMetadata instanceof ClassMetadata);\n\n                if ($this->unitOfWork->isInIdentityMap($id[$field])) {\n                    $associatedId = $this->flattenIdentifier($targetClassMetadata, $this->unitOfWork->getEntityIdentifier($id[$field]));\n                } else {\n                    $associatedId = $this->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($id[$field]));\n                }\n\n                $flatId[$field] = implode(' ', $associatedId);\n            } elseif (isset($class->associationMappings[$field])) {\n                assert($class->associationMappings[$field]->isToOneOwningSide());\n                $associatedId = [];\n\n                foreach ($class->associationMappings[$field]->joinColumns as $joinColumn) {\n                    $associatedId[] = $id[$joinColumn->name];\n                }\n\n                $flatId[$field] = implode(' ', $associatedId);\n            } else {\n                if ($id[$field] instanceof BackedEnum) {\n                    $flatId[$field] = $id[$field]->value;\n                } else {\n                    $flatId[$field] = $id[$field];\n                }\n            }\n        }\n\n        return $flatId;\n    }\n}\n"
  },
  {
    "path": "src/Utility/LockSqlHelper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Utility;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\DB2Platform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLServerPlatform;\n\n/** @internal */\ntrait LockSqlHelper\n{\n    private function getReadLockSQL(AbstractPlatform $platform): string\n    {\n        return match (true) {\n            $platform instanceof AbstractMySQLPlatform => 'LOCK IN SHARE MODE',\n            $platform instanceof PostgreSQLPlatform => 'FOR SHARE',\n            default => $this->getWriteLockSQL($platform),\n        };\n    }\n\n    private function getWriteLockSQL(AbstractPlatform $platform): string\n    {\n        return match (true) {\n            $platform instanceof DB2Platform => 'WITH RR USE AND KEEP UPDATE LOCKS',\n            $platform instanceof SQLitePlatform,\n            $platform instanceof SQLServerPlatform => '',\n            default => 'FOR UPDATE',\n        };\n    }\n}\n"
  },
  {
    "path": "src/Utility/PersisterHelper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\ORM\\Utility;\n\nuse BackedEnum;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse RuntimeException;\n\nuse function array_map;\nuse function array_merge;\nuse function assert;\nuse function is_array;\nuse function is_object;\nuse function sprintf;\n\n/**\n * The PersisterHelper contains logic to infer binding types which is used in\n * several persisters.\n *\n * @link   www.doctrine-project.org\n */\nclass PersisterHelper\n{\n    /**\n     * @return list<string>\n     *\n     * @throws QueryException\n     */\n    public static function getTypeOfField(string $fieldName, ClassMetadata $class, EntityManagerInterface $em): array\n    {\n        if (isset($class->fieldMappings[$fieldName])) {\n            return [$class->fieldMappings[$fieldName]->type];\n        }\n\n        if (! isset($class->associationMappings[$fieldName])) {\n            return [];\n        }\n\n        $assoc = $class->associationMappings[$fieldName];\n\n        if (! $assoc->isOwningSide()) {\n            return self::getTypeOfField($assoc->mappedBy, $em->getClassMetadata($assoc->targetEntity), $em);\n        }\n\n        if ($assoc->isManyToManyOwningSide()) {\n            $joinData = $assoc->joinTable;\n        } else {\n            $joinData = $assoc;\n        }\n\n        $types       = [];\n        $targetClass = $em->getClassMetadata($assoc->targetEntity);\n\n        foreach ($joinData->joinColumns as $joinColumn) {\n            $types[] = self::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $em);\n        }\n\n        return $types;\n    }\n\n    /** @throws RuntimeException */\n    public static function getTypeOfColumn(string $columnName, ClassMetadata $class, EntityManagerInterface $em): string\n    {\n        if (isset($class->fieldNames[$columnName])) {\n            $fieldName = $class->fieldNames[$columnName];\n\n            if (isset($class->fieldMappings[$fieldName])) {\n                return $class->fieldMappings[$fieldName]->type;\n            }\n        }\n\n        // iterate over to-one association mappings\n        foreach ($class->associationMappings as $assoc) {\n            if (! $assoc->isToOneOwningSide()) {\n                continue;\n            }\n\n            foreach ($assoc->joinColumns as $joinColumn) {\n                if ($joinColumn->name === $columnName) {\n                    $targetColumnName = $joinColumn->referencedColumnName;\n                    $targetClass      = $em->getClassMetadata($assoc->targetEntity);\n\n                    return self::getTypeOfColumn($targetColumnName, $targetClass, $em);\n                }\n            }\n        }\n\n        // iterate over to-many association mappings\n        foreach ($class->associationMappings as $assoc) {\n            if (! $assoc->isManyToManyOwningSide()) {\n                continue;\n            }\n\n            foreach ($assoc->joinTable->joinColumns as $joinColumn) {\n                if ($joinColumn->name === $columnName) {\n                    $targetColumnName = $joinColumn->referencedColumnName;\n                    $targetClass      = $em->getClassMetadata($assoc->targetEntity);\n\n                    return self::getTypeOfColumn($targetColumnName, $targetClass, $em);\n                }\n            }\n        }\n\n        throw new RuntimeException(sprintf(\n            'Could not resolve type of column \"%s\" of class \"%s\"',\n            $columnName,\n            $class->getName(),\n        ));\n    }\n\n    /**\n     * Infers field types to be used by parameter type casting.\n     *\n     * @return list<ParameterType|int|string>\n     * @phpstan-return list<ParameterType::*|ArrayParameterType::*|string>\n     *\n     * @throws QueryException\n     */\n    public static function inferParameterTypes(\n        string $field,\n        mixed $value,\n        ClassMetadata $class,\n        EntityManagerInterface $em,\n    ): array {\n        $types = [];\n\n        switch (true) {\n            case isset($class->fieldMappings[$field]):\n                $types = array_merge($types, [$class->fieldMappings[$field]->type]);\n                break;\n\n            case isset($class->associationMappings[$field]):\n                $assoc = $em->getMetadataFactory()->getOwningSide($class->associationMappings[$field]);\n                $class = $em->getClassMetadata($assoc->targetEntity);\n\n                if ($assoc->isManyToManyOwningSide()) {\n                    $columns = $assoc->relationToTargetKeyColumns;\n                } else {\n                    assert($assoc->isToOneOwningSide());\n                    $columns = $assoc->sourceToTargetKeyColumns;\n                }\n\n                foreach ($columns as $column) {\n                    $types[] = self::getTypeOfColumn($column, $class, $em);\n                }\n\n                break;\n\n            default:\n                $types[] = ParameterType::STRING;\n                break;\n        }\n\n        if (is_array($value)) {\n            return array_map(self::getArrayBindingType(...), $types);\n        }\n\n        return $types;\n    }\n\n    /** @phpstan-return ArrayParameterType::* */\n    private static function getArrayBindingType(ParameterType|int|string $type): ArrayParameterType|int\n    {\n        if (! $type instanceof ParameterType) {\n            $type = Type::getType((string) $type)->getBindingType();\n        }\n\n        return match ($type) {\n            ParameterType::STRING => ArrayParameterType::STRING,\n            ParameterType::INTEGER => ArrayParameterType::INTEGER,\n            ParameterType::ASCII => ArrayParameterType::ASCII,\n            ParameterType::BINARY => ArrayParameterType::BINARY,\n        };\n    }\n\n    /**\n     * Converts a value to the type and value required to bind it as a parameter.\n     *\n     * @return list<mixed>\n     */\n    public static function convertToParameterValue(mixed $value, EntityManagerInterface $em): array\n    {\n        if (is_array($value)) {\n            $newValue = [];\n\n            foreach ($value as $itemValue) {\n                $newValue = array_merge($newValue, self::convertToParameterValue($itemValue, $em));\n            }\n\n            return [$newValue];\n        }\n\n        return self::convertIndividualValue($value, $em);\n    }\n\n    /** @phpstan-return list<mixed> */\n    private static function convertIndividualValue(mixed $value, EntityManagerInterface $em): array\n    {\n        if (! is_object($value)) {\n            return [$value];\n        }\n\n        if ($value instanceof BackedEnum) {\n            return [$value->value];\n        }\n\n        $valueClass = DefaultProxyClassNameResolver::getClass($value);\n\n        if ($em->getMetadataFactory()->isTransient($valueClass)) {\n            return [$value];\n        }\n\n        $class = $em->getClassMetadata($valueClass);\n\n        if ($class->isIdentifierComposite) {\n            $newValue = [];\n\n            foreach ($class->getIdentifierValues($value) as $innerValue) {\n                $newValue = array_merge($newValue, self::convertToParameterValue($innerValue, $em));\n            }\n\n            return $newValue;\n        }\n\n        return [$em->getUnitOfWork()->getSingleIdentifierValue($value)];\n    }\n}\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "Tests/Proxies/\nTests/ORM/Proxy/generated/\nTests/ORM/Tools/Export/export\n"
  },
  {
    "path": "tests/Doctrine/Tests/ORM/Functional/GH8011Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\n\n/**\n * Functional tests for ordering with arithmetic expression.\n */\n#[Group('GH8011')]\nclass GH8011Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->generateFixture();\n    }\n\n    private function skipIfPostgres(string $test): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n        if ($platform instanceof PostgreSQLPlatform) {\n            self::markTestSkipped(\n                'The ' . $test . ' test does not work on postgresql (see https://github.com/doctrine/orm/pull/8012).',\n            );\n        }\n    }\n\n    public function testOrderWithArithmeticExpressionWithSingleValuedPathExpression(): void\n    {\n        $dql = 'SELECT p ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY p.id + p.id ASC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Benjamin E.', $result[0]->getName());\n        $this->assertEquals('Guilherme B.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression(): void\n    {\n        $dql = 'SELECT p ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY 1 + p.id ASC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Benjamin E.', $result[0]->getName());\n        $this->assertEquals('Guilherme B.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression2(): void\n    {\n        $dql = 'SELECT p ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY ((1 + p.id)) ASC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Benjamin E.', $result[0]->getName());\n        $this->assertEquals('Guilherme B.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndLiteral(): void\n    {\n        $dql = 'SELECT p ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY p.id + 1 ASC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Benjamin E.', $result[0]->getName());\n        $this->assertEquals('Guilherme B.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY s + 1 DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral2(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY ((s + 1)) DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY 1 + s DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable2(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY ((1 + s)) DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY s + p.id DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression2(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY ((s + p.id)) DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndResultVariable(): void\n    {\n        $this->skipIfPostgres(__FUNCTION__);\n\n        $dql = 'SELECT p, p.salary AS HIDDEN s ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY p.id + s DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function testOrderWithArithmeticExpressionWithLiteralAndResultVariableUsingHiddenResultVariable(): void\n    {\n        $dql = 'SELECT p, 1 + p.salary AS HIDDEN _order ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'ORDER BY _order DESC';\n\n        /** @var CompanyEmployee[] $result */\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        $this->assertEquals(2, count($result));\n        $this->assertEquals('Guilherme B.', $result[0]->getName());\n        $this->assertEquals('Benjamin E.', $result[1]->getName());\n    }\n\n    public function generateFixture(): void\n    {\n        $person1 = new CompanyEmployee();\n        $person1->setName('Benjamin E.');\n        $person1->setDepartment('IT');\n        $person1->setSalary(200000);\n\n        $person2 = new CompanyEmployee();\n        $person2->setName('Guilherme B.');\n        $person2->setDepartment('IT2');\n        $person2->setSalary(400000);\n\n        $this->_em->persist($person1);\n        $this->_em->persist($person2);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Doctrine/Tests/ORM/Functional/Ticket/GH12063Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH12063Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH12063Association::class, GH12063Entity::class);\n    }\n\n    public function testLoadedAssociationWithBackedEnum(): void\n    {\n        $association       = new GH12063Association();\n        $association->code = GH12063Code::One;\n\n        $this->_em->persist($association);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity              = new GH12063Entity();\n        $entity->association = $this->_em->find(GH12063Association::class, GH12063Code::One);\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->assertNotNull($entity->id);\n    }\n\n    public function testProxyAssociationWithBackedEnum(): void\n    {\n        $association       = new GH12063Association();\n        $association->code = GH12063Code::Two;\n\n        $this->_em->persist($association);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity              = new GH12063Entity();\n        $entity->association = $this->_em->getReference(GH12063Association::class, GH12063Code::Two);\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->assertNotNull($entity->id);\n    }\n}\n\nenum GH12063Code: string\n{\n    case One = 'one';\n    case Two = 'two';\n}\n\n#[Entity]\nclass GH12063Association\n{\n    #[Id]\n    #[Column(length: 3)]\n    public GH12063Code $code;\n}\n\n#[Entity]\nclass GH12063Entity\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\ManyToOne]\n    #[ORM\\JoinColumn(referencedColumnName: 'code', options: ['length' => 3])]\n    public GH12063Association $association;\n}\n"
  },
  {
    "path": "tests/Doctrine/Tests/ORM/Functional/Ticket/LazyEagerCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass LazyEagerCollectionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            LazyEagerCollectionUser::class,\n            LazyEagerCollectionAddress::class,\n            LazyEagerCollectionPhone::class,\n        );\n    }\n\n    public function testRefreshRefreshesBothLazyAndEagerCollections(): void\n    {\n        $user       = new LazyEagerCollectionUser();\n        $user->data = 'Guilherme';\n\n        $ph       = new LazyEagerCollectionPhone();\n        $ph->data = '12345';\n        $user->addPhone($ph);\n\n        $ad       = new LazyEagerCollectionAddress();\n        $ad->data = '6789';\n        $user->addAddress($ad);\n\n        $this->_em->persist($user);\n        $this->_em->persist($ad);\n        $this->_em->persist($ph);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(LazyEagerCollectionUser::class, $user->id);\n        $ph   = $user->phones[0];\n        $ad   = $user->addresses[0];\n\n        $ph->data = 'abc';\n        $ad->data = 'def';\n\n        $this->_em->refresh($user);\n\n        self::assertSame('12345', $ph->data);\n        self::assertSame('6789', $ad->data);\n    }\n}\n\n#[Entity]\nclass LazyEagerCollectionUser\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[Column(type: 'string', length: 255)]\n    public string $data;\n\n    /** @var Collection<LazyEagerCollectionPhone> */\n    #[ORM\\OneToMany(targetEntity: 'LazyEagerCollectionPhone', cascade: ['refresh'], fetch: 'EAGER', mappedBy: 'user')]\n    public Collection $phones;\n\n    /** @var Collection<LazyEagerCollectionAddress> */\n    #[ORM\\OneToMany(targetEntity: 'LazyEagerCollectionAddress', cascade: ['refresh'], mappedBy: 'user')]\n    public Collection $addresses;\n\n    public function __construct()\n    {\n        $this->addresses = new ArrayCollection();\n        $this->phones    = new ArrayCollection();\n    }\n\n    public function addPhone(LazyEagerCollectionPhone $phone): void\n    {\n        $phone->user    = $this;\n        $this->phones[] = $phone;\n    }\n\n    public function addAddress(LazyEagerCollectionAddress $address): void\n    {\n        $address->user     = $this;\n        $this->addresses[] = $address;\n    }\n}\n\n#[Entity]\nclass LazyEagerCollectionPhone\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[Column(type: 'string', length: 255)]\n    public string $data;\n\n    #[ORM\\ManyToOne(targetEntity: 'LazyEagerCollectionUser', inversedBy: 'phones')]\n    public LazyEagerCollectionUser $user;\n}\n\n#[Entity]\nclass LazyEagerCollectionAddress\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[Column(type: 'string', length: 255)]\n    public string $data;\n\n    #[ORM\\ManyToOne(targetEntity: 'LazyEagerCollectionUser', inversedBy: 'addresses')]\n    public LazyEagerCollectionUser $user;\n}\n"
  },
  {
    "path": "tests/Performance/ChangeSet/UnitOfWorkComputeChangesBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\ChangeSet;\n\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse LogicException;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\nuse function str_replace;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class UnitOfWorkComputeChangesBench\n{\n    /** @var CmsUser[] */\n    private array|null $users = null;\n\n    private UnitOfWork|null $unitOfWork = null;\n\n    public function init(): void\n    {\n        $this->unitOfWork = EntityManagerFactory::getEntityManager([])->getUnitOfWork();\n\n        for ($i = 1; $i <= 100; ++$i) {\n            $user           = new CmsUser();\n            $user->id       = $i;\n            $user->status   = 'user';\n            $user->username = 'user' . $i;\n            $user->name     = 'Mr.Smith-' . $i;\n            $this->users[]  = $user;\n\n            $this->unitOfWork->registerManaged(\n                $user,\n                ['id' => $i],\n                [\n                    'id'       => $user->id,\n                    'status'   => $user->status,\n                    'username' => $user->username,\n                    'name'     => $user->name,\n                    'address'  => $user->address,\n                    'email'    => $user->email,\n                ],\n            );\n        }\n\n        $this->unitOfWork->computeChangeSets();\n\n        if ($this->unitOfWork->getScheduledEntityUpdates()) {\n            throw new LogicException('Unit of work should be clean at this stage');\n        }\n\n        foreach ($this->users as $user) {\n            $user->status    = 'other';\n            $user->username .= '++';\n            $user->name      = str_replace('Mr.', 'Mrs.', $user->name);\n        }\n    }\n\n    public function benchChangeSetComputation(): void\n    {\n        $this->unitOfWork->computeChangeSets();\n    }\n}\n"
  },
  {
    "path": "tests/Performance/EntityManagerFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver\\PDO\\SQLite\\Driver;\nuse Doctrine\\DBAL\\DriverManager;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\TestUtil;\n\nuse function array_map;\n\nfinal class EntityManagerFactory\n{\n    public static function getEntityManager(array $schemaClassNames): EntityManagerInterface\n    {\n        $config = new Configuration();\n\n        TestUtil::configureProxies($config);\n        $config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);\n        $config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver([\n            __DIR__ . '/../Tests/Models/Cache',\n            __DIR__ . '/../Tests/Models/GeoNames',\n        ]));\n\n        $entityManager = new EntityManager(\n            DriverManager::getConnection([\n                'driverClass' => Driver::class,\n                'memory'      => true,\n            ], $config),\n            $config,\n        );\n\n        (new SchemaTool($entityManager))\n            ->createSchema(array_map([$entityManager, 'getClassMetadata'], $schemaClassNames));\n\n        return $entityManager;\n    }\n\n    public static function makeEntityManagerWithNoResultsConnection(): EntityManagerInterface\n    {\n        $config = new Configuration();\n\n        TestUtil::configureProxies($config);\n        $config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);\n        $config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver([\n            __DIR__ . '/../Tests/Models/Cache',\n            __DIR__ . '/../Tests/Models/Generic',\n            __DIR__ . '/../Tests/Models/GeoNames',\n        ]));\n\n        // A connection that doesn't really do anything\n        $connection = new class ([], new Driver(), null, new EventManager()) extends Connection\n        {\n            /** {@inheritDoc} */\n            public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile|null $qcp = null): Result\n            {\n                return ArrayResultFactory::createWrapperResultFromArray([], $this);\n            }\n        };\n\n        return new EntityManager($connection, $config);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/MixedQueryFetchJoinArrayHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ArrayHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class MixedQueryFetchJoinArrayHydrationPerformanceBench\n{\n    private ArrayHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'          => '1',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'ROMANB',\n                'p__phonenumber' => '42',\n            ],\n            [\n                'u__id'          => '1',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'ROMANB',\n                'p__phonenumber' => '43',\n            ],\n            [\n                'u__id'          => '2',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'JWAGE',\n                'p__phonenumber' => '91',\n            ],\n        ];\n\n        for ($i = 4; $i < 10000; ++$i) {\n            $resultSet[] = [\n                'u__id'          => $i,\n                'u__status'      => 'developer',\n                'u__username'    => 'jwage',\n                'u__name'        => 'Jonathan',\n                'sclr0'          => 'JWAGE' . $i,\n                'p__phonenumber' => '91',\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ArrayHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n        $this->rsm->addScalarResult('sclr0', 'nameUpper');\n        $this->rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/MixedQueryFetchJoinFullObjectHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class MixedQueryFetchJoinFullObjectHydrationPerformanceBench\n{\n    private ObjectHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'          => '1',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id'          => '1',\n            ],\n        ];\n\n        for ($i = 2; $i < 2000; ++$i) {\n            $resultSet[] = [\n                'u__id'          => $i,\n                'u__status'      => 'developer',\n                'u__username'    => 'jwage',\n                'u__name'        => 'Jonathan',\n                'sclr0'          => 'JWAGE' . $i,\n                'p__phonenumber' => '91',\n                'a__id'          => $i,\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n        $this->rsm->addScalarResult('sclr0', 'nameUpper');\n        $this->rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address');\n        $this->rsm->addFieldResult('a', 'a__id', 'id');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class MixedQueryFetchJoinPartialObjectHydrationPerformanceBench\n{\n    private ObjectHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'          => '1',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id'       => '1',\n            ],\n            [\n                'u__id'          => '1',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id'       => '1',\n            ],\n            [\n                'u__id'          => '2',\n                'u__status'      => 'developer',\n                'u__username'    => 'romanb',\n                'u__name'        => 'Roman',\n                'sclr0'          => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id'       => '1',\n            ],\n        ];\n\n        for ($i = 4; $i < 2000; ++$i) {\n            $resultSet[] = [\n                'u__id'          => $i,\n                'u__status'      => 'developer',\n                'u__username'    => 'jwage',\n                'u__name'        => 'Jonathan',\n                'sclr0'          => 'JWAGE' . $i,\n                'p__phonenumber' => '91',\n                'a__id'       => '1',\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n        $this->rsm->addScalarResult('sclr0', 'nameUpper');\n        $this->rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address');\n        $this->rsm->addFieldResult('a', 'a__id', 'id');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleHydrationBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleHydrationBench\n{\n    private EntityManagerInterface|null $entityManager = null;\n\n    private EntityRepository|null $repository = null;\n\n    public function init(): void\n    {\n        $this->entityManager = EntityManagerFactory::getEntityManager([\n            CmsUser::class,\n            CmsPhonenumber::class,\n            CmsAddress::class,\n            CmsEmail::class,\n            CmsGroup::class,\n            CmsTag::class,\n            CmsArticle::class,\n            CmsComment::class,\n        ]);\n\n        for ($i = 2; $i < 10000; ++$i) {\n            $user = new CmsUser();\n\n            $user->status   = 'developer';\n            $user->username = 'jwage' . $i;\n            $user->name     = 'Jonathan';\n\n            $this->entityManager->persist($user);\n        }\n\n        $this->entityManager->flush();\n        $this->entityManager->clear();\n\n        $this->repository = $this->entityManager->getRepository(CmsUser::class);\n    }\n\n    public function benchHydration(): void\n    {\n        $this->repository->findAll();\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleInsertPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\CMS;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleInsertPerformanceBench\n{\n    private EntityManagerInterface|null $entityManager = null;\n\n    /** @var CMS\\CmsUser[] */\n    private array|null $users = null;\n\n    private string|null $tableName = null;\n\n    public function init(): void\n    {\n        $this->entityManager = EntityManagerFactory::getEntityManager([\n            CmsUser::class,\n            CmsPhonenumber::class,\n            CmsAddress::class,\n            CmsEmail::class,\n            CmsGroup::class,\n            CmsTag::class,\n            CmsArticle::class,\n            CmsComment::class,\n        ]);\n\n        for ($i = 1; $i <= 10000; ++$i) {\n            $user           = new CmsUser();\n            $user->status   = 'user';\n            $user->username = 'user' . $i;\n            $user->name     = 'Mr.Smith-' . $i;\n\n            $this->users[$i] = $user;\n        }\n\n        $this->tableName = $this->entityManager->getClassMetadata(CmsUser::class)->getTableName();\n    }\n\n    public function benchHydration(): void\n    {\n        // Yes, this is a lot of overhead, but I have no better solution other than\n        // completely mocking out the DB, which would be silly (query impact is\n        // necessarily part of our benchmarks)\n        $this->entityManager->getConnection()->executeStatement('DELETE FROM ' . $this->tableName);\n\n        foreach ($this->users as $key => $user) {\n            $this->entityManager->persist($user);\n\n            if (! $key % 20) {\n                $this->entityManager->flush();\n                $this->entityManager->clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ArrayHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleQueryArrayHydrationPerformanceBench\n{\n    private ArrayHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n            [\n                'u__id'       => '2',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n        ];\n\n        for ($i = 4; $i < 10000; ++$i) {\n            $resultSet[] = [\n                'u__id'       => $i,\n                'u__status'   => 'developer',\n                'u__username' => 'jwage',\n                'u__name'     => 'Jonathan',\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ArrayHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleQueryFullObjectHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleQueryFullObjectHydrationPerformanceBench\n{\n    private ObjectHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n                'a__id'       => '1',\n            ],\n        ];\n\n        for ($i = 2; $i < 10000; ++$i) {\n            $resultSet[] = [\n                'u__id'       => $i,\n                'u__status'   => 'developer',\n                'u__username' => 'jwage',\n                'u__name'     => 'Jonathan',\n                'a__id'       => $i,\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n        $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address');\n        $this->rsm->addFieldResult('a', 'a__id', 'id');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleQueryPartialObjectHydrationPerformanceBench\n{\n    private ObjectHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n                'a__id'       => '1',\n            ],\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n                'a__id'       => '1',\n            ],\n            [\n                'u__id'       => '2',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n                'a__id'       => '1',\n            ],\n        ];\n\n        for ($i = 4; $i < 10000; ++$i) {\n            $resultSet[] = [\n                'u__id'       => $i,\n                'u__status'   => 'developer',\n                'u__username' => 'jwage',\n                'u__name'     => 'Jonathan',\n                'a__id'       => '1',\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n        $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address');\n        $this->rsm->addFieldResult('a', 'a__id', 'id');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SimpleQueryScalarHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\Internal\\Hydration\\ScalarHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SimpleQueryScalarHydrationPerformanceBench\n{\n    private ScalarHydrator|null $hydrator = null;\n\n    private ResultSetMapping|null $rsm = null;\n\n    private Result|null $result = null;\n\n    public function init(): void\n    {\n        $resultSet = [\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n            [\n                'u__id'       => '1',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n            [\n                'u__id'       => '2',\n                'u__status'   => 'developer',\n                'u__username' => 'romanb',\n                'u__name'     => 'Roman',\n            ],\n        ];\n\n        for ($i = 4; $i < 10000; ++$i) {\n            $resultSet[] = [\n                'u__id'       => $i,\n                'u__status'   => 'developer',\n                'u__username' => 'jwage',\n                'u__name'     => 'Jonathan',\n            ];\n        }\n\n        $this->result   = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $this->hydrator = new ScalarHydrator(EntityManagerFactory::getEntityManager([]));\n        $this->rsm      = new ResultSetMapping();\n\n        $this->rsm->addEntityResult(CmsUser::class, 'u');\n        $this->rsm->addFieldResult('u', 'u__id', 'id');\n        $this->rsm->addFieldResult('u', 'u__status', 'status');\n        $this->rsm->addFieldResult('u', 'u__username', 'username');\n        $this->rsm->addFieldResult('u', 'u__name', 'name');\n    }\n\n    public function benchHydration(): void\n    {\n        $this->hydrator->hydrateAll($this->result, $this->rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SingleTableInheritanceHydrationPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\Company\\CompanyCar;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEvent;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyOrganization;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\Company\\CompanyRaffle;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SingleTableInheritanceHydrationPerformanceBench\n{\n    private EntityRepository|null $contractsRepository = null;\n\n    private EntityRepository|null $fixContractsRepository = null;\n\n    private EntityRepository|null $flexContractRepository = null;\n\n    private EntityRepository|null $ultraContractRepository = null;\n\n    public function init(): void\n    {\n        $entityManager = EntityManagerFactory::getEntityManager([\n            CompanyPerson::class,\n            CompanyEmployee::class,\n            CompanyManager::class,\n            CompanyOrganization::class,\n            CompanyEvent::class,\n            CompanyAuction::class,\n            CompanyRaffle::class,\n            CompanyCar::class,\n            CompanyContract::class,\n        ]);\n\n        $this->contractsRepository     = $entityManager->getRepository(CompanyContract::class);\n        $this->fixContractsRepository  = $entityManager->getRepository(CompanyFixContract::class);\n        $this->flexContractRepository  = $entityManager->getRepository(CompanyFlexContract::class);\n        $this->ultraContractRepository = $entityManager->getRepository(CompanyFlexUltraContract::class);\n\n        $person = new CompanyEmployee();\n        $person->setName('Poor Sales Guy');\n        $person->setDepartment('Sales');\n        $person->setSalary(100);\n        $entityManager->persist($person);\n\n        for ($i = 0; $i < 33; $i++) {\n            $fixContract   = new CompanyFixContract();\n            $flexContract  = new CompanyFlexContract();\n            $ultraContract = new CompanyFlexUltraContract();\n\n            $fixContract->setFixPrice(1000);\n            $fixContract->setSalesPerson($person);\n            $fixContract->markCompleted();\n\n            $flexContract->setSalesPerson($person);\n            $flexContract->setHoursWorked(100);\n            $flexContract->setPricePerHour(100);\n            $flexContract->markCompleted();\n\n            $ultraContract->setSalesPerson($person);\n            $ultraContract->setHoursWorked(150);\n            $ultraContract->setPricePerHour(150);\n            $ultraContract->setMaxPrice(7000);\n\n            $entityManager->persist($fixContract);\n            $entityManager->persist($flexContract);\n            $entityManager->persist($ultraContract);\n        }\n\n        $entityManager->flush();\n        $entityManager->clear();\n    }\n\n    public function benchHydrateFixContracts(): void\n    {\n        $this->fixContractsRepository->findAll();\n    }\n\n    public function benchHydrateFlexContracts(): void\n    {\n        $this->flexContractRepository->findAll();\n    }\n\n    public function benchHydrateUltraContracts(): void\n    {\n        $this->ultraContractRepository->findAll();\n    }\n\n    public function benchHydrateAllContracts(): void\n    {\n        $this->contractsRepository->findAll();\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Hydration/SingleTableInheritanceInsertPerformanceBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Hydration;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\Company;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\Company\\CompanyCar;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEvent;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyOrganization;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\Company\\CompanyRaffle;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\nuse function array_map;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class SingleTableInheritanceInsertPerformanceBench\n{\n    private EntityManagerInterface|null $entityManager = null;\n\n    /** @var Company\\CompanyFixContract[] */\n    private array $fixContracts = [];\n\n    /** @var Company\\CompanyFlexContract[] */\n    private array $flexContracts = [];\n\n    /** @var Company\\CompanyFlexUltraContract[] */\n    private array $ultraContracts = [];\n\n    public function init(): void\n    {\n        $this->entityManager = EntityManagerFactory::getEntityManager([\n            CompanyPerson::class,\n            CompanyEmployee::class,\n            CompanyManager::class,\n            CompanyOrganization::class,\n            CompanyEvent::class,\n            CompanyAuction::class,\n            CompanyRaffle::class,\n            CompanyCar::class,\n            CompanyContract::class,\n        ]);\n\n        $person = new CompanyEmployee();\n        $person->setName('Poor Sales Guy');\n        $person->setDepartment('Sales');\n        $person->setSalary(100);\n        $this->entityManager->persist($person);\n\n        for ($i = 0; $i < 33; $i++) {\n            $this->fixContracts[$i] = new CompanyFixContract();\n            $this->fixContracts[$i]->setFixPrice(1000);\n            $this->fixContracts[$i]->setSalesPerson($person);\n            $this->fixContracts[$i]->markCompleted();\n\n            $this->flexContracts[$i] = new CompanyFlexContract();\n            $this->flexContracts[$i]->setSalesPerson($person);\n            $this->flexContracts[$i]->setHoursWorked(100);\n            $this->flexContracts[$i]->setPricePerHour(100);\n            $this->flexContracts[$i]->markCompleted();\n\n            $this->ultraContracts[$i] = new CompanyFlexUltraContract();\n            $this->ultraContracts[$i]->setSalesPerson($person);\n            $this->ultraContracts[$i]->setHoursWorked(150);\n            $this->ultraContracts[$i]->setPricePerHour(150);\n            $this->ultraContracts[$i]->setMaxPrice(7000);\n        }\n    }\n\n    public function benchInsertFixContracts(): void\n    {\n        array_map([$this->entityManager, 'persist'], $this->fixContracts);\n        $this->entityManager->flush();\n    }\n\n    public function benchInsertFlexContracts(): void\n    {\n        array_map([$this->entityManager, 'persist'], $this->flexContracts);\n        $this->entityManager->flush();\n    }\n\n    public function benchInsertUltraContracts(): void\n    {\n        array_map([$this->entityManager, 'persist'], $this->ultraContracts);\n        $this->entityManager->flush();\n    }\n\n    public function benchInsertAllContracts(): void\n    {\n        array_map([$this->entityManager, 'persist'], $this->fixContracts);\n        array_map([$this->entityManager, 'persist'], $this->flexContracts);\n        array_map([$this->entityManager, 'persist'], $this->ultraContracts);\n        $this->entityManager->flush();\n    }\n}\n"
  },
  {
    "path": "tests/Performance/LazyLoading/ProxyInitializationTimeBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\LazyLoading;\n\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\Proxy\\InternalProxy as Proxy;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Performance\\Mock\\NonProxyLoadingEntityManager;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmployee;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class ProxyInitializationTimeBench\n{\n    /** @var Proxy[] */\n    private array|null $cmsUsers = null;\n\n    /** @var Proxy[] */\n    private array|null $cmsEmployees = null;\n\n    /** @var Proxy[] */\n    private array|null $initializedUsers = null;\n\n    /** @var Proxy[] */\n    private array|null $initializedEmployees = null;\n\n    private EntityManager $em;\n\n    public function init(): void\n    {\n        $proxyFactory = (new NonProxyLoadingEntityManager($this->em = EntityManagerFactory::getEntityManager([])))\n            ->getProxyFactory();\n\n        for ($i = 0; $i < 10000; ++$i) {\n            $this->cmsUsers[$i]             = $proxyFactory->getProxy(CmsUser::class, ['id' => $i]);\n            $this->cmsEmployees[$i]         = $proxyFactory->getProxy(CmsEmployee::class, ['id' => $i]);\n            $this->initializedUsers[$i]     = $proxyFactory->getProxy(CmsUser::class, ['id' => $i]);\n            $this->initializedEmployees[$i] = $proxyFactory->getProxy(CmsEmployee::class, ['id' => $i]);\n\n            $this->em->getUnitOfWork()->initializeObject($this->initializedUsers[$i]);\n            $this->em->getUnitOfWork()->initializeObject($this->initializedEmployees[$i]);\n        }\n    }\n\n    public function benchCmsUserInitialization(): void\n    {\n        foreach ($this->cmsUsers as $proxy) {\n            $this->em->getUnitOfWork()->initializeObject($proxy);\n        }\n    }\n\n    public function benchCmsEmployeeInitialization(): void\n    {\n        foreach ($this->cmsEmployees as $proxy) {\n            $this->em->getUnitOfWork()->initializeObject($proxy);\n        }\n    }\n\n    public function benchInitializationOfAlreadyInitializedCmsUsers(): void\n    {\n        foreach ($this->initializedUsers as $proxy) {\n            $this->em->getUnitOfWork()->initializeObject($proxy);\n        }\n    }\n\n    public function benchInitializationOfAlreadyInitializedCmsEmployees(): void\n    {\n        foreach ($this->initializedEmployees as $proxy) {\n            $this->em->getUnitOfWork()->initializeObject($proxy);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Performance/LazyLoading/ProxyInstantiationTimeBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\LazyLoading;\n\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmployee;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class ProxyInstantiationTimeBench\n{\n    /** @var ProxyFactory */\n    private $proxyFactory;\n\n    public function init(): void\n    {\n        $this->proxyFactory = EntityManagerFactory::getEntityManager([])->getProxyFactory();\n    }\n\n    public function benchCmsUserInstantiation(): void\n    {\n        for ($i = 0; $i < 100000; ++$i) {\n            $this->proxyFactory->getProxy(CmsUser::class, ['id' => $i]);\n        }\n    }\n\n    public function benchCmsEmployeeInstantiation(): void\n    {\n        for ($i = 0; $i < 100000; ++$i) {\n            $this->proxyFactory->getProxy(CmsEmployee::class, ['id' => $i]);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Mock/NonLoadingPersister.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Mock;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\n\n/**\n * A persister that doesn't actually load given objects\n */\nclass NonLoadingPersister extends BasicEntityPersister\n{\n    public function __construct(\n        ClassMetadata $class,\n    ) {\n        $this->class = $class;\n    }\n\n    public function loadById(array $identifier, object|null $entity = null): object|null\n    {\n        return $entity ?? new ($this->class->name)();\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Mock/NonProxyLoadingEntityManager.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Mock;\n\nuse DateTimeInterface;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\NativeQuery;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\QueryBuilder;\nuse Doctrine\\ORM\\UnitOfWork;\n\n/**\n * An entity manager mock that prevents lazy-loading of proxies\n */\nclass NonProxyLoadingEntityManager implements EntityManagerInterface\n{\n    public function __construct(private readonly EntityManagerInterface $realEntityManager)\n    {\n    }\n\n    public function getProxyFactory(): ProxyFactory\n    {\n        $config = $this->realEntityManager->getConfiguration();\n\n        return new ProxyFactory(\n            $this,\n            $config->getProxyDir(),\n            $config->getProxyNamespace(),\n            $config->getAutoGenerateProxyClasses(),\n        );\n    }\n\n    public function getMetadataFactory(): ClassMetadataFactory\n    {\n        return $this->realEntityManager->getMetadataFactory();\n    }\n\n    public function getClassMetadata(string $className): ClassMetadata\n    {\n        return $this->realEntityManager->getClassMetadata($className);\n    }\n\n    public function getUnitOfWork(): UnitOfWork\n    {\n        return new NonProxyLoadingUnitOfWork($this);\n    }\n\n    public function getCache(): Cache|null\n    {\n        return $this->realEntityManager->getCache();\n    }\n\n    public function getConnection(): Connection\n    {\n        return $this->realEntityManager->getConnection();\n    }\n\n    public function getExpressionBuilder(): Expr\n    {\n        return $this->realEntityManager->getExpressionBuilder();\n    }\n\n    public function beginTransaction(): void\n    {\n        $this->realEntityManager->beginTransaction();\n    }\n\n    public function wrapInTransaction(callable $func): mixed\n    {\n        return $this->realEntityManager->wrapInTransaction($func);\n    }\n\n    public function commit(): void\n    {\n        $this->realEntityManager->commit();\n    }\n\n    public function rollback(): void\n    {\n        $this->realEntityManager->rollback();\n    }\n\n    public function createQuery(string $dql = ''): Query\n    {\n        return $this->realEntityManager->createQuery($dql);\n    }\n\n    public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery\n    {\n        return $this->realEntityManager->createNativeQuery($sql, $rsm);\n    }\n\n    public function createQueryBuilder(): QueryBuilder\n    {\n        return $this->realEntityManager->createQueryBuilder();\n    }\n\n    public function getReference(string $entityName, mixed $id): object|null\n    {\n        return $this->realEntityManager->getReference($entityName, $id);\n    }\n\n    public function close(): void\n    {\n        $this->realEntityManager->close();\n    }\n\n    public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void\n    {\n        $this->realEntityManager->lock($entity, $lockMode, $lockVersion);\n    }\n\n    public function getEventManager(): EventManager\n    {\n        return $this->realEntityManager->getEventManager();\n    }\n\n    public function getConfiguration(): Configuration\n    {\n        return $this->realEntityManager->getConfiguration();\n    }\n\n    public function isOpen(): bool\n    {\n        return $this->realEntityManager->isOpen();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function newHydrator($hydrationMode): AbstractHydrator\n    {\n        return $this->realEntityManager->newHydrator($hydrationMode);\n    }\n\n    public function getFilters(): FilterCollection\n    {\n        return $this->realEntityManager->getFilters();\n    }\n\n    public function isFiltersStateClean(): bool\n    {\n        return $this->realEntityManager->isFiltersStateClean();\n    }\n\n    public function hasFilters(): bool\n    {\n        return $this->realEntityManager->hasFilters();\n    }\n\n    public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null\n    {\n        return $this->realEntityManager->find($className, $id, $lockMode, $lockVersion);\n    }\n\n    public function persist(object $object): void\n    {\n        $this->realEntityManager->persist($object);\n    }\n\n    public function remove(object $object): void\n    {\n        $this->realEntityManager->remove($object);\n    }\n\n    public function clear(): void\n    {\n        $this->realEntityManager->clear();\n    }\n\n    public function detach(object $object): void\n    {\n        $this->realEntityManager->detach($object);\n    }\n\n    public function refresh(object $object, LockMode|int|null $lockMode = null): void\n    {\n        $this->realEntityManager->refresh($object, $lockMode);\n    }\n\n    public function flush(): void\n    {\n        $this->realEntityManager->flush();\n    }\n\n    public function getRepository(string $className): EntityRepository\n    {\n        return $this->realEntityManager->getRepository($className);\n    }\n\n    public function initializeObject(object $obj): void\n    {\n        $this->realEntityManager->initializeObject($obj);\n    }\n\n    public function contains(object $object): bool\n    {\n        return $this->realEntityManager->contains($object);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function isUninitializedObject($value): bool\n    {\n        return $this->realEntityManager->isUninitializedObject($value);\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Mock/NonProxyLoadingUnitOfWork.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Mock;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\UnitOfWork;\n\n/**\n * An unit of work mock that prevents lazy-loading of proxies\n */\nclass NonProxyLoadingUnitOfWork extends UnitOfWork\n{\n    /** @var array<class-string, NonLoadingPersister> */\n    private array $entityPersisters = [];\n\n    public function __construct(\n        private EntityManagerInterface $entityManager,\n    ) {\n    }\n\n    public function getEntityPersister(string $entityName): NonLoadingPersister\n    {\n        return $this->entityPersisters[$entityName]\n            ??= new NonLoadingPersister($this->entityManager->getClassMetadata($entityName));\n    }\n}\n"
  },
  {
    "path": "tests/Performance/Query/QueryBoundParameterProcessingBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Performance\\Query;\n\nuse DateTime;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Performance\\EntityManagerFactory;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\BeforeMethods;\n\nuse function range;\n\n/** @BeforeMethods({\"init\"}) */\nfinal class QueryBoundParameterProcessingBench\n{\n    private Query|null $parsedQueryWithInferredParameterType = null;\n\n    private Query|null $parsedQueryWithDeclaredParameterType = null;\n\n    public function init(): void\n    {\n        $entityManager = EntityManagerFactory::makeEntityManagerWithNoResultsConnection();\n\n        // Note: binding a lot of parameters because DQL operations are noisy due to hydrators and other components\n        //       kicking in, so we make the parameter operations more noticeable.\n        $dql = <<<'DQL'\nSELECT e\nFROM Doctrine\\Tests\\Models\\Generic\\DateTimeModel e\nWHERE\n    e.datetime = :parameter1\n    OR\n    e.datetime = :parameter2\n    OR\n    e.datetime = :parameter3\n    OR\n    e.datetime = :parameter4\n    OR\n    e.datetime = :parameter5\n    OR\n    e.datetime = :parameter6\n    OR\n    e.datetime = :parameter7\n    OR\n    e.datetime = :parameter8\n    OR\n    e.datetime = :parameter9\n    OR\n    e.datetime = :parameter10\nDQL;\n\n        $this->parsedQueryWithInferredParameterType = $entityManager->createQuery($dql);\n        $this->parsedQueryWithDeclaredParameterType = $entityManager->createQuery($dql);\n\n        foreach (range(1, 10) as $index) {\n            $this->parsedQueryWithInferredParameterType->setParameter('parameter' . $index, new DateTime());\n            $this->parsedQueryWithDeclaredParameterType->setParameter('parameter' . $index, new DateTime(), Types::DATETIME_MUTABLE);\n        }\n\n        // Force parsing upfront - we don't benchmark that bit in this scenario\n        $this->parsedQueryWithInferredParameterType->getSQL();\n        $this->parsedQueryWithDeclaredParameterType->getSQL();\n    }\n\n    public function benchExecuteParsedQueryWithInferredParameterType(): void\n    {\n        $this->parsedQueryWithInferredParameterType->execute();\n    }\n\n    public function benchExecuteParsedQueryWithDeclaredParameterType(): void\n    {\n        $this->parsedQueryWithDeclaredParameterType->execute();\n    }\n}\n"
  },
  {
    "path": "tests/README.markdown",
    "content": "# Running the Doctrine ORM Testsuite\n\nTo execute the ORM testsuite, you just need to execute this simple steps:\n\n * Clone the project from GitHub\n * Enter the ORM folder\n * Install the dependencies\n * Execute the tests\n \n All this is (normally) done with:\n\n    git clone git@github.com:doctrine/orm.git\n    cd orm\n    composer install\n    ./vendor/bin/phpunit\n\n## Pre-requisites\nDoctrine2 works on many database vendors; the tests can detect the presence of installed vendors, but you need at least one of those; the easier to install is SQLite.\n\nIf you're using Debian, or a Debian-derivate Linux distribution (like Ubuntu), you can install SQLite with:\n\n    sudo apt-get install sqlite\n\n## Testing Lock-Support\n\nThe Lock support in Doctrine 2 is tested using Gearman, which allows to run concurrent tasks in parallel.\nInstall Gearman with PHP as follows:\n\n1. Go to http://www.gearman.org and download the latest Gearman Server\n2. Compile it and then call ldconfig\n3. Start it up \"gearmand -vvvv\"\n4. Install pecl/gearman by calling \"gearman-beta\"\n\nYou can then go into `tests/` and start up two workers:\n\n    php Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php\n\nThen run the locking test-suite:\n\n    phpunit --configuration <myconfig.xml> Doctrine/Tests/ORM/Functional/Locking/GearmanLockTest.php\n\nThis can run considerable time, because it is using sleep() to test for the timing ranges of locks.\n"
  },
  {
    "path": "tests/StaticAnalysis/Mapping/class-metadata-constructor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\StaticAnalysis\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/** @template T of object */\nclass MetadataGenerator\n{\n    /**\n     * @param class-string<T> $entityName\n     *\n     * @return ClassMetadata<T>\n     */\n    public function createMetadata(string $entityName): ClassMetadata\n    {\n        return new ClassMetadata($entityName);\n    }\n}\n"
  },
  {
    "path": "tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\StaticAnalysis\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\n\n/** @template-covariant T of object */\nabstract class PaginatorFactory\n{\n    /** @var class-string<T> */\n    private $class;\n\n    /** @param class-string<T> $class */\n    final public function __construct(string $class)\n    {\n        $this->class = $class;\n    }\n\n    /** @return class-string<T> */\n    public function getClass(): string\n    {\n        return $this->class;\n    }\n\n    /** @phpstan-return Paginator<T> */\n    abstract public function createPaginator(): Paginator;\n}\n\ninterface Animal\n{\n}\n\nclass Cat implements Animal\n{\n}\n\n/** @param Paginator<Animal> $paginator */\nfunction getFirstAnimal(Paginator $paginator): Animal|null\n{\n    foreach ($paginator as $result) {\n        return $result;\n    }\n\n    return null;\n}\n\n/** @param PaginatorFactory<Cat> $catPaginatorFactory */\nfunction test(PaginatorFactory $catPaginatorFactory): Animal|null\n{\n    return getFirstAnimal($catPaginatorFactory->createPaginator());\n}\n"
  },
  {
    "path": "tests/StaticAnalysis/get-metadata.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\StaticAnalysis;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n/**\n * EntityManagerInterface::getClassMetadata() is templated only for Psalm,\n * because of limitations in PHPStan.\n *\n * @see https://github.com/phpstan/phpstan/issues/5175#issuecomment-861437050\n */\nabstract class GetMetadata\n{\n    /** @param class-string|object $class */\n    abstract public function getEntityManager(string|object $class): EntityManagerInterface;\n\n    /**\n     * @param class-string<TObject> $class\n     *\n     * @return ClassMetadata<TObject>\n     *\n     * @template TObject of object\n     */\n    public function __invoke(string $class): ClassMetadata\n    {\n        return $this->getEntityManager($class)->getClassMetadata($class);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalExtensions/Connection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalExtensions;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Configuration;\nuse Doctrine\\DBAL\\Connection as BaseConnection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Logging\\Middleware as LoggingMiddleware;\n\nclass Connection extends BaseConnection\n{\n    public QueryLog $queryLog;\n\n    public function __construct(array $params, Driver $driver, Configuration|null $config = null, EventManager|null $eventManager = null)\n    {\n        $this->queryLog = new QueryLog();\n        $logging        = new LoggingMiddleware(new SqlLogger($this->queryLog));\n        $driver         = $logging->wrap($driver);\n\n        parent::__construct($params, $driver, $config, $eventManager);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalExtensions/QueryLog.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalExtensions;\n\nfinal class QueryLog\n{\n    /** @var list<array{sql: string, params: array|null, types: array|null}> */\n    public array $queries = [];\n\n    private bool $enabled = false;\n\n    public function logQuery(string $sql, array|null $params = null, array|null $types = null): void\n    {\n        if (! $this->enabled) {\n            return;\n        }\n\n        $this->queries[] = [\n            'sql' => $sql,\n            'params' => $params,\n            'types' => $types,\n        ];\n    }\n\n    /** @return $this */\n    public function reset(): self\n    {\n        $this->enabled = false;\n        $this->queries = [];\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function enable(): self\n    {\n        $this->enabled = true;\n\n        return $this;\n    }\n\n    /** @return $this */\n    public function disable(): self\n    {\n        $this->enabled = false;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalExtensions/SqlLogger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalExtensions;\n\nuse Psr\\Log\\AbstractLogger;\n\nfinal class SqlLogger extends AbstractLogger\n{\n    public function __construct(private readonly QueryLog $queryLog)\n    {\n    }\n\n    public function log($level, $message, array $context = []): void\n    {\n        if (! isset($context['sql'])) {\n            return;\n        }\n\n        $this->queryLog->logQuery(\n            $context['sql'],\n            $context['params'] ?? null,\n            $context['types'] ?? null,\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/CustomIdObject.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Stringable;\n\nclass CustomIdObject implements Stringable\n{\n    public function __construct(public string $id)\n    {\n    }\n\n    public function __toString(): string\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/CustomIdObjectType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\n\nclass CustomIdObjectType extends Type\n{\n    public const NAME = 'CustomIdObject';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        return $value->id;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): CustomIdObject\n    {\n        return new CustomIdObject($value);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/CustomIntType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Types\\IntegerType;\n\nclass CustomIntType extends IntegerType\n{\n    public const NAME = 'custom_int_type';\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/GH8565EmployeePayloadType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\JsonType;\nuse Exception;\n\nclass GH8565EmployeePayloadType extends JsonType\n{\n    public const NAME = 'GH8565EmployeePayloadType';\n\n    public function convertToPHPValue($value, AbstractPlatform $platform): string\n    {\n        if (! isset($value['GH8565EmployeePayloadRequiredField'])) {\n            throw new Exception('GH8565EmployeePayloadType cannot be initialized without required field');\n        }\n\n        return $value['GH8565EmployeePayloadRequiredField'];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/GH8565ManagerPayloadType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\JsonType;\n\nclass GH8565ManagerPayloadType extends JsonType\n{\n    public const NAME = 'GH8565ManagerPayloadType';\n\n    public function convertToPHPValue($value, AbstractPlatform $platform): string\n    {\n        return $value;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/NegativeToPositiveType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\n\nclass NegativeToPositiveType extends Type\n{\n    public const NAME = 'negative_to_positive';\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getIntegerTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string\n    {\n        return 'ABS(' . $sqlExpr . ')';\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValueSQL($sqlExpr, $platform): string\n    {\n        return '-(' . $sqlExpr . ')';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/Rot13Type.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\n\nuse function str_rot13;\n\n/**\n * Shifts every letter by 13 places in the alphabet (ROT13 encoding).\n */\nclass Rot13Type extends Type\n{\n    /**\n     * {@inheritDoc}\n     *\n     * @param string|null $value\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string|null\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        return str_rot13($value);\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param string|null $value\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): string|null\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        return str_rot13($value);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return 'rot13';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/DbalTypes/UpperCaseStringType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\DbalTypes;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\n\nclass UpperCaseStringType extends StringType\n{\n    public const NAME = 'upper_case_string';\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string\n    {\n        return 'UPPER(' . $sqlExpr . ')';\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValueSQL($sqlExpr, $platform): string\n    {\n        return 'LOWER(' . $sqlExpr . ')';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/EventListener/CacheMetadataListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\EventListener;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\nuse function strstr;\n\nclass CacheMetadataListener\n{\n    /**\n     * Tracks which entities we have already forced caching enabled on. This is\n     * important to avoid some potential infinite-recursion issues.\n     *\n     * Key is the name of the entity, payload is unimportant.\n     *\n     * @var array<string, bool>\n     */\n    protected $enabledItems = [];\n\n    public function loadClassMetadata(LoadClassMetadataEventArgs $event): void\n    {\n        $metadata = $event->getClassMetadata();\n        $em       = $event->getObjectManager();\n\n        if (strstr($metadata->name, 'Doctrine\\Tests\\Models\\Cache')) {\n            return;\n        }\n\n        $this->enableCaching($metadata, $em);\n    }\n\n    private function isVisited(ClassMetadata $metadata): bool\n    {\n        return isset($this->enabledItems[$metadata->getName()]);\n    }\n\n    private function recordVisit(ClassMetadata $metadata): void\n    {\n        $this->enabledItems[$metadata->getName()] = true;\n    }\n\n    protected function enableCaching(ClassMetadata $metadata, EntityManagerInterface $em): void\n    {\n        if ($this->isVisited($metadata)) {\n            return; // Already handled in the past\n        }\n\n        $cache = [\n            'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE,\n        ];\n\n        if ($metadata->isVersioned) {\n            return;\n        }\n\n        $metadata->enableCache($cache);\n\n        $this->recordVisit($metadata);\n\n        // only enable association-caching when the target has already been\n        // given caching settings\n        foreach ($metadata->associationMappings as $mapping) {\n            $targetMeta = $em->getClassMetadata($mapping->targetEntity);\n            $this->enableCaching($targetMeta, $em);\n\n            if ($this->isVisited($targetMeta)) {\n                $metadata->enableAssociationCache($mapping->fieldName, $cache);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/IterableTester.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Query;\nuse PHPUnit\\Framework\\Assert;\n\nuse function is_array;\nuse function iterator_to_array;\n\nfinal class IterableTester\n{\n    public static function assertResultsAreTheSame(Query $query): void\n    {\n        $result   = $query->getResult();\n        $iterable = $query->toIterable();\n\n        Assert::assertSame($result, self::iterableToArray($iterable));\n\n        $result   = $query->getResult(AbstractQuery::HYDRATE_ARRAY);\n        $iterable = $query->toIterable([], AbstractQuery::HYDRATE_ARRAY);\n\n        Assert::assertSame($result, self::iterableToArray($iterable));\n    }\n\n    /**\n     * Copy the iterable into an array. If the iterable is already an array, return it.\n     *\n     * @return mixed[]\n     */\n    public static function iterableToArray(iterable $iterable): array\n    {\n        return is_array($iterable) ? $iterable : iterator_to_array($iterable, true);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/ArrayResultFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\DBAL\\Cache\\ArrayResult;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver\\PDO\\SQLite\\Driver;\nuse Doctrine\\DBAL\\Result;\nuse ReflectionMethod;\n\nuse function array_keys;\nuse function array_map;\nuse function array_values;\n\nfinal class ArrayResultFactory\n{\n    /** @param list<array<string, mixed>> $resultSet */\n    public static function createDriverResultFromArray(array $resultSet): ArrayResult\n    {\n        if ((new ReflectionMethod(ArrayResult::class, '__construct'))->getNumberOfRequiredParameters() < 2) {\n            // DBAL < 4.2\n            return new ArrayResult($resultSet);\n        }\n\n        // DBAL 4.2+\n        return new ArrayResult(\n            array_keys($resultSet[0] ?? []),\n            array_map(array_values(...), $resultSet),\n        );\n    }\n\n    /** @param list<array<string, mixed>> $resultSet */\n    public static function createWrapperResultFromArray(array $resultSet, Connection|null $connection = null): Result\n    {\n        return new Result(\n            self::createDriverResultFromArray($resultSet),\n            $connection ?? new Connection([], new Driver()),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/AttributeDriverFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ClassLocator;\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileClassLocator;\n\nuse function interface_exists;\n\nfinal class AttributeDriverFactory\n{\n    /** @param list<string> $paths */\n    public static function createAttributeDriver(array $paths = []): AttributeDriver\n    {\n        if (! self::isClassLocatorSupported()) {\n            // Persistence < 4.1\n            return new AttributeDriver($paths);\n        }\n\n        // Persistence >= 4.1\n        $classLocator = FileClassLocator::createFromDirectories($paths);\n\n        return new AttributeDriver($classLocator);\n    }\n\n    /** Supported since doctrine/persistence >= 4.1 */\n    public static function isClassLocatorSupported(): bool\n    {\n        return interface_exists(ClassLocator::class);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/CacheEntryMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse ArrayObject;\nuse Doctrine\\ORM\\Cache\\CacheEntry;\n\n/**\n * Cache entry mock\n */\nclass CacheEntryMock extends ArrayObject implements CacheEntry\n{\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/CacheKeyMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Cache\\CacheKey;\n\n/**\n * Cache key mock\n *\n * Used to store/retrieve entries from a cache region\n */\nclass CacheKeyMock extends CacheKey\n{\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/CacheRegionMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Region;\n\nuse function array_shift;\n\n/**\n * Cache region mock\n */\nclass CacheRegionMock implements Region\n{\n    /** @var array<string, list<array<string, mixed>>> */\n    public array $calls = [];\n\n    /** @var array<string, mixed> */\n    public array $returns = [];\n\n    public string $name = 'mock';\n\n    /**\n     * Queue a return value for a specific method invocation\n     */\n    public function addReturn(string $method, mixed $value): void\n    {\n        $this->returns[$method][] = $value;\n    }\n\n    /**\n     * Dequeue a value for a specific method invocation\n     */\n    private function getReturn(string $method, mixed $default): mixed\n    {\n        if (isset($this->returns[$method]) && ! empty($this->returns[$method])) {\n            return array_shift($this->returns[$method]);\n        }\n\n        return $default;\n    }\n\n    public function getName(): string\n    {\n        $this->calls[__FUNCTION__][] = [];\n\n        return $this->name;\n    }\n\n    public function contains(CacheKey $key): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        return $this->getReturn(__FUNCTION__, false);\n    }\n\n    public function evict(CacheKey $key): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        return $this->getReturn(__FUNCTION__, true);\n    }\n\n    public function evictAll(): bool\n    {\n        $this->calls[__FUNCTION__][] = [];\n\n        return $this->getReturn(__FUNCTION__, true);\n    }\n\n    public function get(CacheKey $key): CacheEntry|null\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        return $this->getReturn(__FUNCTION__, null);\n    }\n\n    public function getMultiple(CollectionCacheEntry $collection): array|null\n    {\n        $this->calls[__FUNCTION__][] = ['collection' => $collection];\n\n        return $this->getReturn(__FUNCTION__, null);\n    }\n\n    public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key, 'entry' => $entry];\n\n        return $this->getReturn(__FUNCTION__, true);\n    }\n\n    public function clear(): void\n    {\n        $this->calls   = [];\n        $this->returns = [];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/CompatibilityType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\DBAL\\ParameterType;\n\nuse function enum_exists;\n\nif (! enum_exists(ParameterType::class)) {\n    trait CompatibilityType\n    {\n        public function getBindingType(): int\n        {\n            return $this->doGetBindingType();\n        }\n\n        private function doGetBindingType(): int|ParameterType\n        {\n            return parent::getBindingType();\n        }\n    }\n} else {\n    trait CompatibilityType\n    {\n        public function getBindingType(): ParameterType\n        {\n            return $this->doGetBindingType();\n        }\n\n        private function doGetBindingType(): int|ParameterType\n        {\n            return parent::getBindingType();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/ConcurrentRegionMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\LockException;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Exception;\n\nuse function array_shift;\n\n/**\n * Concurrent region mock\n *\n * Used to mock a ConcurrentRegion\n */\nclass ConcurrentRegionMock implements ConcurrentRegion\n{\n    /** @phpstan-var array<string, list<array<string, mixed>>> */\n    public array $calls = [];\n\n    /** @phpstan-var array<string, list<Exception>> */\n    public array $exceptions = [];\n\n    /** @phpstan-var array<string, Lock> */\n    public array $locks = [];\n\n    public function __construct(\n        private readonly Region $region,\n    ) {\n    }\n\n    /**\n     * Dequeue an exception for a specific method invocation\n     */\n    private function throwException(string $method): void\n    {\n        if (isset($this->exceptions[$method]) && ! empty($this->exceptions[$method])) {\n            $exception = array_shift($this->exceptions[$method]);\n\n            if ($exception !== null) {\n                throw $exception;\n            }\n        }\n    }\n\n    /**\n     * Queue an exception for the next method invocation\n     */\n    public function addException(string $method, Exception $e): void\n    {\n        $this->exceptions[$method][] = $e;\n    }\n\n    /**\n     * Locks a specific cache entry\n     */\n    public function setLock(CacheKey $key, Lock $lock): void\n    {\n        $this->locks[$key->hash] = $lock;\n    }\n\n    public function contains(CacheKey $key): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        if (isset($this->locks[$key->hash])) {\n            return false;\n        }\n\n        $this->throwException(__FUNCTION__);\n\n        return $this->region->contains($key);\n    }\n\n    public function evict(CacheKey $key): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        $this->throwException(__FUNCTION__);\n\n        return $this->region->evict($key);\n    }\n\n    public function evictAll(): bool\n    {\n        $this->calls[__FUNCTION__][] = [];\n\n        $this->throwException(__FUNCTION__);\n\n        return $this->region->evictAll();\n    }\n\n    public function get(CacheKey $key): CacheEntry|null\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        $this->throwException(__FUNCTION__);\n\n        if (isset($this->locks[$key->hash])) {\n            return null;\n        }\n\n        return $this->region->get($key);\n    }\n\n    public function getMultiple(CollectionCacheEntry $collection): array|null\n    {\n        $this->calls[__FUNCTION__][] = ['collection' => $collection];\n\n        $this->throwException(__FUNCTION__);\n\n        return $this->region->getMultiple($collection);\n    }\n\n    public function getName(): string\n    {\n        $this->calls[__FUNCTION__][] = [];\n\n        $this->throwException(__FUNCTION__);\n\n        return $this->region->getName();\n    }\n\n    public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key, 'entry' => $entry];\n\n        $this->throwException(__FUNCTION__);\n\n        if (isset($this->locks[$key->hash])) {\n            if ($lock !== null && $this->locks[$key->hash]->value === $lock->value) {\n                return $this->region->put($key, $entry);\n            }\n\n            return false;\n        }\n\n        return $this->region->put($key, $entry);\n    }\n\n    public function lock(CacheKey $key): Lock|null\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n\n        $this->throwException(__FUNCTION__);\n\n        if (isset($this->locks[$key->hash])) {\n            return null;\n        }\n\n        return $this->locks[$key->hash] = Lock::createLockRead();\n    }\n\n    public function unlock(CacheKey $key, Lock $lock): bool\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key, 'lock' => $lock];\n\n        $this->throwException(__FUNCTION__);\n\n        if (! isset($this->locks[$key->hash])) {\n            return false;\n        }\n\n        if ($this->locks[$key->hash]->value !== $lock->value) {\n            throw new LockException('unexpected lock value');\n        }\n\n        unset($this->locks[$key->hash]);\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/CustomTreeWalkerJoin.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Query\\AST\\IdentificationVariableDeclaration;\nuse Doctrine\\ORM\\Query\\AST\\Join;\nuse Doctrine\\ORM\\Query\\AST\\JoinAssociationDeclaration;\nuse Doctrine\\ORM\\Query\\AST\\JoinAssociationPathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\n\nclass CustomTreeWalkerJoin extends TreeWalkerAdapter\n{\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        foreach ($selectStatement->fromClause->identificationVariableDeclarations as $identificationVariableDeclaration) {\n            $rangeVariableDecl = $identificationVariableDeclaration->rangeVariableDeclaration;\n\n            if ($rangeVariableDecl->abstractSchemaName !== CmsUser::class) {\n                continue;\n            }\n\n            $this->modifySelectStatement($selectStatement, $identificationVariableDeclaration);\n        }\n    }\n\n    private function modifySelectStatement(SelectStatement $selectStatement, IdentificationVariableDeclaration $identificationVariableDecl): void\n    {\n        $rangeVariableDecl       = $identificationVariableDecl->rangeVariableDeclaration;\n        $joinAssocPathExpression = new JoinAssociationPathExpression($rangeVariableDecl->aliasIdentificationVariable, 'address');\n        $joinAssocDeclaration    = new JoinAssociationDeclaration($joinAssocPathExpression, $rangeVariableDecl->aliasIdentificationVariable . 'a', null);\n        $join                    = new Join(Join::JOIN_TYPE_LEFT, $joinAssocDeclaration);\n        $selectExpression        = new SelectExpression($rangeVariableDecl->aliasIdentificationVariable . 'a', null, false);\n\n        $identificationVariableDecl->joins[]                = $join;\n        $selectStatement->selectClause->selectExpressions[] = $selectExpression;\n\n        $entityManager   = $this->_getQuery()->getEntityManager();\n        $userMetadata    = $entityManager->getClassMetadata(CmsUser::class);\n        $addressMetadata = $entityManager->getClassMetadata(CmsAddress::class);\n\n        $this->setQueryComponent(\n            $rangeVariableDecl->aliasIdentificationVariable . 'a',\n            [\n                'metadata'     => $addressMetadata,\n                'parent'       => $rangeVariableDecl->aliasIdentificationVariable,\n                'relation'     => $userMetadata->getAssociationMapping('address'),\n                'map'          => null,\n                'nestingLevel' => 0,\n                'token'        => null,\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/EntityManagerMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\TestUtil;\n\n/**\n * Special EntityManager mock used for testing purposes.\n */\nclass EntityManagerMock extends EntityManager\n{\n    private UnitOfWork|null $_uowMock            = null;\n    private ProxyFactory|null $_proxyFactoryMock = null;\n\n    public function __construct(Connection $conn, Configuration|null $config = null, EventManager|null $eventManager = null)\n    {\n        if ($config === null) {\n            $config = new Configuration();\n            TestUtil::configureProxies($config);\n            $config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver());\n        }\n\n        parent::__construct($conn, $config, $eventManager);\n    }\n\n    public function getUnitOfWork(): UnitOfWork\n    {\n        return $this->_uowMock ?? parent::getUnitOfWork();\n    }\n\n    /* Mock API */\n\n    /**\n     * Sets a (mock) UnitOfWork that will be returned when getUnitOfWork() is called.\n     */\n    public function setUnitOfWork(UnitOfWork $uow): void\n    {\n        $this->_uowMock = $uow;\n    }\n\n    public function setProxyFactory(ProxyFactory $proxyFactory): void\n    {\n        $this->_proxyFactoryMock = $proxyFactory;\n    }\n\n    public function getProxyFactory(): ProxyFactory\n    {\n        return $this->_proxyFactoryMock ?? parent::getProxyFactory();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/EntityPersisterMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\n\n/**\n * EntityPersister implementation used for mocking during tests.\n */\nclass EntityPersisterMock extends BasicEntityPersister\n{\n    /** @var int<0, max> */\n    private int $countOfExecuteInsertCalls  = 0;\n    private array $inserts                  = [];\n    private array $updates                  = [];\n    private array $deletes                  = [];\n    private int $identityColumnValueCounter = 0;\n    private int|null $mockIdGeneratorType   = null;\n\n    /** @phpstan-var list<array{generatedId: int, entity: object}> */\n    private array $postInsertIds = [];\n\n    private bool $existsCalled = false;\n\n    public function addInsert(object $entity): void\n    {\n        $this->inserts[] = $entity;\n        if ($this->mockIdGeneratorType !== ClassMetadata::GENERATOR_TYPE_IDENTITY && ! $this->class->isIdGeneratorIdentity()) {\n            return;\n        }\n\n        $id                    = $this->identityColumnValueCounter++;\n        $this->postInsertIds[] = [\n            'generatedId' => $id,\n            'entity' => $entity,\n        ];\n    }\n\n    public function executeInserts(): void\n    {\n        $this->countOfExecuteInsertCalls += 1;\n\n        foreach ($this->postInsertIds as $item) {\n            $this->em->getUnitOfWork()->assignPostInsertId($item['entity'], $item['generatedId']);\n        }\n    }\n\n    public function setMockIdGeneratorType(int $genType): void\n    {\n        $this->mockIdGeneratorType = $genType;\n    }\n\n    public function update(object $entity): void\n    {\n        $this->updates[] = $entity;\n    }\n\n    public function exists(object $entity, Criteria|null $extraConditions = null): bool\n    {\n        $this->existsCalled = true;\n\n        return false;\n    }\n\n    public function delete(object $entity): bool\n    {\n        $this->deletes[] = $entity;\n\n        return true;\n    }\n\n    public function getInserts(): array\n    {\n        return $this->inserts;\n    }\n\n    public function getUpdates(): array\n    {\n        return $this->updates;\n    }\n\n    public function getDeletes(): array\n    {\n        return $this->deletes;\n    }\n\n    public function reset(): void\n    {\n        $this->countOfExecuteInsertCalls  = 0;\n        $this->existsCalled               = false;\n        $this->identityColumnValueCounter = 0;\n        $this->inserts                    = [];\n        $this->updates                    = [];\n        $this->deletes                    = [];\n    }\n\n    public function isExistsCalled(): bool\n    {\n        return $this->existsCalled;\n    }\n\n    /** @return int<0, max> */\n    public function countOfExecuteInsertCalls(): int\n    {\n        return $this->countOfExecuteInsertCalls;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/ExceptionConverterMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\DBAL\\Driver\\API\\ExceptionConverter;\nuse Doctrine\\DBAL\\Driver\\Exception;\nuse Doctrine\\DBAL\\Exception\\DriverException;\nuse Doctrine\\DBAL\\Query;\n\nclass ExceptionConverterMock implements ExceptionConverter\n{\n    public function convert(Exception $exception, Query|null $query): DriverException\n    {\n        return new DriverException($exception, $query);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/MetadataDriverMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\n\n/**\n * Mock class for MappingDriver.\n */\nclass MetadataDriverMock implements MappingDriver\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function loadMetadataForClass($className, ClassMetadata $metadata): void\n    {\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function isTransient($className): bool\n    {\n        return false;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getAllClassNames(): array\n    {\n        return [];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/NullSqlWalker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Query\\AST;\nuse Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\PreparedExecutorFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\n\n/**\n * SqlWalker implementation that does not produce SQL.\n */\nfinal class NullSqlWalker extends SqlOutputWalker\n{\n    public function walkSelectStatement(AST\\SelectStatement $selectStatement): string\n    {\n        return '';\n    }\n\n    public function walkUpdateStatement(AST\\UpdateStatement $updateStatement): string\n    {\n        return '';\n    }\n\n    public function walkDeleteStatement(AST\\DeleteStatement $deleteStatement): string\n    {\n        return '';\n    }\n\n    public function getFinalizer(AST\\SelectStatement|AST\\UpdateStatement|AST\\DeleteStatement $statement): SqlFinalizer\n    {\n        return new PreparedExecutorFinalizer(\n            new class extends AbstractSqlExecutor {\n                public function execute(Connection $conn, array $params, array $types): int\n                {\n                    return 0;\n                }\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/SchemaManagerMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\DBAL\\Schema\\AbstractSchemaManager;\nuse Exception;\n\n/**\n * Mock class for AbstractSchemaManager.\n */\nclass SchemaManagerMock extends AbstractSchemaManager\n{\n    /**\n     * {@inheritDoc}\n     */\n    protected function _getPortableTableColumnDefinition($tableColumn)\n    {\n        throw new Exception('not implemented');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/TimestampRegionMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\TimestampRegion;\n\n/**\n * Timestamp region mock\n *\n * Used to mock a TimestampRegion\n */\nclass TimestampRegionMock extends CacheRegionMock implements TimestampRegion\n{\n    public function update(CacheKey $key): void\n    {\n        $this->calls[__FUNCTION__][] = ['key' => $key];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Mocks/UnitOfWorkMock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Mocks;\n\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\ORM\\UnitOfWork;\n\nuse function spl_object_id;\n\n/**\n * Mock class for UnitOfWork.\n */\nclass UnitOfWorkMock extends UnitOfWork\n{\n    private array $mockDataChangeSets = [];\n    private array|null $persisterMock = null;\n\n    public function getEntityPersister(string $entityName): EntityPersister\n    {\n        return $this->persisterMock[$entityName] ?? parent::getEntityPersister($entityName);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function & getEntityChangeSet(object $entity): array\n    {\n        $oid = spl_object_id($entity);\n\n        if (isset($this->mockDataChangeSets[$oid])) {\n            return $this->mockDataChangeSets[$oid];\n        }\n\n        $data = parent::getEntityChangeSet($entity);\n\n        return $data;\n    }\n\n    /* MOCK API */\n\n    /**\n     * Sets a (mock) persister for an entity class that will be returned when\n     * getEntityPersister() is invoked for that class.\n     */\n    public function setEntityPersister(string $entityName, BasicEntityPersister $persister): void\n    {\n        $this->persisterMock[$entityName] = $persister;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function setOriginalEntityData(object $entity, array $originalData): void\n    {\n        $this->_originalEntityData[spl_object_id($entity)] = $originalData;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\AbstractFetchEager;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'abstract_fetch_eager_remote_control')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap(['mobile' => 'MobileRemoteControl'])]\nabstract class AbstractRemoteControl\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    /** @var Collection<User> */\n    #[ORM\\OneToMany(targetEntity: User::class, mappedBy: 'remoteControl', fetch: 'EAGER')]\n    public Collection $users;\n\n    public function __construct(string $name)\n    {\n        $this->name  = $name;\n        $this->users = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\AbstractFetchEager;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass MobileRemoteControl extends AbstractRemoteControl\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/AbstractFetchEager/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\AbstractFetchEager;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'abstract_fetch_eager_user')]\nclass User\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\ManyToOne(targetEntity: AbstractRemoteControl::class, inversedBy: 'users')]\n    #[ORM\\JoinColumn(nullable: false)]\n    public AbstractRemoteControl $remoteControl;\n\n    public function __construct(AbstractRemoteControl $control)\n    {\n        $this->remoteControl = $control;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/BigIntegers/BigIntegers.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\BigIntegers;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass BigIntegers\n{\n    #[ORM\\Column]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\Column(type: Types::BIGINT)]\n    public int $one = 1;\n\n    #[ORM\\Column(type: Types::BIGINT)]\n    public string $two = '2';\n\n    #[ORM\\Column(type: Types::BIGINT)]\n    public float $three = 3.0;\n}\n"
  },
  {
    "path": "tests/Tests/Models/BinaryPrimaryKey/BinaryId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\BinaryPrimaryKey;\n\nuse LogicException;\n\nuse function bin2hex;\nuse function hex2bin;\nuse function random_bytes;\n\nclass BinaryId\n{\n    public const LENGTH = 6;\n\n    private string $hexId;\n\n    private function __construct(string $data)\n    {\n        $this->hexId = $data;\n    }\n\n    public static function new(): self\n    {\n        return new self(bin2hex(random_bytes(self::LENGTH)));\n    }\n\n    public static function fromBytes(string $value): self\n    {\n        return new self(bin2hex($value));\n    }\n\n    public function getBytes(): string\n    {\n        $binary = hex2bin($this->hexId);\n        if ($binary === false) {\n            throw new LogicException('Cannot convert hex to binary: ' . $this->hexId);\n        }\n\n        return $binary;\n    }\n\n    public function __toString(): string\n    {\n        return $this->getBytes();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/BinaryPrimaryKey/BinaryIdType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\BinaryPrimaryKey;\n\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\Tests\\Mocks\\CompatibilityType;\nuse LogicException;\n\nfinal class BinaryIdType extends Type\n{\n    use CompatibilityType;\n\n    public const NAME = 'binary_id';\n\n    public function convertToPHPValue(\n        mixed $value,\n        AbstractPlatform $platform,\n    ): BinaryId|null {\n        if ($value === null) {\n            return null;\n        }\n\n        if ($value instanceof BinaryId) {\n            return $value;\n        }\n\n        return BinaryId::fromBytes($value);\n    }\n\n    public function convertToDatabaseValue(\n        mixed $value,\n        AbstractPlatform $platform,\n    ): string|null {\n        if ($value === null) {\n            return null;\n        } elseif ($value instanceof BinaryId) {\n            return $value->getBytes();\n        } else {\n            throw new LogicException('Unexpected value: ' . $value);\n        }\n    }\n\n    public function getSQLDeclaration(\n        array $column,\n        AbstractPlatform $platform,\n    ): string {\n        return $platform->getBinaryTypeDeclarationSQL([\n            'length' => BinaryId::LENGTH,\n            'fixed' => true,\n        ]);\n    }\n\n    private function doGetBindingType(): ParameterType|int\n    {\n        return ParameterType::BINARY;\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/BinaryPrimaryKey/Category.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\BinaryPrimaryKey;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\ReadableCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\n\n#[Entity]\nclass Category\n{\n    #[Id]\n    #[Column(type: BinaryIdType::NAME, nullable: false)]\n    private BinaryId $id;\n\n    #[Column]\n    private string $name;\n\n    #[ManyToOne(targetEntity: self::class, inversedBy: 'children')]\n    private self|null $parent;\n\n    /** @var Collection<int, Category> */\n    #[OneToMany(targetEntity: self::class, mappedBy: 'parent')]\n    private Collection $children;\n\n    public function __construct(\n        string $name,\n        self|null $parent = null,\n    ) {\n        $this->id       = BinaryId::new();\n        $this->name     = $name;\n        $this->parent   = $parent;\n        $this->children = new ArrayCollection();\n\n        $parent?->addChild($this);\n    }\n\n    public function getId(): BinaryId\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function getParent(): self|null\n    {\n        return $this->parent;\n    }\n\n    /** @return ReadableCollection<int, Category> */\n    public function getChildren(): ReadableCollection\n    {\n        return $this->children;\n    }\n\n    /** @internal */\n    public function addChild(self $category): void\n    {\n        if (! $this->children->contains($category)) {\n            $this->children->add($category);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsAddress.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'cms_addresses')]\n#[ORM\\EntityListeners(['CmsAddressListener'])]\nclass CmsAddress\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(length: 50)]\n    public $country;\n\n    /** @var string */\n    #[Column(length: 50)]\n    public $zip;\n\n    /** @var string */\n    #[Column(length: 50)]\n    public $city;\n\n    /**\n     * Testfield for Schema Updating Tests.\n     *\n     * @var string\n     */\n    public $street;\n\n    /** @var CmsUser */\n    #[OneToOne(targetEntity: 'CmsUser', inversedBy: 'address')]\n    #[JoinColumn(referencedColumnName: 'id')]\n    public $user;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getUser(): CmsUser\n    {\n        return $this->user;\n    }\n\n    public function getCountry(): string\n    {\n        return $this->country;\n    }\n\n    public function getZipCode(): string\n    {\n        return $this->zip;\n    }\n\n    public function getCity(): string\n    {\n        return $this->city;\n    }\n\n    public function setUser(CmsUser $user): void\n    {\n        if ($this->user !== $user) {\n            $this->user = $user;\n            $user->setAddress($this);\n        }\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setPrimaryTable(\n            ['name' => 'company_person'],\n        );\n\n        $metadata->mapField(\n            [\n                'id'        => true,\n                'fieldName' => 'id',\n                'type'      => 'integer',\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'zip',\n                'length'    => 50,\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'city',\n                'length'    => 50,\n            ],\n        );\n\n        $metadata->mapOneToOne(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n                'joinColumns'   => [['referencedColumnName' => 'id']],\n            ],\n        );\n\n        $metadata->addEntityListener(Events::postPersist, 'CmsAddressListener', 'postPersist');\n        $metadata->addEntityListener(Events::prePersist, 'CmsAddressListener', 'prePersist');\n\n        $metadata->addEntityListener(Events::postUpdate, 'CmsAddressListener', 'postUpdate');\n        $metadata->addEntityListener(Events::preUpdate, 'CmsAddressListener', 'preUpdate');\n\n        $metadata->addEntityListener(Events::postRemove, 'CmsAddressListener', 'postRemove');\n        $metadata->addEntityListener(Events::preRemove, 'CmsAddressListener', 'preRemove');\n\n        $metadata->addEntityListener(Events::preFlush, 'CmsAddressListener', 'preFlush');\n        $metadata->addEntityListener(Events::postLoad, 'CmsAddressListener', 'postLoad');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsAddressDTO.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsAddressDTO\n{\n    public function __construct(public string|null $country = null, public string|null $city = null, public string|null $zip = null, public string|null $address = null, public CmsDumbDTO|null $other = null)\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsAddressDTONamedArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsAddressDTONamedArgs\n{\n    public function __construct(\n        public string|null $country = null,\n        public string|null $city = null,\n        public string|null $zip = null,\n        public CmsAddressDTO|string|null $address = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsAddressListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse BadMethodCallException;\n\nuse function func_get_args;\n\nclass CmsAddressListener\n{\n    /** @phpstan-var array<string, list<list<mixed>>> */\n    public $calls;\n\n    public function prePersist(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postPersist(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function preUpdate(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postUpdate(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function preRemove(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postRemove(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postLoad(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function preFlush(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    protected function postPersistHandler(): void\n    {\n        throw new BadMethodCallException('This is not a valid callback');\n    }\n\n    protected function prePersistHandler(): void\n    {\n        throw new BadMethodCallException('This is not a valid callback');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsArticle.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\n\n#[Table(name: 'cms_articles')]\n#[Entity]\nclass CmsArticle\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $topic;\n\n    /** @var string */\n    #[Column(type: 'text')]\n    public $text;\n\n    /** @var CmsUser */\n    #[ManyToOne(targetEntity: 'CmsUser', inversedBy: 'articles')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    public $user;\n\n    /** @var Collection<int, CmsComment> */\n    #[OneToMany(targetEntity: 'CmsComment', mappedBy: 'article')]\n    public $comments;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    public function setAuthor(CmsUser $author): void\n    {\n        $this->user = $author;\n    }\n\n    public function addComment(CmsComment $comment): void\n    {\n        $this->comments[] = $comment;\n        $comment->setArticle($this);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsComment.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Stringable;\n\n#[Table(name: 'cms_comments')]\n#[Entity]\nclass CmsComment implements Stringable\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $topic;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $text;\n\n    /** @var CmsArticle */\n    #[ManyToOne(targetEntity: 'CmsArticle', inversedBy: 'comments')]\n    #[JoinColumn(name: 'article_id', referencedColumnName: 'id')]\n    public $article;\n\n    public function setArticle(CmsArticle $article): void\n    {\n        $this->article = $article;\n    }\n\n    public function __toString(): string\n    {\n        return self::class . '[id=' . $this->id . ']';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsDumbDTO.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsDumbDTO\n{\n    public function __construct(\n        public mixed $val1 = null,\n        public mixed $val2 = null,\n        public mixed $val3 = null,\n        public mixed $val4 = null,\n        public mixed $val5 = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsEmail.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * CmsEmail\n */\n#[Table(name: 'cms_emails')]\n#[Entity]\nclass CmsEmail\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(length: 250)]\n    public $email;\n\n    /** @var CmsUser */\n    #[OneToOne(targetEntity: 'CmsUser', mappedBy: 'email')]\n    public $user;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getEmail(): string\n    {\n        return $this->email;\n    }\n\n    public function setEmail(string $email): void\n    {\n        $this->email = $email;\n    }\n\n    public function getUser(): CmsUser\n    {\n        return $this->user;\n    }\n\n    public function setUser(CmsUser $user): void\n    {\n        $this->user = $user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsEmployee.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Description of CmsEmployee\n */\n#[Table(name: 'cms_employees')]\n#[Entity]\nclass CmsEmployee\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column]\n    private string $name;\n\n    #[OneToOne(targetEntity: 'CmsEmployee')]\n    #[JoinColumn(name: 'spouse_id', referencedColumnName: 'id')]\n    private CmsEmployee $spouse;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function getSpouse(): CmsEmployee|null\n    {\n        return $this->spouse;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsGroup.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse IteratorAggregate;\n\n/**\n * Description of CmsGroup\n */\n#[Table(name: 'cms_groups')]\n#[Entity]\nclass CmsGroup implements IteratorAggregate\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(length: 50)]\n    public $name;\n\n    /** @phpstan-var Collection<int, CmsUser> */\n    #[ManyToMany(targetEntity: 'CmsUser', mappedBy: 'groups')]\n    public $users;\n\n    public function __construct()\n    {\n        $this->users = new ArrayCollection();\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function addUser(CmsUser $user): void\n    {\n        $this->users[] = $user;\n    }\n\n    /** @phpstan-return Collection<int, CmsUser> */\n    public function getUsers(): Collection\n    {\n        return $this->users;\n    }\n\n    /** @return Collection<int, CmsUser> */\n    public function getIterator(): Collection\n    {\n        return $this->getUsers();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsPhonenumber.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'cms_phonenumbers')]\n#[Entity]\nclass CmsPhonenumber\n{\n    /** @var string */\n    #[Id]\n    #[Column(length: 50)]\n    public $phonenumber;\n\n    /** @var CmsUser */\n    #[ManyToOne(targetEntity: 'CmsUser', inversedBy: 'phonenumbers', cascade: [])]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    public $user;\n\n    public function setUser(CmsUser $user): void\n    {\n        $this->user = $user;\n    }\n\n    public function getUser(): CmsUser|null\n    {\n        return $this->user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsTag.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Description of CmsTag\n */\n#[Table(name: 'cms_tags')]\n#[Entity]\nclass CmsTag\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(length: 50, name: 'tag_name', nullable: true)]\n    public $name;\n\n    /** @phpstan-var Collection<int, CmsUser> */\n    #[ManyToMany(targetEntity: 'CmsUser', mappedBy: 'tags')]\n    public $users;\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function addUser(CmsUser $user): void\n    {\n        $this->users[] = $user;\n    }\n\n    /** @phpstan-return Collection<int, CmsUser> */\n    public function getUsers(): Collection\n    {\n        return $this->users;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'cms_users')]\n#[Entity]\nclass CmsUser\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 50, nullable: true)]\n    public $status;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, unique: true)]\n    public $username;\n\n    /** @phpstan-var string|null */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, CmsPhonenumber> */\n    #[OneToMany(targetEntity: 'CmsPhonenumber', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    public $phonenumbers;\n\n    /** @phpstan-var Collection<int, CmsArticle> */\n    #[OneToMany(targetEntity: 'CmsArticle', mappedBy: 'user', cascade: ['detach'])]\n    public $articles;\n\n    /** @var CmsAddress */\n    #[OneToOne(targetEntity: 'CmsAddress', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    public $address;\n\n    /** @var CmsEmail */\n    #[OneToOne(targetEntity: 'CmsEmail', inversedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    #[JoinColumn(referencedColumnName: 'id', nullable: true)]\n    public $email;\n\n    /** @phpstan-var Collection<int, CmsGroup> */\n    #[JoinTable(name: 'cms_users_groups')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'CmsGroup', inversedBy: 'users', cascade: ['persist', 'detach'])]\n    public $groups;\n\n    /** @var Collection<int, CmsTag> */\n    #[JoinTable(name: 'cms_users_tags')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'CmsTag', inversedBy: 'users', cascade: ['all'])]\n    public $tags;\n\n    /** @var mixed */\n    public $nonPersistedProperty;\n\n    /** @var mixed */\n    public $nonPersistedPropertyObject;\n\n    public function __construct()\n    {\n        $this->phonenumbers = new ArrayCollection();\n        $this->articles     = new ArrayCollection();\n        $this->groups       = new ArrayCollection();\n        $this->tags         = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getStatus(): string\n    {\n        return $this->status;\n    }\n\n    public function getUsername(): string\n    {\n        return $this->username;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    /**\n     * Adds a phonenumber to the user.\n     */\n    public function addPhonenumber(CmsPhonenumber $phone): void\n    {\n        $this->phonenumbers[] = $phone;\n        $phone->setUser($this);\n    }\n\n    /** @phpstan-return Collection<int, CmsPhonenumber> */\n    public function getPhonenumbers(): Collection\n    {\n        return $this->phonenumbers;\n    }\n\n    public function addArticle(CmsArticle $article): void\n    {\n        $this->articles[] = $article;\n        $article->setAuthor($this);\n    }\n\n    public function addGroup(CmsGroup $group): void\n    {\n        $this->groups[] = $group;\n        $group->addUser($this);\n    }\n\n    /** @phpstan-return Collection<int, CmsGroup> */\n    public function getGroups(): Collection\n    {\n        return $this->groups;\n    }\n\n    public function addTag(CmsTag $tag): void\n    {\n        $this->tags[] = $tag;\n        $tag->addUser($this);\n    }\n\n    public function getTags(): Collection\n    {\n        return $this->tags;\n    }\n\n    public function removePhonenumber($index): bool\n    {\n        if (isset($this->phonenumbers[$index])) {\n            $ph = $this->phonenumbers[$index];\n            unset($this->phonenumbers[$index]);\n            $ph->user = null;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    public function getAddress(): CmsAddress\n    {\n        return $this->address;\n    }\n\n    public function setAddress(CmsAddress $address): void\n    {\n        if ($this->address !== $address) {\n            $this->address = $address;\n            $address->setUser($this);\n        }\n    }\n\n    public function getEmail(): CmsEmail|null\n    {\n        return $this->email;\n    }\n\n    public function setEmail(CmsEmail|null $email = null): void\n    {\n        if ($this->email !== $email) {\n            $this->email = $email;\n\n            if ($email) {\n                $email->setUser($this);\n            }\n        }\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setPrimaryTable(\n            ['name' => 'cms_users'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsUserDTO.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsUserDTO\n{\n    public function __construct(public string|null $name = null, public string|null $email = null, public CmsAddressDTO|string|null $address = null, public int|null $phonenumbers = null)\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsUserDTONamedArgs.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsUserDTONamedArgs\n{\n    public function __construct(\n        public string|null $name = null,\n        public string|null $email = null,\n        public string|null $address = null,\n        public int|null $phonenumbers = null,\n        public CmsAddressDTO|null $addressDto = null,\n        public CmsAddressDTONamedArgs|null $addressDtoNamedArgs = null,\n        public CmsDumbDTO|null $dumb = null,\n        public CmsAddress|null $addressEntity = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CMS/CmsUserDTOVariadicArg.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CMS;\n\nclass CmsUserDTOVariadicArg\n{\n    public string|null $name      = null;\n    public string|null $email     = null;\n    public string|null $address   = null;\n    public int|null $phonenumbers = null;\n    public array $otherProperties = [];\n\n    public function __construct(...$args)\n    {\n        $this->name            = $args['name'] ?? null;\n        $this->email           = $args['email'] ?? null;\n        $this->phonenumbers    = $args['phonenumbers'] ?? null;\n        $this->address         = $args['address'] ?? null;\n        $this->otherProperties = $args;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Action.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_action')]\n#[Entity]\nclass Action\n{\n    /** @phpstan-var Collection<int, Token> */\n    #[OneToMany(targetEntity: 'Token', cascade: ['persist', 'remove'], mappedBy: 'action')]\n    public $tokens;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        #[GeneratedValue(strategy: 'NONE')]\n        public string $name,\n    ) {\n        $this->tokens = new ArrayCollection();\n    }\n\n    public function addToken(Token $token): void\n    {\n        $this->tokens[] = $token;\n        $token->action  = $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Address.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_client_address')]\n#[Entity]\nclass Address\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Person */\n    #[JoinColumn(name: 'person_id', referencedColumnName: 'id')]\n    #[OneToOne(targetEntity: 'Person', inversedBy: 'address')]\n    public $person;\n\n    public function __construct(\n        #[Column]\n        public string $location,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Attraction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_attraction')]\n#[Cache('NONSTRICT_READ_WRITE')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap([1 => 'Restaurant', 2 => 'Beach', 3 => 'Bar'])]\nabstract class Attraction\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @phpstan-var Collection<int, AttractionInfo> */\n    #[Cache]\n    #[OneToMany(targetEntity: 'AttractionInfo', mappedBy: 'attraction')]\n    protected $infos;\n\n    public function __construct(\n        #[Column(unique: true)]\n        protected string $name,\n        #[Cache]\n        #[ManyToOne(targetEntity: 'City', inversedBy: 'attractions')]\n        #[JoinColumn(name: 'city_id', referencedColumnName: 'id')]\n        protected City $city,\n    ) {\n        $this->infos = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getCity(): City\n    {\n        return $this->city;\n    }\n\n    public function setCity(City $city): void\n    {\n        $this->city = $city;\n    }\n\n    /** @phpstan-return Collection<int, AttractionInfo> */\n    public function getInfos(): Collection\n    {\n        return $this->infos;\n    }\n\n    public function addInfo(AttractionInfo $info): void\n    {\n        if (! $this->infos->contains($info)) {\n            $this->infos->add($info);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/AttractionContactInfo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_attraction_contact_info')]\n#[Entity]\nclass AttractionContactInfo extends AttractionInfo\n{\n    /** @var string */\n    #[Column(unique: true)]\n    protected $fone;\n\n    public function __construct(string $fone, Attraction $attraction)\n    {\n        $this->setAttraction($attraction);\n        $this->setFone($fone);\n    }\n\n    public function getFone(): string\n    {\n        return $this->fone;\n    }\n\n    public function setFone(string $fone): void\n    {\n        $this->fone = $fone;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/AttractionInfo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_attraction_info')]\n#[Cache]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap([1 => 'AttractionContactInfo', 2 => 'AttractionLocationInfo'])]\nabstract class AttractionInfo\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @var Attraction */\n    #[Cache]\n    #[ManyToOne(targetEntity: 'Attraction', inversedBy: 'infos')]\n    #[JoinColumn(name: 'attraction_id', referencedColumnName: 'id')]\n    protected $attraction;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getAttraction(): Attraction\n    {\n        return $this->attraction;\n    }\n\n    public function setAttraction(Attraction $attraction): void\n    {\n        $this->attraction = $attraction;\n\n        $attraction->addInfo($this);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/AttractionLocationInfo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_attraction_location_info')]\n#[Entity]\nclass AttractionLocationInfo extends AttractionInfo\n{\n    /** @var string */\n    #[Column(unique: true)]\n    protected $address;\n\n    public function __construct(string $address, Attraction $attraction)\n    {\n        $this->setAttraction($attraction);\n        $this->setAddress($address);\n    }\n\n    public function getAddress(): string\n    {\n        return $this->address;\n    }\n\n    public function setAddress(string $address): void\n    {\n        $this->address = $address;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Bar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Bar extends Attraction\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Beach.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Beach extends Attraction\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/City.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'cache_city')]\n#[ORM\\Cache]\nclass City\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    protected $id;\n\n    /** @var Collection<int, Travel> */\n    #[ORM\\ManyToMany(targetEntity: 'Travel', mappedBy: 'visitedCities')]\n    public $travels;\n\n    /** @phpstan-var Collection<int, Attraction> */\n    #[ORM\\Cache]\n    #[ORM\\OrderBy(['name' => 'ASC'])]\n    #[ORM\\OneToMany(targetEntity: 'Attraction', mappedBy: 'city')]\n    public $attractions;\n\n    public function __construct(\n        #[ORM\\Column(unique: true)]\n        protected string $name,\n        #[ORM\\Cache]\n        #[ORM\\ManyToOne(targetEntity: 'State', inversedBy: 'cities')]\n        #[ORM\\JoinColumn(name: 'state_id', referencedColumnName: 'id')]\n        protected State|null $state = null,\n    ) {\n        $this->travels     = new ArrayCollection();\n        $this->attractions = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getState(): State|null\n    {\n        return $this->state;\n    }\n\n    public function setState(State $state): void\n    {\n        $this->state = $state;\n    }\n\n    public function addTravel(Travel $travel): void\n    {\n        $this->travels[] = $travel;\n    }\n\n    /** @phpstan-return Collection<int, Travel> */\n    public function getTravels(): Collection\n    {\n        return $this->travels;\n    }\n\n    public function addAttraction(Attraction $attraction): void\n    {\n        $this->attractions[] = $attraction;\n    }\n\n    /** @phpstan-return Collection<int, Attraction> */\n    public function getAttractions(): Collection\n    {\n        return $this->attractions;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable(['name' => 'cache_city']);\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n        $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT);\n\n        $metadata->enableCache(\n            [\n                'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY,\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'id',\n                'type' => 'integer',\n                'id' => true,\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type' => 'string',\n            ],\n        );\n\n        $metadata->mapOneToOne(\n            [\n                'fieldName'      => 'state',\n                'targetEntity'   => State::class,\n                'inversedBy'     => 'cities',\n                'joinColumns'    =>\n                    [\n                        [\n                            'name' => 'state_id',\n                            'referencedColumnName' => 'id',\n                        ],\n                    ],\n            ],\n        );\n        $metadata->enableAssociationCache('state', [\n            'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY,\n        ]);\n\n        $metadata->mapManyToMany(\n            [\n                'fieldName' => 'travels',\n                'targetEntity' => Travel::class,\n                'mappedBy' => 'visitedCities',\n            ],\n        );\n\n        $metadata->mapOneToMany(\n            [\n                'fieldName' => 'attractions',\n                'targetEntity' => Attraction::class,\n                'mappedBy' => 'city',\n                'orderBy' => ['name' => 'ASC'],\n            ],\n        );\n        $metadata->enableAssociationCache('attractions', [\n            'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY,\n        ]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Client.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_client')]\n#[Entity]\nclass Client\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Column(unique: true)]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/ComplexAction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_complex_action')]\n#[Entity]\nclass ComplexAction\n{\n    /** @phpstan-var Collection<int, Token> */\n    #[OneToMany(targetEntity: 'Token', cascade: ['persist', 'remove'], mappedBy: 'complexAction')]\n    public $tokens;\n\n    public function __construct(\n        /** @var Action */\n        #[Id]\n        #[OneToOne(targetEntity: 'Action', cascade: ['persist', 'remove'])]\n        #[JoinColumn(name: 'action1_name', referencedColumnName: 'name')]\n        public $action1,\n        /** @var Action */\n        #[Id]\n        #[OneToOne(targetEntity: 'Action', cascade: ['persist', 'remove'])]\n        #[JoinColumn(name: 'action2_name', referencedColumnName: 'name')]\n        public $action2,\n        #[Column]\n        public string $name,\n    ) {\n        $this->tokens = new ArrayCollection();\n    }\n\n    public function addToken(Token $token): void\n    {\n        $this->tokens[]       = $token;\n        $token->complexAction = $this;\n    }\n\n    public function getAction1(): Action\n    {\n        return $this->action1;\n    }\n\n    public function getAction2(): Action\n    {\n        return $this->action2;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Country.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_country')]\n#[Cache]\n#[Entity]\nclass Country\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function __construct(\n        #[Column(unique: true)]\n        protected string $name,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Flight.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_flight')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass Flight\n{\n    /** @var DateTime */\n    #[Column(type: 'date')]\n    protected $departure;\n\n    public function __construct(\n        #[Id]\n        #[Cache]\n        #[ManyToOne(targetEntity: 'City')]\n        #[JoinColumn(name: 'leaving_from_city_id', referencedColumnName: 'id')]\n        protected City $leavingFrom,\n        #[Id]\n        #[Cache]\n        #[ManyToOne(targetEntity: 'City')]\n        #[JoinColumn(name: 'going_to_city_id', referencedColumnName: 'id')]\n        protected City $goingTo,\n    ) {\n        $this->departure = new DateTime();\n    }\n\n    public function getLeavingFrom(): City\n    {\n        return $this->leavingFrom;\n    }\n\n    public function getGoingTo(): City\n    {\n        return $this->goingTo;\n    }\n\n    public function getDeparture(): DateTime\n    {\n        return $this->departure;\n    }\n\n    public function setDeparture(DateTime $departure): void\n    {\n        $this->departure = $departure;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Login.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_login')]\n#[Entity]\nclass Login\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Token */\n    #[ManyToOne(targetEntity: 'Token', cascade: ['persist', 'remove'], inversedBy: 'logins')]\n    #[JoinColumn(name: 'token_id', referencedColumnName: 'token')]\n    public $token;\n\n    public function __construct(\n        #[Column]\n        public string $name,\n    ) {\n    }\n\n    public function getToken(): Token\n    {\n        return $this->token;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Person.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_person')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass Person\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Address */\n    #[OneToOne(targetEntity: 'Address', mappedBy: 'person')]\n    public $address;\n\n    public function __construct(\n        #[Column(unique: true)]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Restaurant.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Restaurant extends Attraction\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/State.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_state')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass State\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @phpstan-var Collection<int, City> */\n    #[Cache('NONSTRICT_READ_WRITE')]\n    #[OneToMany(targetEntity: 'City', mappedBy: 'state')]\n    protected $cities;\n\n    public function __construct(\n        #[Column(unique: true)]\n        protected string $name,\n        #[Cache]\n        #[ManyToOne(targetEntity: 'Country')]\n        #[JoinColumn(name: 'country_id', referencedColumnName: 'id')]\n        protected Country|null $country = null,\n    ) {\n        $this->cities = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getCountry(): Country|null\n    {\n        return $this->country;\n    }\n\n    public function setCountry(Country $country): void\n    {\n        $this->country = $country;\n    }\n\n    /** @phpstan-return Collection<int, City> */\n    public function getCities(): Collection\n    {\n        return $this->cities;\n    }\n\n    /** @phpstan-param Collection<int, City> $cities */\n    public function setCities(Collection $cities): void\n    {\n        $this->cities = $cities;\n    }\n\n    public function addCity(City $city): void\n    {\n        $this->cities[] = $city;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Token.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\nuse function date;\nuse function strtotime;\n\n#[Table('cache_token')]\n#[Entity]\n#[Cache('READ_ONLY')]\nclass Token\n{\n    /** @var DateTime */\n    #[Column(type: 'date')]\n    public $expiresAt;\n\n    /** @phpstan-var Collection<int, Login> */\n    #[OneToMany(targetEntity: 'Login', cascade: ['persist', 'remove'], mappedBy: 'token')]\n    public $logins;\n\n    /** @var Action */\n    #[ManyToOne(targetEntity: 'Action', cascade: ['persist', 'remove'], inversedBy: 'tokens')]\n    #[JoinColumn(name: 'action_name', referencedColumnName: 'name')]\n    public $action;\n\n    /** @var ComplexAction */\n    #[JoinColumn(name: 'complex_action1_name', referencedColumnName: 'action1_name')]\n    #[JoinColumn(name: 'complex_action2_name', referencedColumnName: 'action2_name')]\n    #[ManyToOne(targetEntity: 'ComplexAction', cascade: ['persist', 'remove'], inversedBy: 'tokens')]\n    public $complexAction;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        public string $token,\n        #[OneToOne(targetEntity: 'Client')]\n        public Client|null $client = null,\n    ) {\n        $this->logins    = new ArrayCollection();\n        $this->expiresAt = new DateTime(date('Y-m-d H:i:s', strtotime('+7 day')));\n    }\n\n    public function addLogin(Login $login): void\n    {\n        $this->logins[] = $login;\n        $login->token   = $this;\n    }\n\n    public function getClient(): Client|null\n    {\n        return $this->client;\n    }\n\n    public function getAction(): Action\n    {\n        return $this->action;\n    }\n\n    public function getComplexAction(): ComplexAction\n    {\n        return $this->complexAction;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Travel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_travel')]\n#[Cache]\n#[Entity]\nclass Travel\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @var DateTime */\n    #[Column(type: 'date')]\n    protected $createdAt;\n\n    /** @phpstan-var Collection<int, City> */\n    #[JoinTable(name: 'cache_visited_cities')]\n    #[JoinColumn(name: 'travel_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'city_id', referencedColumnName: 'id')]\n    #[Cache]\n    #[ManyToMany(targetEntity: 'City', inversedBy: 'travels', cascade: ['persist', 'remove'])]\n    public $visitedCities;\n\n    public function __construct(\n        #[Cache]\n        #[ManyToOne(targetEntity: 'Traveler', inversedBy: 'travels')]\n        #[JoinColumn(name: 'traveler_id', referencedColumnName: 'id')]\n        protected Traveler $traveler,\n    ) {\n        $this->createdAt     = new DateTime('now');\n        $this->visitedCities = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getTraveler(): Traveler\n    {\n        return $this->traveler;\n    }\n\n    public function setTraveler(Traveler $traveler): void\n    {\n        $this->traveler = $traveler;\n    }\n\n    /** @phpstan-return Collection<int, City> */\n    public function getVisitedCities(): Collection\n    {\n        return $this->visitedCities;\n    }\n\n    public function addVisitedCity(City $city): void\n    {\n        $this->visitedCities->add($city);\n    }\n\n    public function removeVisitedCity(City $city): void\n    {\n        $this->visitedCities->removeElement($city);\n    }\n\n    public function getCreatedAt(): DateTime\n    {\n        return $this->createdAt;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/Traveler.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_traveler')]\n#[Cache]\n#[Entity]\nclass Traveler\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @phpstan-var Collection<int, Travel> */\n    #[Cache('NONSTRICT_READ_WRITE')]\n    #[OneToMany(targetEntity: 'Travel', mappedBy: 'traveler', cascade: ['persist', 'remove'], orphanRemoval: true)]\n    public $travels;\n\n    /** @var TravelerProfile */\n     #[Cache]\n     #[OneToOne(targetEntity: 'TravelerProfile')]\n     protected $profile;\n\n    public function __construct(\n        #[Column]\n        protected string $name,\n    ) {\n        $this->travels = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getProfile(): TravelerProfile\n    {\n        return $this->profile;\n    }\n\n    public function setProfile(TravelerProfile $profile): void\n    {\n        $this->profile = $profile;\n    }\n\n    /** @phpstan-return Collection<int, Travel> */\n    public function getTravels(): Collection\n    {\n        return $this->travels;\n    }\n\n    public function addTravel(Travel $item): void\n    {\n        if (! $this->travels->contains($item)) {\n            $this->travels->add($item);\n        }\n\n        if ($item->getTraveler() !== $this) {\n            $item->setTraveler($this);\n        }\n    }\n\n    public function removeTravel(Travel $item): void\n    {\n        $this->travels->removeElement($item);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/TravelerProfile.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_traveler_profile')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass TravelerProfile\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    #[OneToOne(targetEntity: 'TravelerProfileInfo', mappedBy: 'profile')]\n    #[Cache]\n    private TravelerProfileInfo|null $info = null;\n\n    public function __construct(\n        #[Column(unique: true)]\n        private string $name,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $nae): void\n    {\n        $this->name = $nae;\n    }\n\n    public function getInfo(): TravelerProfileInfo\n    {\n        return $this->info;\n    }\n\n    public function setInfo(TravelerProfileInfo $info): void\n    {\n        $this->info = $info;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Cache/TravelerProfileInfo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Cache;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table('cache_traveler_profile_info')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass TravelerProfileInfo\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function __construct(\n        #[Cache]\n        #[JoinColumn(name: 'profile_id', referencedColumnName: 'id')]\n        #[OneToOne(targetEntity: 'TravelerProfile', inversedBy: 'info')]\n        private TravelerProfile $profile,\n        #[Column(unique: true)]\n        private string $description,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n\n    public function setDescription(string $description): void\n    {\n        $this->description = $description;\n    }\n\n    public function getProfile(): TravelerProfile\n    {\n        return $this->profile;\n    }\n\n    public function setProfile(TravelerProfile $profile): void\n    {\n        $this->profile = $profile;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyAuction.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_auctions')]\n#[Entity]\nclass CompanyAuction extends CompanyEvent\n{\n    #[Column(type: 'string', length: 255)]\n    private string|null $data = null;\n\n    public function setData(string $data): void\n    {\n        $this->data = $data;\n    }\n\n    public function getData(): string\n    {\n        return $this->data;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyCar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_cars')]\n#[Entity]\nclass CompanyCar\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 50)]\n        private string|null $brand = null,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getBrand(): string|null\n    {\n        return $this->brand;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'company_contracts')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'discr', type: 'string')]\n#[ORM\\DiscriminatorMap(['fix' => 'CompanyFixContract', 'flexible' => 'CompanyFlexContract', 'flexultra' => 'CompanyFlexUltraContract'])]\n#[ORM\\EntityListeners(['CompanyContractListener'])]\nabstract class CompanyContract\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'CompanyEmployee', inversedBy: 'soldContracts')]\n    private CompanyEmployee|null $salesPerson = null;\n\n    #[Column(type: 'boolean')]\n    private bool $completed = false;\n\n    /** @phpstan-var Collection<int, CompanyEmployee> */\n    #[JoinTable(name: 'company_contract_employees')]\n    #[JoinColumn(name: 'contract_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[InverseJoinColumn(name: 'employee_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'CompanyEmployee', inversedBy: 'contracts')]\n    private $engineers;\n\n    public function __construct()\n    {\n        $this->engineers = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function markCompleted(): void\n    {\n        $this->completed = true;\n    }\n\n    public function isCompleted(): bool\n    {\n        return $this->completed;\n    }\n\n    public function getSalesPerson(): CompanyEmployee\n    {\n        return $this->salesPerson;\n    }\n\n    public function setSalesPerson(CompanyEmployee $salesPerson): void\n    {\n        $this->salesPerson = $salesPerson;\n    }\n\n    /** @phpstan-return Collection<int, CompanyEmployee> */\n    public function getEngineers(): Collection\n    {\n        return $this->engineers;\n    }\n\n    public function addEngineer(CompanyEmployee $engineer): void\n    {\n        $this->engineers[] = $engineer;\n    }\n\n    public function removeEngineer(CompanyEmployee $engineer): void\n    {\n        $this->engineers->removeElement($engineer);\n    }\n\n    abstract public function calculatePrice(): int;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);\n        $metadata->setTableName('company_contracts');\n        $metadata->setDiscriminatorColumn(\n            [\n                'name' => 'discr',\n                'type' => 'string',\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'id'        => true,\n                'name'      => 'id',\n                'fieldName' => 'id',\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'type'      => 'boolean',\n                'name'      => 'completed',\n                'fieldName' => 'completed',\n            ],\n        );\n\n        $metadata->setDiscriminatorMap(\n            [\n                'fix'       => 'CompanyFixContract',\n                'flexible'  => 'CompanyFlexContract',\n                'flexultra' => 'CompanyFlexUltraContract',\n            ],\n        );\n\n        $metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler');\n        $metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler');\n\n        $metadata->addEntityListener(Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');\n        $metadata->addEntityListener(Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');\n\n        $metadata->addEntityListener(Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');\n        $metadata->addEntityListener(Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');\n\n        $metadata->addEntityListener(Events::preFlush, 'CompanyContractListener', 'preFlushHandler');\n        $metadata->addEntityListener(Events::postLoad, 'CompanyContractListener', 'postLoadHandler');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyContractListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\nuse function func_get_args;\n\nclass CompanyContractListener\n{\n    /** @phpstan-var list<list<mixed>> */\n    public $postPersistCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $prePersistCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $postUpdateCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $preUpdateCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $postRemoveCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $preRemoveCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $preFlushCalls;\n\n    /** @phpstan-var list<list<mixed>> */\n    public $postLoadCalls;\n\n    #[ORM\\PostPersist]\n    public function postPersistHandler(CompanyContract $contract): void\n    {\n        $this->postPersistCalls[] = func_get_args();\n    }\n\n    #[ORM\\PrePersist]\n    public function prePersistHandler(CompanyContract $contract): void\n    {\n        $this->prePersistCalls[] = func_get_args();\n    }\n\n    #[ORM\\PostUpdate]\n    public function postUpdateHandler(CompanyContract $contract): void\n    {\n        $this->postUpdateCalls[] = func_get_args();\n    }\n\n    #[ORM\\PreUpdate]\n    public function preUpdateHandler(CompanyContract $contract): void\n    {\n        $this->preUpdateCalls[] = func_get_args();\n    }\n\n    #[ORM\\PostRemove]\n    public function postRemoveHandler(CompanyContract $contract): void\n    {\n        $this->postRemoveCalls[] = func_get_args();\n    }\n\n    #[ORM\\PreRemove]\n    public function preRemoveHandler(CompanyContract $contract): void\n    {\n        $this->preRemoveCalls[] = func_get_args();\n    }\n\n    #[ORM\\PreFlush]\n    public function preFlushHandler(CompanyContract $contract): void\n    {\n        $this->preFlushCalls[] = func_get_args();\n    }\n\n    #[ORM\\PostLoad]\n    public function postLoadHandler(CompanyContract $contract): void\n    {\n        $this->postLoadCalls[] = func_get_args();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyEmployee.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_employees')]\n#[Entity]\nclass CompanyEmployee extends CompanyPerson\n{\n    #[Column(type: 'integer')]\n    private int|null $salary = null;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $department = null;\n\n    #[Column(type: 'datetime', nullable: true)]\n    private DateTime|null $startDate = null;\n\n    /** @phpstan-var Collection<int, CompanyContract> */\n    #[ManyToMany(targetEntity: 'CompanyContract', mappedBy: 'engineers', fetch: 'EXTRA_LAZY')]\n    public $contracts;\n\n    /** @phpstan-var Collection<int, CompanyFlexUltraContract> */\n    #[OneToMany(targetEntity: 'CompanyFlexUltraContract', mappedBy: 'salesPerson', fetch: 'EXTRA_LAZY')]\n    public $soldContracts;\n\n    public function getSalary(): int\n    {\n        return $this->salary;\n    }\n\n    public function setSalary(int $salary): void\n    {\n        $this->salary = $salary;\n    }\n\n    public function getDepartment(): string\n    {\n        return $this->department;\n    }\n\n    public function setDepartment(string $dep): void\n    {\n        $this->department = $dep;\n    }\n\n    public function getStartDate(): DateTime|null\n    {\n        return $this->startDate;\n    }\n\n    public function setStartDate(DateTime $date): void\n    {\n        $this->startDate = $date;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_events')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'event_type', type: 'string', length: 255)]\n#[DiscriminatorMap(['auction' => 'CompanyAuction', 'raffle' => 'CompanyRaffle'])]\nabstract class CompanyEvent\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'CompanyOrganization', inversedBy: 'events', cascade: ['persist'])]\n    #[JoinColumn(name: 'org_id', referencedColumnName: 'id')]\n    private CompanyOrganization|null $organization = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getOrganization(): CompanyOrganization\n    {\n        return $this->organization;\n    }\n\n    public function setOrganization(CompanyOrganization $org): void\n    {\n        $this->organization = $org;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyFixContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\n\n#[ORM\\Entity]\nclass CompanyFixContract extends CompanyContract\n{\n    #[Column(type: 'integer')]\n    private int $fixPrice = 0;\n\n    public function calculatePrice(): int\n    {\n        return $this->fixPrice;\n    }\n\n    public function getFixPrice(): int\n    {\n        return $this->fixPrice;\n    }\n\n    public function setFixPrice($fixPrice): void\n    {\n        $this->fixPrice = $fixPrice;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'type'      => 'integer',\n                'name'      => 'fixPrice',\n                'fieldName' => 'fixPrice',\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyFlexContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[ORM\\Entity]\nclass CompanyFlexContract extends CompanyContract\n{\n    #[Column(type: 'integer')]\n    private int $hoursWorked = 0;\n\n    #[Column(type: 'integer')]\n    private int $pricePerHour = 0;\n\n    /** @phpstan-var Collection<int, CompanyManager> */\n    #[JoinTable(name: 'company_contract_managers')]\n    #[JoinColumn(name: 'contract_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[InverseJoinColumn(name: 'employee_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'CompanyManager', inversedBy: 'managedContracts', fetch: 'EXTRA_LAZY')]\n    public $managers;\n\n    public function calculatePrice(): int\n    {\n        return $this->hoursWorked * $this->pricePerHour;\n    }\n\n    public function getHoursWorked(): int\n    {\n        return $this->hoursWorked;\n    }\n\n    public function setHoursWorked(int $hoursWorked): void\n    {\n        $this->hoursWorked = $hoursWorked;\n    }\n\n    public function getPricePerHour(): int\n    {\n        return $this->pricePerHour;\n    }\n\n    public function setPricePerHour(int $pricePerHour): void\n    {\n        $this->pricePerHour = $pricePerHour;\n    }\n\n    /** @phpstan-return Collection<int, CompanyManager> */\n    public function getManagers(): Collection\n    {\n        return $this->managers;\n    }\n\n    public function addManager(CompanyManager $manager): void\n    {\n        $this->managers[] = $manager;\n    }\n\n    public function removeManager(CompanyManager $manager): void\n    {\n        $this->managers->removeElement($manager);\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'type'      => 'integer',\n                'name'      => 'hoursWorked',\n                'fieldName' => 'hoursWorked',\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'type'      => 'integer',\n                'name'      => 'pricePerHour',\n                'fieldName' => 'pricePerHour',\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyFlexUltraContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\n\nuse function max;\n\n#[ORM\\Entity]\n#[ORM\\EntityListeners(['CompanyContractListener', 'CompanyFlexUltraContractListener'])]\nclass CompanyFlexUltraContract extends CompanyFlexContract\n{\n    #[Column(type: 'integer')]\n    private int $maxPrice = 0;\n\n    public function calculatePrice(): int\n    {\n        return max($this->maxPrice, parent::calculatePrice());\n    }\n\n    public function getMaxPrice(): int\n    {\n        return $this->maxPrice;\n    }\n\n    public function setMaxPrice(int $maxPrice): void\n    {\n        $this->maxPrice = $maxPrice;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'type'      => 'integer',\n                'name'      => 'maxPrice',\n                'fieldName' => 'maxPrice',\n            ],\n        );\n        $metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler');\n        $metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler');\n\n        $metadata->addEntityListener(Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');\n        $metadata->addEntityListener(Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');\n\n        $metadata->addEntityListener(Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');\n        $metadata->addEntityListener(Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');\n\n        $metadata->addEntityListener(Events::preFlush, 'CompanyContractListener', 'preFlushHandler');\n        $metadata->addEntityListener(Events::postLoad, 'CompanyContractListener', 'postLoadHandler');\n\n        $metadata->addEntityListener(Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler1');\n        $metadata->addEntityListener(Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler2');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyFlexUltraContractListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Event\\PrePersistEventArgs;\nuse Doctrine\\ORM\\Mapping as ORM;\n\nuse function func_get_args;\n\nclass CompanyFlexUltraContractListener\n{\n    /** @phpstan-var list<mixed[]> */\n    public $prePersistCalls;\n\n    #[ORM\\PrePersist]\n    public function prePersistHandler1(CompanyContract $contract, PrePersistEventArgs $args): void\n    {\n        $this->prePersistCalls[] = func_get_args();\n    }\n\n    #[ORM\\PrePersist]\n    public function prePersistHandler2(CompanyContract $contract, PrePersistEventArgs $args): void\n    {\n        $this->prePersistCalls[] = func_get_args();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyManager.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_managers')]\n#[Entity]\nclass CompanyManager extends CompanyEmployee\n{\n    #[Column(type: 'string', length: 250)]\n    private string|null $title = null;\n\n    #[OneToOne(targetEntity: 'CompanyCar', cascade: ['persist'])]\n    #[JoinColumn(name: 'car_id', referencedColumnName: 'id')]\n    private CompanyCar|null $car = null;\n\n    /** @phpstan-var Collection<int, CompanyFlexContract> */\n    #[ManyToMany(targetEntity: 'CompanyFlexContract', mappedBy: 'managers', fetch: 'EXTRA_LAZY')]\n    public $managedContracts;\n\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    public function setTitle(string $title): void\n    {\n        $this->title = $title;\n    }\n\n    public function getCar(): CompanyCar\n    {\n        return $this->car;\n    }\n\n    public function setCar(CompanyCar $car): void\n    {\n        $this->car = $car;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyOrganization.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_organizations')]\n#[Entity]\nclass CompanyOrganization\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    /** @phpstan-var Collection<int, CompanyEvent> */\n    #[OneToMany(targetEntity: 'CompanyEvent', mappedBy: 'organization', cascade: ['persist'], fetch: 'EXTRA_LAZY')]\n    public $events;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    /** @phpstan-return Collection<int, CompanyEvent> */\n    public function getEvents(): Collection\n    {\n        return $this->events;\n    }\n\n    public function addEvent(CompanyEvent $event): void\n    {\n        $this->events[] = $event;\n        $event->setOrganization($this);\n    }\n\n    #[OneToOne(targetEntity: 'CompanyEvent', cascade: ['persist'])]\n    #[JoinColumn(name: 'main_event_id', referencedColumnName: 'id', nullable: true)]\n    private CompanyEvent|null $mainevent = null;\n\n    public function getMainEvent(): CompanyEvent|null\n    {\n        return $this->mainevent;\n    }\n\n    public function setMainEvent(CompanyEvent $event): void\n    {\n        $this->mainevent = $event;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyPerson.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Description of CompanyPerson\n */\n#[Table(name: 'company_persons')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['person' => 'CompanyPerson', 'manager' => 'CompanyManager', 'employee' => 'CompanyEmployee'])]\nclass CompanyPerson\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column]\n    private string|null $name = null;\n\n    #[OneToOne(targetEntity: 'CompanyPerson')]\n    #[JoinColumn(name: 'spouse_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    private CompanyPerson|null $spouse = null;\n\n    /** @phpstan-var Collection<int, CompanyPerson> */\n    #[JoinTable(name: 'company_persons_friends')]\n    #[JoinColumn(name: 'person_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[InverseJoinColumn(name: 'friend_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[ManyToMany(targetEntity: 'CompanyPerson')]\n    private $friends;\n\n    public function __construct()\n    {\n        $this->friends = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getSpouse(): CompanyPerson|null\n    {\n        return $this->spouse;\n    }\n\n    /** @phpstan-return Collection<int, CompanyPerson> */\n    public function getFriends(): Collection\n    {\n        return $this->friends;\n    }\n\n    public function addFriend(CompanyPerson $friend): void\n    {\n        if (! $this->friends->contains($friend)) {\n            $this->friends->add($friend);\n            $friend->addFriend($this);\n        }\n    }\n\n    public function setSpouse(CompanyPerson $spouse): void\n    {\n        if ($spouse !== $this->spouse) {\n            $this->spouse = $spouse;\n            $this->spouse->setSpouse($this);\n        }\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setPrimaryTable(\n            ['name' => 'company_person'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Company/CompanyRaffle.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Company;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'company_raffles')]\n#[Entity]\nclass CompanyRaffle extends CompanyEvent\n{\n    #[Column]\n    private string|null $data = null;\n\n    public function setData(string $data): void\n    {\n        $this->data = $data;\n    }\n\n    public function getData(): string\n    {\n        return $this->data;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass JoinedChildClass extends JoinedRootClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $extension = 'ext';\n\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    private string $additionalId = 'additional';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'joined_derived_child')]\n#[Entity]\nclass JoinedDerivedChildClass extends JoinedDerivedRootClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $extension = 'ext';\n\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    private string $additionalId = 'additional';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedIdentityClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'joined_derived_identity')]\n#[Entity]\nclass JoinedDerivedIdentityClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $id = 'part-0';\n\n    /** @var JoinedDerivedRootClass[] */\n    #[OneToMany(targetEntity: 'JoinedDerivedRootClass', mappedBy: 'keyPart1')]\n    protected $children;\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedRootClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'joined_derived_root')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['child' => 'JoinedDerivedChildClass', 'root' => 'JoinedDerivedRootClass'])]\nclass JoinedDerivedRootClass\n{\n    /** @var JoinedDerivedIdentityClass */\n    #[ManyToOne(targetEntity: 'JoinedDerivedIdentityClass', inversedBy: 'children')]\n    #[Id]\n    protected $keyPart1 = 'part-1';\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $keyPart2 = 'part-2';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['child' => 'JoinedChildClass', 'root' => 'JoinedRootClass'])]\nclass JoinedRootClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $keyPart1 = 'part-1';\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $keyPart2 = 'part-2';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/SingleChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass SingleChildClass extends SingleRootClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $extension = 'ext';\n\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    private string $additionalId = 'additional';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyInheritance/SingleRootClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['child' => 'SingleChildClass', 'root' => 'SingleRootClass'])]\nclass SingleRootClass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $keyPart1 = 'part-1';\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    #[Id]\n    protected $keyPart2 = 'part-2';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyRelations/CustomerClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyRelations;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass CustomerClass\n{\n    #[Id]\n    #[Column(type: 'string')]\n    public string $companyCode;\n\n    #[Id]\n    #[Column(type: 'string')]\n    public string $code;\n\n    #[Column(type: 'string')]\n    public string $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CompositeKeyRelations;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass InvoiceClass\n{\n    #[Id]\n    #[Column(type: 'string')]\n    public string $companyCode;\n\n    #[Id]\n    #[Column(type: 'string')]\n    public string $invoiceNumber;\n\n    #[ManyToOne(targetEntity: CustomerClass::class)]\n    #[JoinColumn(name: 'companyCode', referencedColumnName: 'companyCode')]\n    #[JoinColumn(name: 'customerCode', referencedColumnName: 'code')]\n    public CustomerClass|null $customer;\n\n    #[Column(type: 'string', nullable: true)]\n    public string|null $customerCode = null;\n}\n"
  },
  {
    "path": "tests/Tests/Models/CustomType/CustomIdObjectTypeChild.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CustomType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\n\n#[Table(name: 'custom_id_type_child')]\n#[Entity]\nclass CustomIdObjectTypeChild\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'CustomIdObject', length: 255)]\n        public CustomIdObject $id,\n        #[ManyToOne(targetEntity: 'Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeParent', inversedBy: 'children')]\n        public CustomIdObjectTypeParent $parent,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CustomType/CustomIdObjectTypeParent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CustomType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\n\n#[Table(name: 'custom_id_type_parent')]\n#[Entity]\nclass CustomIdObjectTypeParent\n{\n    /** @phpstan-var Collection<int, CustomIdObjectTypeChild> */\n    #[OneToMany(targetEntity: 'Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeChild', cascade: ['persist', 'remove'], mappedBy: 'parent')]\n    public $children;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'CustomIdObject', length: 255)]\n        public CustomIdObject $id,\n    ) {\n        $this->children = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CustomType/CustomTypeChild.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CustomType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'customtype_children')]\n#[Entity]\nclass CustomTypeChild\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'upper_case_string', length: 255)]\n    public $lowerCaseString = 'foo';\n}\n"
  },
  {
    "path": "tests/Tests/Models/CustomType/CustomTypeParent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CustomType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'customtype_parents')]\n#[Entity]\nclass CustomTypeParent\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'negative_to_positive', nullable: true)]\n    public $customInteger;\n\n    /** @var CustomTypeChild */\n    #[OneToOne(targetEntity: 'Doctrine\\Tests\\Models\\CustomType\\CustomTypeChild', cascade: ['persist', 'remove'])]\n    public $child;\n\n    /** @phpstan-var Collection<int, CustomTypeParent> */\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent', mappedBy: 'myFriends')]\n    private $friendsWithMe;\n\n    /** @phpstan-var Collection<int, CustomTypeParent> */\n    #[JoinTable(name: 'customtype_parent_friends')]\n    #[JoinColumn(name: 'customtypeparent_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'friend_customtypeparent_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent', inversedBy: 'friendsWithMe')]\n    private $myFriends;\n\n    public function __construct()\n    {\n        $this->friendsWithMe = new ArrayCollection();\n        $this->myFriends     = new ArrayCollection();\n    }\n\n    public function addMyFriend(CustomTypeParent $friend): void\n    {\n        $this->getMyFriends()->add($friend);\n        $friend->addFriendWithMe($this);\n    }\n\n    /** @phpstan-return Collection<int, CustomTypeParent> */\n    public function getMyFriends(): Collection\n    {\n        return $this->myFriends;\n    }\n\n    public function addFriendWithMe(CustomTypeParent $friend): void\n    {\n        $this->getFriendsWithMe()->add($friend);\n    }\n\n    /** @phpstan-return Collection<int, CustomTypeParent> */\n    public function getFriendsWithMe()\n    {\n        return $this->friendsWithMe;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/CustomType/CustomTypeUpperCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\CustomType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'customtype_uppercases')]\n#[Entity]\nclass CustomTypeUpperCase\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'upper_case_string', length: 255)]\n    public $lowerCaseString;\n\n    /** @var string */\n    #[Column(type: 'upper_case_string', length: 255, name: 'named_lower_case_string', nullable: true)]\n    public $namedLowerCaseString;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Customer/CustomerType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Customer;\n\nclass CustomerType\n{\n    /** @var string */\n    public $name;\n\n    public function __construct(string $name)\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Customer/ExternalCustomer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Customer;\n\nfinal class ExternalCustomer extends CustomerType\n{\n    public function __construct(string $name)\n    {\n        parent::__construct($name);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Customer/InternalCustomer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Customer;\n\nfinal class InternalCustomer extends CustomerType\n{\n    public function __construct(string $name)\n    {\n        parent::__construct($name);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117ApproveChanges.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass DDC117ApproveChanges\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC117ArticleDetails')]\n        #[JoinColumn(name: 'details_id', referencedColumnName: 'article_id')]\n        private DDC117ArticleDetails $articleDetails,\n        #[JoinColumn(name: 'source_id', referencedColumnName: 'source_id')]\n        #[JoinColumn(name: 'target_id', referencedColumnName: 'target_id')]\n        #[ManyToOne(targetEntity: 'DDC117Reference')]\n        private DDC117Reference $reference,\n        #[JoinColumn(name: 'trans_article_id', referencedColumnName: 'article_id')]\n        #[JoinColumn(name: 'trans_language', referencedColumnName: 'language')]\n        #[ManyToOne(targetEntity: 'DDC117Translation')]\n        private DDC117Translation $translation,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getArticleDetails(): DDC117ArticleDetails\n    {\n        return $this->articleDetails;\n    }\n\n    public function getReference(): DDC117Reference\n    {\n        return $this->reference;\n    }\n\n    public function getTranslation(): DDC117Translation\n    {\n        return $this->translation;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass DDC117Article\n{\n    #[Id]\n    #[Column(type: 'integer', name: 'article_id')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, DDC117Reference> */\n    #[OneToMany(targetEntity: 'DDC117Reference', mappedBy: 'source', cascade: ['remove'])]\n    private $references;\n\n    #[OneToOne(targetEntity: 'DDC117ArticleDetails', mappedBy: 'article', cascade: ['persist', 'remove'])]\n    private DDC117ArticleDetails|null $details = null;\n\n    /** @phpstan-var Collection<int, DDC117Translation> */\n    #[OneToMany(targetEntity: 'DDC117Translation', mappedBy: 'article', cascade: ['persist', 'remove'])]\n    private $translations;\n\n    /** @var Collection<int, DDC117Translation> */\n    #[OneToMany(targetEntity: 'DDC117Link', mappedBy: 'source', indexBy: 'target_id', cascade: ['persist', 'remove'])]\n    private Collection $links;\n\n    public function __construct(\n        #[Column]\n        private string $title,\n    ) {\n        $this->references   = new ArrayCollection();\n        $this->translations = new ArrayCollection();\n    }\n\n    public function setDetails(DDC117ArticleDetails $details): void\n    {\n        $this->details = $details;\n    }\n\n    public function id(): int\n    {\n        return $this->id;\n    }\n\n    public function addReference(DDC117Reference $reference): void\n    {\n        $this->references[] = $reference;\n    }\n\n    public function references(): Collection\n    {\n        return $this->references;\n    }\n\n    public function addTranslation(string $language, string $title): void\n    {\n        $this->translations[] = new DDC117Translation($this, $language, $title);\n    }\n\n    public function getText(): string\n    {\n        return $this->details->getText();\n    }\n\n    public function getDetails(): DDC117ArticleDetails\n    {\n        return $this->details;\n    }\n\n    public function getLinks(): Collection\n    {\n        return $this->links;\n    }\n\n    public function resetText(): void\n    {\n        $this->details = null;\n    }\n\n    public function getTranslations(): Collection\n    {\n        return $this->translations;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117ArticleDetails.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass DDC117ArticleDetails\n{\n    #[Column(type: 'text')]\n    private string $text;\n\n    public function __construct(\n        /** @var DDC117Article */\n        #[Id]\n        #[OneToOne(targetEntity: 'DDC117Article', inversedBy: 'details')]\n        #[JoinColumn(name: 'article_id', referencedColumnName: 'article_id')]\n        private $article,\n        string $text,\n    ) {\n        $article->setDetails($this);\n\n        $this->update($text);\n    }\n\n    public function update(string $text): void\n    {\n        $this->text = $text;\n    }\n\n    public function getText(): string\n    {\n        return $this->text;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117Editor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass DDC117Editor\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC117Translation> */\n    #[JoinTable]\n    #[JoinColumn(name: 'editor_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'article_id', referencedColumnName: 'article_id')]\n    #[InverseJoinColumn(name: 'language', referencedColumnName: 'language')]\n    #[ManyToMany(targetEntity: 'DDC117Translation', inversedBy: 'reviewedByEditors')]\n    public $reviewingTranslations;\n\n    /** @var DDC117Translation */\n    #[JoinColumn(name: 'lt_article_id', referencedColumnName: 'article_id')]\n    #[JoinColumn(name: 'lt_language', referencedColumnName: 'language')]\n    #[ManyToOne(targetEntity: 'DDC117Translation', inversedBy: 'lastTranslatedBy')]\n    public $lastTranslation;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string|null $name = '',\n    ) {\n        $this->reviewingTranslations = new ArrayCollection();\n    }\n\n    public function addLastTranslation(DDC117Translation $t): void\n    {\n        $this->lastTranslation = $t;\n        $t->lastTranslatedBy[] = $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117Link.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n/**\n * Foreign Key Entity without additional fields!\n */\n#[Entity]\nclass DDC117Link\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'links')]\n        #[JoinColumn(name: 'source_id', referencedColumnName: 'article_id')]\n        public DDC117Article $source,\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC117Article')]\n        #[JoinColumn(name: 'target_id', referencedColumnName: 'article_id')]\n        public DDC117Article $target,\n        $description,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117Reference.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass DDC117Reference\n{\n    /** @var DDC117Article */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'references')]\n    #[JoinColumn(name: 'source_id', referencedColumnName: 'article_id')]\n    private $source;\n\n    /** @var DDC117Article */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC117Article')]\n    #[JoinColumn(name: 'target_id', referencedColumnName: 'article_id')]\n    private $target;\n\n    #[Column(type: 'datetime')]\n    private DateTime $created;\n\n    public function __construct(\n        DDC117Article $source,\n        DDC117Article $target,\n        #[Column(type: 'string', length: 255)]\n        private string $description,\n    ) {\n        $source->addReference($this);\n        $target->addReference($this);\n\n        $this->source  = $source;\n        $this->target  = $target;\n        $this->created = new DateTime('now');\n    }\n\n    public function source(): DDC117Article\n    {\n        return $this->source;\n    }\n\n    public function target(): DDC117Article\n    {\n        return $this->target;\n    }\n\n    public function setDescription(string $desc): void\n    {\n        $this->description = $desc;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC117/DDC117Translation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC117;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\n\n#[Entity]\nclass DDC117Translation\n{\n    /** @var Collection<int, DDC117Editor> */\n    #[ManyToMany(targetEntity: 'DDC117Editor', mappedBy: 'reviewingTranslations')]\n    public $reviewedByEditors;\n\n    /** @var Collection<int, DDC117Editor> */\n    #[OneToMany(targetEntity: 'DDC117Editor', mappedBy: 'lastTranslation')]\n    public $lastTranslatedBy;\n\n    public function __construct(\n        /** @var DDC117Article */\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'translations')]\n        #[JoinColumn(name: 'article_id', referencedColumnName: 'article_id')]\n        private $article,\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        private string $language,\n        #[Column(type: 'string', length: 255)]\n        private string $title,\n    ) {\n        $this->reviewedByEditors = new ArrayCollection();\n        $this->lastTranslatedBy  = new ArrayCollection();\n    }\n\n    public function getArticleId(): int\n    {\n        return $this->article->id();\n    }\n\n    public function getLanguage(): string\n    {\n        return $this->language;\n    }\n\n    public function getLastTranslatedBy(): Collection\n    {\n        return $this->lastTranslatedBy;\n    }\n\n    public function getReviewedByEditors(): Collection\n    {\n        return $this->reviewedByEditors;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1476/DDC1476EntityWithDefaultFieldType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1476;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\Entity]\nclass DDC1476EntityWithDefaultFieldType\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    protected $id;\n\n    /** @var string */\n    #[ORM\\Column]\n    protected $name;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'         => true,\n                'fieldName'  => 'id',\n            ],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'name'],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1590/DDC1590Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1590;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nabstract class DDC1590Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    protected $createdAt;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setCreatedAt(DateTime $createdAt): DDC1590User\n    {\n        $this->createdAt = $createdAt;\n\n        return $this;\n    }\n\n    public function getCreatedAt(): DateTime\n    {\n        return $this->createdAt;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1590/DDC1590User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1590;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\Models\\DDC1590\\DDC1590Entity;\n\n#[Table(name: 'users')]\n#[Entity]\nclass DDC1590User extends DDC1590Entity\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1872/DDC1872Bar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1872;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass DDC1872Bar\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    private string $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithOverride.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1872;\n\nuse Doctrine\\ORM\\Mapping\\AssociationOverride;\nuse Doctrine\\ORM\\Mapping\\AssociationOverrides;\nuse Doctrine\\ORM\\Mapping\\AttributeOverride;\nuse Doctrine\\ORM\\Mapping\\AttributeOverrides;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\n\n#[Entity]\n#[AttributeOverrides([new AttributeOverride(name: 'foo', column: new Column(name: 'foo_overridden', type: 'integer', length: 140, nullable: false, unique: false))])]\n#[AssociationOverrides([new AssociationOverride(name: 'bar', joinColumns: new JoinColumn(name: 'example_entity_overridden_bar_id', referencedColumnName: 'id'))])]\nclass DDC1872ExampleEntityWithOverride\n{\n    use DDC1872ExampleTrait;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithoutOverride.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1872;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass DDC1872ExampleEntityWithoutOverride\n{\n    use DDC1872ExampleTrait;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC1872;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\ntrait DDC1872ExampleTrait\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    private $id;\n\n    /** @var int */\n    #[Column(name: 'trait_foo', type: 'integer', length: 100, nullable: true, unique: true)]\n    protected $foo;\n\n    /** @var DDC1872Bar */\n    #[OneToOne(targetEntity: 'DDC1872Bar', cascade: ['persist'])]\n    #[JoinColumn(name: 'example_trait_bar_id', referencedColumnName: 'id')]\n    protected $bar;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2372/DDC2372Address.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2372;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'addresses')]\n#[Entity]\nclass DDC2372Address\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $street = null;\n\n    /** @var User */\n    #[OneToOne(targetEntity: 'User', mappedBy: 'address')]\n    private $user;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getStreet(): string\n    {\n        return $this->street;\n    }\n\n    public function setStreet(string $street): void\n    {\n        $this->street = $street;\n    }\n\n    public function getUser(): User\n    {\n        return $this->user;\n    }\n\n    public function setUser(User $user): void\n    {\n        if ($this->user !== $user) {\n            $this->user = $user;\n            $user->setAddress($this);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2372/DDC2372Admin.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2372;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'admins')]\n#[Entity]\nclass DDC2372Admin extends DDC2372User\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2372/DDC2372User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2372;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\Models\\DDC2372\\Traits\\DDC2372AddressAndAccessors;\n\n#[Table(name: 'users')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn('type')]\n#[DiscriminatorMap(['user' => 'DDC2372User', 'admin' => 'DDC2372Admin'])]\nclass DDC2372User\n{\n    use DDC2372AddressAndAccessors;\n\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 50)]\n    private string|null $name = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2372/Traits/DDC2372AddressAndAccessors.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2372\\Traits;\n\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\Models\\DDC2372\\DDC2372Address;\n\ntrait DDC2372AddressAndAccessors\n{\n    /** @var DDC2372Address */\n    #[OneToOne(targetEntity: 'Doctrine\\Tests\\Models\\DDC2372\\DDC2372Address', inversedBy: 'user')]\n    #[JoinColumn(name: 'address_id', referencedColumnName: 'id')]\n    private $address;\n\n    public function getAddress(): DDC2372Address\n    {\n        return $this->address;\n    }\n\n    public function setAddress(DDC2372Address $address): void\n    {\n        if ($this->address !== $address) {\n            $this->address = $address;\n            $address->setUser($this);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2504/DDC2504ChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2504;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass DDC2504ChildClass extends DDC2504RootClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2504/DDC2504OtherClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2504;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\PersistentCollection;\n\n#[Entity]\nclass DDC2504OtherClass\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /**\n     * @var DDC2504ChildClass\n     * @var ArrayCollection|PersistentCollection\n     */\n    #[OneToMany(targetEntity: 'DDC2504ChildClass', mappedBy: 'other', fetch: 'EXTRA_LAZY')]\n    public $childClasses;\n\n    public function __construct()\n    {\n        $this->childClasses = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2504/DDC2504RootClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2504;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['root' => 'DDC2504RootClass', 'child' => 'DDC2504ChildClass'])]\nclass DDC2504RootClass\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2504OtherClass */\n    #[ManyToOne(targetEntity: 'DDC2504OtherClass', inversedBy: 'childClasses')]\n    public $other;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2825/ExplicitSchemaAndTable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2825;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'explicit_table', schema: 'explicit_schema')]\nclass ExplicitSchemaAndTable\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC2825/SchemaAndTableInTableName.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC2825;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n/**\n * Quoted column name to check that sequence names are\n * correctly handled\n */\n#[ORM\\Entity]\n#[ORM\\Table(name: 'implicit_schema.implicit_table')]\nclass SchemaAndTableInTableName\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3231/DDC3231EntityRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3231;\n\nuse Doctrine\\ORM\\EntityRepository;\n\nclass DDC3231EntityRepository extends EntityRepository\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3231/DDC3231User1.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3231;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'users')]\n#[Entity(repositoryClass: 'DDC3231User1Repository')]\nclass DDC3231User1\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'no_namespace_users')]\n#[Entity(repositoryClass: 'DDC3231User1NoNamespaceRepository')]\nclass DDC3231User1NoNamespace\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3231/DDC3231User2.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3231;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'users2')]\n#[Entity(repositoryClass: 'DDC3231User2Repository')]\nclass DDC3231User2\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'no_namespace_users2')]\n#[Entity(repositoryClass: 'DDC3231User2NoNamespaceRepository')]\nclass DDC3231User2NoNamespace\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3293/DDC3293Address.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3293;\n\nclass DDC3293Address\n{\n    /** @var string */\n    public $street;\n\n    /** @var string */\n    public $city;\n\n    /** @var string */\n    public $country;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3293/DDC3293User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3293;\n\nclass DDC3293User\n{\n    /** @var string */\n    protected $id;\n\n    /** @var DDC3293Address */\n    protected $address;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3293/DDC3293UserPrefixed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3293;\n\nclass DDC3293UserPrefixed\n{\n    /** @var string */\n    protected $id;\n\n    /** @var DDC3293Address */\n    protected $address;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3346/DDC3346Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3346;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'ddc3346_articles')]\n#[Entity]\nclass DDC3346Article\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC3346Author */\n    #[ManyToOne(targetEntity: 'DDC3346Author', inversedBy: 'articles')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3346/DDC3346Author.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3346;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'ddc3346_users')]\n#[Entity]\nclass DDC3346Author\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, unique: true)]\n    public $username;\n\n    /** @var Collection<int, DDC3346Article> */\n    #[OneToMany(targetEntity: 'DDC3346Article', mappedBy: 'user', fetch: 'EAGER', cascade: ['detach'])]\n    public $articles = [];\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3579/DDC3579Admin.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3579;\n\nuse Doctrine\\ORM\\Mapping\\AssociationOverride;\nuse Doctrine\\ORM\\Mapping\\AssociationOverrides;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\n#[AssociationOverrides([new AssociationOverride(name: 'groups', inversedBy: 'admins')])]\nclass DDC3579Admin extends DDC3579User\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setAssociationOverride('groups', ['inversedBy' => 'admins']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3579/DDC3579Group.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3579;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass DDC3579Group\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    /** @phpstan-var Collection<int, DDC3579Admin> */\n    #[ManyToMany(targetEntity: DDC3579Admin::class, mappedBy: 'groups')]\n    private $admins;\n\n    public function __construct(\n        #[Column]\n        private string|null $name = null,\n    ) {\n        $this->admins = new ArrayCollection();\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function addAdmin(DDC3579Admin $admin): void\n    {\n        $this->admins[] = $admin;\n    }\n\n    /** @phpstan-return Collection<int, DDC3579Admin> */\n    public function getAdmins(): Collection\n    {\n        return $this->admins;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3579/DDC3579User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3579;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nclass DDC3579User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: 'user_id', length: 150)]\n    protected $id;\n\n    /** @var ArrayCollection */\n    #[ManyToMany(targetEntity: DDC3579Group::class)]\n    protected $groups;\n\n    public function __construct(\n        #[Column(name: 'user_name', nullable: true, unique: false, length: 250)]\n        protected string|null $name = null,\n    ) {\n        $this->groups = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function addGroup(DDC3579Group $group): void\n    {\n        $this->groups->add($group);\n        $group->addUser($this);\n    }\n\n    public function getGroups(): ArrayCollection\n    {\n        return $this->groups;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->isMappedSuperclass = true;\n\n        $metadata->mapField(\n            [\n                'id'         => true,\n                'fieldName'  => 'id',\n                'type'       => 'integer',\n                'columnName' => 'user_id',\n                'length'     => 150,\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type'      => 'string',\n                'columnName' => 'user_name',\n                'nullable'  => true,\n                'unique'    => false,\n                'length'    => 250,\n            ],\n        );\n\n        $metadata->mapManyToMany(\n            [\n                'fieldName'      => 'groups',\n                'targetEntity'   => 'DDC3579Group',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3597/DDC3597Image.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3597;\n\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\Tests\\Models\\DDC3597\\Embeddable\\DDC3597Dimension;\n\n/**\n * Description of Image\n */\n#[Entity]\nclass DDC3597Image extends DDC3597Media\n{\n    #[Embedded(class: 'Doctrine\\Tests\\Models\\DDC3597\\Embeddable\\DDC3597Dimension', columnPrefix: false)]\n    private DDC3597Dimension $dimension;\n\n    public function __construct(string $distributionHash)\n    {\n        parent::__construct($distributionHash);\n\n        $this->dimension = new DDC3597Dimension();\n    }\n\n    public function getDimension(): DDC3597Dimension\n    {\n        return $this->dimension;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3597/DDC3597Media.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3597;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n/**\n * Description of Media\n */\n#[Entity]\nabstract class DDC3597Media extends DDC3597Root\n{\n    #[Column]\n    private int $size = 0;\n\n    #[Column]\n    private string|null $format = null;\n\n    public function __construct(\n        #[Column]\n        private string $distributionHash,\n    ) {\n    }\n\n    public function getDistributionHash(): string\n    {\n        return $this->distributionHash;\n    }\n\n    public function getSize(): int\n    {\n        return $this->size;\n    }\n\n    public function setSize(int $size): void\n    {\n        $this->size = $size;\n    }\n\n    public function getFormat(): string\n    {\n        return $this->format;\n    }\n\n    public function setFormat(string $format): void\n    {\n        $this->format = $format;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3597/DDC3597Root.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3597;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\PrePersist;\nuse Doctrine\\ORM\\Mapping\\PreUpdate;\n\n/**\n * Description of Root\n */\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discriminator', type: 'string', length: 255)]\n#[DiscriminatorMap(['image' => 'DDC3597Image'])]\n#[HasLifecycleCallbacks]\nabstract class DDC3597Root\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer', nullable: false)]\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    protected $id;\n\n    /** @var DateTime */\n    #[Column(name: 'created_at', type: 'datetime', nullable: false)]\n    protected $createdAt = null;\n\n    /** @var DateTime */\n    #[Column(name: 'updated_at', type: 'datetime', nullable: false)]\n    protected $updatedAt = null;\n\n    /**\n     * Set createdAt\n     */\n    #[PrePersist]\n    public function prePersist(): void\n    {\n        $this->updatedAt = $this->createdAt = new DateTime();\n    }\n\n    /**\n     * Set updatedAt\n     */\n    #[PreUpdate]\n    public function preUpdate(): void\n    {\n        $this->updatedAt = new DateTime();\n    }\n\n    public function getId(): int\n    {\n        return (int) $this->id;\n    }\n\n    public function getCreatedAt(): DateTime\n    {\n        return $this->createdAt;\n    }\n\n    public function getUpdatedAt(): DateTime\n    {\n        return $this->updatedAt;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3597/Embeddable/DDC3597Dimension.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3597\\Embeddable;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\n\n/**\n * Description of DDC3597Dimension\n */\n#[Embeddable]\nclass DDC3597Dimension\n{\n    #[Column(type: 'integer', name: 'width')]\n    private int $width;\n\n    #[Column(type: 'integer', name: 'height')]\n    private int $height;\n\n    public function __construct($width = 0, $height = 0)\n    {\n        $this->setWidth($width);\n        $this->setHeight($height);\n    }\n\n    public function getWidth(): int\n    {\n        return $this->width;\n    }\n\n    public function setWidth(int $width): void\n    {\n        $this->width = (int) $width;\n    }\n\n    public function getHeight(): int\n    {\n        return $this->height;\n    }\n\n    public function setHeight(int $height): void\n    {\n        $this->height = (int) $height;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3699/DDC3699Child.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3699;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'ddc3699_child')]\n#[Entity]\nclass DDC3699Child extends DDC3699Parent\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $childField;\n\n    /** @var DDC3699RelationOne */\n    #[OneToOne(targetEntity: 'DDC3699RelationOne', inversedBy: 'child')]\n    public $oneRelation;\n\n    /** @phpstan-var Collection<int, DDC3699RelationMany> */\n    #[OneToMany(targetEntity: 'DDC3699RelationMany', mappedBy: 'child')]\n    public $relations;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3699/DDC3699Parent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3699;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nabstract class DDC3699Parent\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $parentField;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3699/DDC3699RelationMany.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3699;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'ddc3699_relation_many')]\n#[Entity]\nclass DDC3699RelationMany\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC3699Child */\n    #[ManyToOne(targetEntity: 'DDC3699Child', inversedBy: 'relations')]\n    public $child;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3699/DDC3699RelationOne.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3699;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'ddc3699_relation_one')]\n#[Entity]\nclass DDC3699RelationOne\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC3699Child */\n    #[OneToOne(targetEntity: 'DDC3699Child', mappedBy: 'oneRelation')]\n    public $child;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3711/DDC3711EntityA.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3711;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\n\nclass DDC3711EntityA\n{\n    /** @var int */\n    private $id1;\n\n    /** @var int */\n    private $id2;\n\n    /** @var ArrayCollection */\n    private $entityB;\n\n    public function getId1(): mixed\n    {\n        return $this->id1;\n    }\n\n    public function setId1(mixed $id1): void\n    {\n        $this->id1 = $id1;\n    }\n\n    public function getId2(): mixed\n    {\n        return $this->id2;\n    }\n\n    public function setId2(mixed $id2): void\n    {\n        $this->id2 = $id2;\n    }\n\n    public function getEntityB(): ArrayCollection\n    {\n        return $this->entityB;\n    }\n\n    public function addEntityB(ArrayCollection $entityB): DDC3711EntityA\n    {\n        $this->entityB[] = $entityB;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3711/DDC3711EntityB.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3711;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\n\nclass DDC3711EntityB\n{\n    private int|null $id1 = null;\n\n    private int|null $id2 = null;\n\n    /** @var ArrayCollection */\n    private $entityA;\n\n    public function getId1(): int\n    {\n        return $this->id1;\n    }\n\n    public function setId1(int $id1): void\n    {\n        $this->id1 = $id1;\n    }\n\n    public function getId2(): int\n    {\n        return $this->id2;\n    }\n\n    public function setId2(int $id2): void\n    {\n        $this->id2 = $id2;\n    }\n\n    public function getEntityA(): ArrayCollection\n    {\n        return $this->entityA;\n    }\n\n    public function addEntityA(ArrayCollection $entityA): void\n    {\n        $this->entityA[] = $entityA;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3899/DDC3899Contract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3899;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'dc3899_contracts')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['fix' => 'DDC3899FixContract', 'flexible' => 'DDC3899FlexContract'])]\nabstract class DDC3899Contract\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var bool */\n    #[Column(type: 'boolean')]\n    public $completed = false;\n\n    /** @var DDC3899User */\n    #[ManyToOne(targetEntity: 'DDC3899User', inversedBy: 'contract')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3899/DDC3899FixContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3899;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass DDC3899FixContract extends DDC3899Contract\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $fixPrice = 0;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3899/DDC3899FlexContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3899;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass DDC3899FlexContract extends DDC3899Contract\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $hoursWorked = 0;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $pricePerHour = 0;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC3899/DDC3899User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC3899;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'dc3899_users')]\n#[Entity]\nclass DDC3899User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC3899Contract> */\n    #[OneToMany(targetEntity: 'DDC3899Contract', mappedBy: 'user')]\n    public $contracts;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC4006/DDC4006User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC4006;\n\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass DDC4006User\n{\n    #[Embedded(class: 'DDC4006UserId')]\n    private DDC4006UserId $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC4006/DDC4006UserId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC4006;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Embeddable]\nclass DDC4006UserId\n{\n    #[Id]\n    #[GeneratedValue('IDENTITY')]\n    #[Column(type: 'integer')]\n    private int $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC5934/DDC5934BaseContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC5934;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nclass DDC5934BaseContract\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC5934Member> */\n    #[ManyToMany(targetEntity: DDC5934Member::class, fetch: 'LAZY', inversedBy: 'contracts')]\n    public $members;\n\n    public function __construct()\n    {\n        $this->members = new ArrayCollection();\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->isMappedSuperclass = true;\n\n        $metadata->mapField([\n            'id'         => true,\n            'fieldName'  => 'id',\n            'type'       => 'integer',\n            'columnName' => 'id',\n        ]);\n\n        $metadata->mapManyToMany([\n            'fieldName'    => 'members',\n            'targetEntity' => 'DDC5934Member',\n        ]);\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC5934/DDC5934Contract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC5934;\n\nuse Doctrine\\ORM\\Mapping\\AssociationOverride;\nuse Doctrine\\ORM\\Mapping\\AssociationOverrides;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\n#[AssociationOverrides([new AssociationOverride(name: 'members', fetch: 'EXTRA_LAZY')])]\nclass DDC5934Contract extends DDC5934BaseContract\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setAssociationOverride('members', [\n            'fetch' => ClassMetadata::FETCH_EXTRA_LAZY,\n        ]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC5934/DDC5934Member.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC5934;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass DDC5934Member\n{\n    /** @var ArrayCollection */\n    #[ORM\\ManyToMany(targetEntity: DDC5934BaseContract::class, mappedBy: 'members')]\n    public $contracts;\n\n    public function __construct()\n    {\n        $this->contracts = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC6412/DDC6412File.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC6412;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\n\n#[Entity]\nclass DDC6412File\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(length: 50, name: 'file_name')]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC6573/DDC6573Currency.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC6573;\n\nfinal class DDC6573Currency\n{\n    public function __construct(private readonly string $code)\n    {\n    }\n\n    public function getCode(): string\n    {\n        return $this->code;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC6573/DDC6573Item.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC6573;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'ddc6573_items')]\nclass DDC6573Item\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: Types::INTEGER)]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    #[Column(type: Types::STRING)]\n    public string $name;\n\n    #[Column(type: Types::INTEGER)]\n    public int $priceAmount;\n\n    #[Column(type: Types::STRING, length: 3)]\n    public string $priceCurrency;\n\n    public function __construct(string $name, DDC6573Money $price)\n    {\n        $this->name          = $name;\n        $this->priceAmount   = $price->getAmount();\n        $this->priceCurrency = $price->getCurrency()->getCode();\n    }\n\n    public function getPrice(): DDC6573Money\n    {\n        return new DDC6573Money($this->priceAmount, new DDC6573Currency($this->priceCurrency));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC6573/DDC6573Money.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC6573;\n\nfinal class DDC6573Money\n{\n    public function __construct(\n        private readonly int $amount,\n        private readonly DDC6573Currency $currency,\n    ) {\n    }\n\n    public function getAmount(): int\n    {\n        return $this->amount;\n    }\n\n    public function getCurrency(): DDC6573Currency\n    {\n        return $this->currency;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753CustomRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nuse Doctrine\\ORM\\EntityRepository;\n\nclass DDC753CustomRepository extends EntityRepository\n{\n    public function isCustomRepository(): bool\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753DefaultRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nuse Doctrine\\ORM\\EntityRepository;\n\nclass DDC753DefaultRepository extends EntityRepository\n{\n    public function isDefaultRepository(): bool\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753EntityWithCustomRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity(repositoryClass: 'Doctrine\\Tests\\Models\\DDC753\\DDC753CustomRepository')]\nclass DDC753EntityWithCustomRepository\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753EntityWithDefaultCustomRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass DDC753EntityWithDefaultCustomRepository\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753EntityWithInvalidRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity(repositoryClass: '\\stdClass')]\nclass DDC753EntityWithInvalidRepository\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC753/DDC753InvalidRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC753;\n\nclass DDC753InvalidRepository\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC869/DDC869ChequePayment.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC869;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\Entity]\nclass DDC869ChequePayment extends DDC869Payment\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    protected $serialNumber;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'fieldName'  => 'serialNumber',\n                'type'       => 'string',\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC869/DDC869CreditCardPayment.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC869;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\Entity]\nclass DDC869CreditCardPayment extends DDC869Payment\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    protected $creditCardNumber;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'fieldName'  => 'creditCardNumber',\n                'type'       => 'string',\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC869/DDC869Payment.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC869;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\MappedSuperclass(repositoryClass: DDC869PaymentRepository::class)]\nclass DDC869Payment\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    protected $id;\n\n    /** @var float */\n    #[ORM\\Column(type: 'float')]\n    protected $value;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'         => true,\n                'fieldName'  => 'id',\n                'type'       => 'integer',\n                'columnName' => 'id',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName'  => 'value',\n                'type'       => 'float',\n            ],\n        );\n        $metadata->isMappedSuperclass = true;\n        $metadata->setCustomRepositoryClass(DDC869PaymentRepository::class);\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC869/DDC869PaymentRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC869;\n\nuse Doctrine\\ORM\\EntityRepository;\n\nclass DDC869PaymentRepository extends EntityRepository\n{\n    /**\n     * Very complex method\n     */\n    public function isTrue(): bool\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC889/DDC889Class.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC889;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\nclass DDC889Class extends DDC889SuperClass\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'         => true,\n                'fieldName'  => 'id',\n                'type'       => 'integer',\n                'columnName' => 'id',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC889/DDC889Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC889;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\Entity]\nclass DDC889Entity extends DDC889SuperClass\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC889/DDC889SuperClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC889;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n#[ORM\\MappedSuperclass]\nclass DDC889SuperClass\n{\n    /** @var string */\n    #[ORM\\Column]\n    protected $name;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            ['fieldName' => 'name'],\n        );\n\n        $metadata->isMappedSuperclass = true;\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC964/DDC964Address.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC964;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass DDC964Address\n{\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function __construct(\n        #[Column]\n        private string|null $zip = null,\n        #[Column]\n        private string|null $country = null,\n        #[Column]\n        private string|null $city = null,\n        #[Column]\n        private string|null $street = null,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getCountry(): string|null\n    {\n        return $this->country;\n    }\n\n    public function setCountry(string $country): void\n    {\n        $this->country = $country;\n    }\n\n    public function getZip(): string|null\n    {\n        return $this->zip;\n    }\n\n    public function setZip(string $zip): void\n    {\n        $this->zip = $zip;\n    }\n\n    public function getCity(): string|null\n    {\n        return $this->city;\n    }\n\n    public function setCity(string $city): void\n    {\n        $this->city = $city;\n    }\n\n    public function getStreet(): string|null\n    {\n        return $this->street;\n    }\n\n    public function setStreet(string $street): void\n    {\n        $this->street = $street;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC964/DDC964Admin.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC964;\n\nuse Doctrine\\ORM\\Mapping\\AssociationOverride;\nuse Doctrine\\ORM\\Mapping\\AssociationOverrides;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\n\n#[Entity]\n#[AssociationOverrides([new AssociationOverride(name: 'groups', joinTable: new JoinTable(name: 'ddc964_users_admingroups'), joinColumns: [new JoinColumn(name: 'adminuser_id')], inverseJoinColumns: [new JoinColumn(name: 'admingroup_id')]), new AssociationOverride(name: 'address', joinColumns: [new JoinColumn(name: 'adminaddress_id', referencedColumnName: 'id')])])]\nclass DDC964Admin extends DDC964User\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setAssociationOverride(\n            'address',\n            [\n                'joinColumns' => [\n                    [\n                        'name' => 'adminaddress_id',\n                        'referencedColumnName' => 'id',\n                    ],\n                ],\n            ],\n        );\n\n        $metadata->setAssociationOverride(\n            'groups',\n            [\n                'joinTable' => [\n                    'name'      => 'ddc964_users_admingroups',\n                    'joinColumns' => [\n                        ['name' => 'adminuser_id', 'referencedColumnName' => 'id'],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'admingroup_id', 'referencedColumnName' => 'id'],\n                    ],\n                ],\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC964/DDC964Group.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC964;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass DDC964Group\n{\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    /** @phpstan-var ArrayCollection<int, DDC964User> */\n    #[ManyToMany(targetEntity: 'DDC964User', mappedBy: 'groups')]\n    private $users;\n\n    public function __construct(\n        #[Column]\n        private string|null $name = null,\n    ) {\n        $this->users = new ArrayCollection();\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function addUser(DDC964User $user): void\n    {\n        $this->users[] = $user;\n    }\n\n    /** @phpstan-return ArrayCollection<int, DDC964User> */\n    public function getUsers(): ArrayCollection\n    {\n        return $this->users;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC964/DDC964Guest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC964;\n\nuse Doctrine\\ORM\\Mapping\\AttributeOverride;\nuse Doctrine\\ORM\\Mapping\\AttributeOverrides;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\n#[AttributeOverrides([new AttributeOverride(name: 'id', column: new Column(name: 'guest_id', type: 'integer', length: 140)), new AttributeOverride(name: 'name', column: new Column(name: 'guest_name', nullable: false, unique: true, length: 240))])]\nclass DDC964Guest extends DDC964User\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setAttributeOverride('id', [\n            'columnName'    => 'guest_id',\n            'type'          => 'integer',\n            'length'        => 140,\n        ]);\n\n        $metadata->setAttributeOverride(\n            'name',\n            [\n                'columnName'    => 'guest_name',\n                'nullable'      => false,\n                'unique'        => true,\n                'length'        => 240,\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DDC964/DDC964User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DDC964;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nclass DDC964User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: 'user_id', length: 150)]\n    protected $id;\n\n    /** @phpstan-var Collection<int, DDC964Group> */\n    #[ManyToMany(targetEntity: DDC964Group::class, inversedBy: 'users', cascade: ['persist', 'detach'])]\n    #[JoinTable(name: 'ddc964_users_groups')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n    protected $groups;\n\n    /** @var DDC964Address */\n    #[ManyToOne(targetEntity: DDC964Address::class, cascade: ['persist'])]\n    #[JoinColumn(name: 'address_id', referencedColumnName: 'id')]\n    protected $address;\n\n    public function __construct(\n        #[Column(name: 'user_name', nullable: true, unique: false, length: 250)]\n        protected string|null $name = null,\n    ) {\n        $this->groups = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function addGroup(DDC964Group $group): void\n    {\n        $this->groups->add($group);\n        $group->addUser($this);\n    }\n\n    /** @phpstan-return Collection<int, DDC964Group> */\n    public function getGroups(): ArrayCollection\n    {\n        return $this->groups;\n    }\n\n    public function getAddress(): DDC964Address\n    {\n        return $this->address;\n    }\n\n    public function setAddress(DDC964Address $address): void\n    {\n        $this->address = $address;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->isMappedSuperclass = true;\n\n        $metadata->mapField(\n            [\n                'id'         => true,\n                'fieldName'  => 'id',\n                'type'       => 'integer',\n                'columnName' => 'user_id',\n                'length'     => 150,\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type'      => 'string',\n                'columnName' => 'user_name',\n                'nullable'  => true,\n                'unique'    => false,\n                'length'    => 250,\n            ],\n        );\n\n        $metadata->mapManyToOne(\n            [\n                'fieldName'      => 'address',\n                'targetEntity'   => 'DDC964Address',\n                'cascade'        => ['persist'],\n                'joinColumns'    => [['name' => 'address_id', 'referencedColumnName' => 'id']],\n            ],\n        );\n\n        $metadata->mapManyToMany(\n            [\n                'fieldName'      => 'groups',\n                'targetEntity'   => 'DDC964Group',\n                'inversedBy'     => 'users',\n                'cascade'        => ['persist','detach'],\n                'joinTable'      => [\n                    'name'          => 'ddc964_users_groups',\n                    'joinColumns'   => [\n                        [\n                            'name' => 'user_id',\n                            'referencedColumnName' => 'id',\n                        ],\n                    ],\n                    'inverseJoinColumns' => [\n                        [\n                            'name' => 'group_id',\n                            'referencedColumnName' => 'id',\n                        ],\n                    ],\n                ],\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DataTransferObjects/DtoWithArrayOfEnums.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DataTransferObjects;\n\nuse Doctrine\\Tests\\Models\\Enums\\Unit;\n\nfinal class DtoWithArrayOfEnums\n{\n    /** @var Unit[] */\n    public $supportedUnits;\n\n    /** @param Unit[] $supportedUnits */\n    public function __construct(array $supportedUnits)\n    {\n        $this->supportedUnits = $supportedUnits;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DataTransferObjects/DtoWithEnum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DataTransferObjects;\n\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\n\nfinal class DtoWithEnum\n{\n    /** @var Suit|null */\n    public $suit;\n\n    public function __construct(Suit|null $suit)\n    {\n        $this->suit = $suit;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DirectoryTree/AbstractContentItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DirectoryTree;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nabstract class AbstractContentItem\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n\n    /**\n     * This field is transient and private on purpose\n     */\n    private bool $nodeIsLoaded = false;\n\n    /**\n     * This field is transient on purpose\n     *\n     * @var mixed\n     */\n    public static $fileSystem;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'Directory')]\n        protected Directory|null $parentDirectory = null,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function getParent(): Directory\n    {\n        return $this->parentDirectory;\n    }\n\n    public function getNodeIsLoaded(): bool\n    {\n        return $this->nodeIsLoaded;\n    }\n\n    public function setNodeIsLoaded(bool $nodeIsLoaded): void\n    {\n        $this->nodeIsLoaded = (bool) $nodeIsLoaded;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DirectoryTree/Directory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DirectoryTree;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Directory extends AbstractContentItem\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $path;\n\n    public function setPath(string $path): void\n    {\n        $this->path = $path;\n    }\n\n    public function getPath(): string\n    {\n        return $this->path;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/DirectoryTree/File.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\DirectoryTree;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`file`')]\n#[Entity]\nclass File extends AbstractContentItem\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $extension = 'html';\n\n    public function __construct(Directory|null $parent = null)\n    {\n        parent::__construct($parent);\n    }\n\n    public function getExtension(): string\n    {\n        return $this->extension;\n    }\n\n    public function setExtension(string $ext): void\n    {\n        $this->extension = $ext;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceCart.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceCart\n * Represents a typical cart of a shopping application.\n */\n#[Table(name: 'ecommerce_carts')]\n#[Entity]\nclass ECommerceCart\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column(length: 50, nullable: true)]\n    private string|null $payment = null;\n\n    #[OneToOne(targetEntity: 'ECommerceCustomer', inversedBy: 'cart')]\n    #[JoinColumn(name: 'customer_id', referencedColumnName: 'id')]\n    private ECommerceCustomer|null $customer = null;\n\n    /** @phpstan-var Collection<int, ECommerceProduct> */\n    #[JoinTable(name: 'ecommerce_carts_products')]\n    #[JoinColumn(name: 'cart_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'ECommerceProduct', cascade: ['persist'])]\n    private $products;\n\n    public function __construct()\n    {\n        $this->products = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getPayment(): string\n    {\n        return $this->payment;\n    }\n\n    public function setPayment(string $payment): void\n    {\n        $this->payment = $payment;\n    }\n\n    public function setCustomer(ECommerceCustomer $customer): void\n    {\n        if ($this->customer !== $customer) {\n            $this->customer = $customer;\n            $customer->setCart($this);\n        }\n    }\n\n    public function removeCustomer(): void\n    {\n        if ($this->customer !== null) {\n            $customer       = $this->customer;\n            $this->customer = null;\n            $customer->removeCart();\n        }\n    }\n\n    public function getCustomer(): ECommerceCustomer|null\n    {\n        return $this->customer;\n    }\n\n    /** @phpstan-return Collection<int, ECommerceProduct> */\n    public function getProducts(): Collection\n    {\n        return $this->products;\n    }\n\n    public function addProduct(ECommerceProduct $product): void\n    {\n        $this->products[] = $product;\n    }\n\n    public function removeProduct(ECommerceProduct $product): bool\n    {\n        return $this->products->removeElement($product);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceCategory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceCategory\n * Represents a tag applied on particular products.\n */\n#[Table(name: 'ecommerce_categories')]\n#[Entity]\nclass ECommerceCategory\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 50)]\n    private string|null $name = null;\n\n    /** @phpstan-var Collection<int, ECommerceProduct> */\n    #[ManyToMany(targetEntity: 'ECommerceProduct', mappedBy: 'categories')]\n    private $products;\n\n    /** @phpstan-var Collection<int, ECommerceCategory> */\n    #[OneToMany(targetEntity: 'ECommerceCategory', mappedBy: 'parent', cascade: ['persist'])]\n    private $children;\n\n    #[ManyToOne(targetEntity: 'ECommerceCategory', inversedBy: 'children')]\n    #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')]\n    private ECommerceCategory|null $parent = null;\n\n    public function __construct()\n    {\n        $this->products = new ArrayCollection();\n        $this->children = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function addProduct(ECommerceProduct $product): void\n    {\n        if (! $this->products->contains($product)) {\n            $this->products[] = $product;\n            $product->addCategory($this);\n        }\n    }\n\n    public function removeProduct(ECommerceProduct $product): void\n    {\n        $removed = $this->products->removeElement($product);\n        if ($removed) {\n            $product->removeCategory($this);\n        }\n    }\n\n    /** @phpstan-return Collection<int, ECommerceProduct> */\n    public function getProducts(): Collection\n    {\n        return $this->products;\n    }\n\n    private function setParent(ECommerceCategory $parent): void\n    {\n        $this->parent = $parent;\n    }\n\n    /** @phpstan-return Collection<int, ECommerceCategory> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n\n    public function getParent(): ECommerceCategory|null\n    {\n        return $this->parent;\n    }\n\n    public function addChild(ECommerceCategory $child): void\n    {\n        $this->children[] = $child;\n        $child->setParent($this);\n    }\n\n    /** does not set the owning side. */\n    public function brokenAddChild(ECommerceCategory $child): void\n    {\n        $this->children[] = $child;\n    }\n\n    public function removeChild(ECommerceCategory $child): void\n    {\n        $removed = $this->children->removeElement($child);\n        if ($removed) {\n            $child->removeParent();\n        }\n    }\n\n    private function removeParent(): void\n    {\n        $this->parent = null;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceCustomer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceCustomer\n * Represents a registered user of a shopping application.\n */\n#[Table(name: 'ecommerce_customers')]\n#[Entity]\nclass ECommerceCustomer\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 50)]\n    private string|null $name = null;\n\n    #[OneToOne(targetEntity: 'ECommerceCart', mappedBy: 'customer', cascade: ['persist'])]\n    private ECommerceCart|null $cart = null;\n\n    /**\n     * Example of a one-one self referential association. A mentor can follow\n     * only one customer at the time, while a customer can choose only one\n     * mentor. Not properly appropriate but it works.\n     */\n    #[OneToOne(targetEntity: 'ECommerceCustomer', cascade: ['persist'], fetch: 'EAGER')]\n    #[JoinColumn(name: 'mentor_id', referencedColumnName: 'id')]\n    private ECommerceCustomer|null $mentor = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function setCart(ECommerceCart $cart): void\n    {\n        if ($this->cart !== $cart) {\n            $this->cart = $cart;\n            $cart->setCustomer($this);\n        }\n    }\n\n    /** Does not properly maintain the bidirectional association! */\n    public function brokenSetCart(ECommerceCart $cart): void\n    {\n        $this->cart = $cart;\n    }\n\n    public function getCart(): ECommerceCart|null\n    {\n        return $this->cart;\n    }\n\n    public function removeCart(): void\n    {\n        if ($this->cart !== null) {\n            $cart       = $this->cart;\n            $this->cart = null;\n            $cart->removeCustomer();\n        }\n    }\n\n    public function setMentor(ECommerceCustomer $mentor): void\n    {\n        $this->mentor = $mentor;\n    }\n\n    public function removeMentor(): void\n    {\n        $this->mentor = null;\n    }\n\n    public function getMentor(): ECommerceCustomer|null\n    {\n        return $this->mentor;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceFeature.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Describes a product feature.\n */\n#[Table(name: 'ecommerce_features')]\n#[Entity]\nclass ECommerceFeature\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column(length: 50)]\n    private string|null $description = null;\n\n    #[ManyToOne(targetEntity: 'ECommerceProduct', inversedBy: 'features')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    private ECommerceProduct|null $product = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getDescription(): string|null\n    {\n        return $this->description;\n    }\n\n    public function setDescription(string $description): void\n    {\n        $this->description = $description;\n    }\n\n    public function setProduct(ECommerceProduct $product): void\n    {\n        $this->product = $product;\n    }\n\n    public function removeProduct(): void\n    {\n        if ($this->product !== null) {\n            $product       = $this->product;\n            $this->product = null;\n            $product->removeFeature($this);\n        }\n    }\n\n    public function getProduct(): ECommerceProduct|null\n    {\n        return $this->product;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceProduct.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Index;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceProduct\n * Represents a type of product of a shopping application.\n */\n#[Table(name: 'ecommerce_products')]\n#[Index(name: 'name_idx', columns: ['name'])]\n#[Entity]\nclass ECommerceProduct\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column(type: 'string', length: 50, nullable: true)]\n    private string|null $name = null;\n\n    #[OneToOne(targetEntity: 'ECommerceShipping', cascade: ['persist'])]\n    #[JoinColumn(name: 'shipping_id', referencedColumnName: 'id')]\n    private ECommerceShipping|null $shipping = null;\n\n    /** @phpstan-var Collection<int, ECommerceFeature> */\n    #[OneToMany(targetEntity: 'ECommerceFeature', mappedBy: 'product', cascade: ['persist'])]\n    private $features;\n\n    /** @phpstan-var Collection<int, ECommerceCategory> */\n    #[JoinTable(name: 'ecommerce_products_categories')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'category_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'ECommerceCategory', cascade: ['persist'], inversedBy: 'products')]\n    private $categories;\n\n    /**\n     * This relation is saved with two records in the association table for\n     * simplicity.\n     *\n     * @phpstan-var Collection<int, ECommerceProduct>\n     */\n    #[JoinTable(name: 'ecommerce_products_related')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'related_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'ECommerceProduct', cascade: ['persist'])]\n    private $related;\n\n    /** @var bool */\n    public $isCloned = false;\n\n    /** @var bool */\n    public $wakeUp = false;\n\n    public function __construct()\n    {\n        $this->features   = new ArrayCollection();\n        $this->categories = new ArrayCollection();\n        $this->related    = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getShipping(): ECommerceShipping|null\n    {\n        return $this->shipping;\n    }\n\n    public function setShipping(ECommerceShipping $shipping): void\n    {\n        $this->shipping = $shipping;\n    }\n\n    public function removeShipping(): void\n    {\n        $this->shipping = null;\n    }\n\n    /** @phpstan-return Collection<int, ECommerceFeature> */\n    public function getFeatures(): Collection\n    {\n        return $this->features;\n    }\n\n    public function addFeature(ECommerceFeature $feature): void\n    {\n        $this->features[] = $feature;\n        $feature->setProduct($this);\n    }\n\n    /** does not set the owning side */\n    public function brokenAddFeature(ECommerceFeature $feature): void\n    {\n        $this->features[] = $feature;\n    }\n\n    public function removeFeature(ECommerceFeature $feature): bool\n    {\n        $removed = $this->features->removeElement($feature);\n        if ($removed) {\n            $feature->removeProduct();\n        }\n\n        return $removed;\n    }\n\n    public function addCategory(ECommerceCategory $category): void\n    {\n        if (! $this->categories->contains($category)) {\n            $this->categories[] = $category;\n            $category->addProduct($this);\n        }\n    }\n\n    public function removeCategory(ECommerceCategory $category): void\n    {\n        $removed = $this->categories->removeElement($category);\n        if ($removed) {\n            $category->removeProduct($this);\n        }\n    }\n\n    /** @phpstan-param Collection<int, ECommerceCategory> $categories */\n    public function setCategories(Collection $categories): void\n    {\n        $this->categories = $categories;\n    }\n\n    /** @phpstan-return Collection<int, ECommerceCategory> $categories */\n    public function getCategories(): Collection\n    {\n        return $this->categories;\n    }\n\n    /** @phpstan-return Collection<int, ECommerceProduct> $categories */\n    public function getRelated(): Collection\n    {\n        return $this->related;\n    }\n\n    public function addRelated(ECommerceProduct $related): void\n    {\n        if (! $this->related->contains($related)) {\n            $this->related[] = $related;\n            $related->addRelated($this);\n        }\n    }\n\n    public function removeRelated(ECommerceProduct $related): void\n    {\n        $removed = $this->related->removeElement($related);\n        if ($removed) {\n            $related->removeRelated($this);\n        }\n    }\n\n    public function __clone()\n    {\n        $this->isCloned = true;\n        if ($this->categories) {\n            $this->categories = clone $this->categories;\n        }\n    }\n\n    /**\n     * Testing docblock contents here\n     */\n    public function __wakeup(): void\n    {\n        $this->wakeUp = true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceProduct2.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Index;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceProduct2\n * Resets the id when being cloned.\n */\n#[Entity]\n#[Table(name: 'ecommerce_products')]\n#[Index(name: 'name_idx', columns: ['name'])]\nclass ECommerceProduct2\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue]\n    private int|null $id = null;\n\n    #[Column(length: 50, nullable: true)]\n    private string|null $name = null;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function __clone()\n    {\n        $this->id   = null;\n        $this->name = 'Clone of ' . $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ECommerce/ECommerceShipping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ECommerce;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * ECommerceShipping\n * Represents a shipping method.\n */\n#[Table(name: 'ecommerce_shippings')]\n#[Entity]\nclass ECommerceShipping\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column(type: 'integer')]\n    private int|string|null $days = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getDays(): int|string\n    {\n        return $this->days;\n    }\n\n    public function setDays(int|string $days): void\n    {\n        $this->days = $days;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'eager_composite_join_root')]\nclass RootEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer', nullable: false)]\n    private int|null $id = null;\n\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'string', nullable: false, name: 'other_key', length: 42)]\n    private string $otherKey;\n\n    /** @var Collection<int, SecondLevel> */\n    #[ORM\\OneToMany(mappedBy: 'root', targetEntity: SecondLevel::class, fetch: 'EAGER')]\n    private Collection $secondLevel;\n\n    /** @var Collection<int, SecondLevelWithoutCompositePrimaryKey> */\n    #[ORM\\OneToMany(mappedBy: 'root', targetEntity: SecondLevelWithoutCompositePrimaryKey::class, fetch: 'EAGER')]\n    private Collection $anotherSecondLevel;\n\n    public function __construct(int $id, string $other)\n    {\n        $this->otherKey           = $other;\n        $this->secondLevel        = new ArrayCollection();\n        $this->anotherSecondLevel = new ArrayCollection();\n        $this->id                 = $id;\n    }\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getOtherKey(): string\n    {\n        return $this->otherKey;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'eager_composite_join_second_level')]\n#[ORM\\Index(name: 'root_other_key_idx', columns: ['root_other_key', 'root_id'])]\nclass SecondLevel\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer', nullable: false)]\n    private int|null $upperId;\n\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'string', nullable: false, name: 'other_key')]\n    private string $otherKey;\n\n    #[ORM\\ManyToOne(targetEntity: RootEntity::class, inversedBy: 'secondLevel')]\n    #[ORM\\JoinColumn(name: 'root_id', referencedColumnName: 'id')]\n    #[ORM\\JoinColumn(name: 'root_other_key', referencedColumnName: 'other_key')]\n    private RootEntity $root;\n\n    public function __construct(RootEntity $upper)\n    {\n        $this->upperId  = $upper->getId();\n        $this->otherKey = $upper->getOtherKey();\n        $this->root     = $upper;\n    }\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevelWithoutCompositePrimaryKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass SecondLevelWithoutCompositePrimaryKey\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer', nullable: false)]\n    private int|null $id;\n\n    #[ORM\\ManyToOne(targetEntity: RootEntity::class, inversedBy: 'anotherSecondLevel')]\n    #[ORM\\JoinColumn(name: 'root_id', referencedColumnName: 'id')]\n    #[ORM\\JoinColumn(name: 'root_other_key', referencedColumnName: 'other_key')]\n    private RootEntity $root;\n\n    public function __construct(RootEntity $upper)\n    {\n        $this->root = $upper;\n    }\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/AccessLevel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum AccessLevel: int\n{\n    case Admin  = 1;\n    case User   = 2;\n    case Guests = 3;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/BookCategory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass BookCategory\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    public int $id;\n\n    #[ManyToMany(targetEntity: BookWithGenre::class, mappedBy: 'categories')]\n    public Collection $books;\n\n    public function __construct()\n    {\n        $this->books = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/BookGenre.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum BookGenre: string\n{\n    case FICTION     = 'fiction';\n    case NON_FICTION = 'non fiction';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/BookWithGenre.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass BookWithGenre\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    public int $id;\n\n    #[ManyToOne(targetEntity: Library::class, inversedBy: 'books')]\n    public Library $library;\n\n    #[Column(enumType: BookGenre::class)]\n    public BookGenre $genre;\n\n    #[ManyToMany(targetEntity: BookCategory::class, inversedBy: 'books')]\n    public Collection $categories;\n\n    public function __construct(BookGenre $genre)\n    {\n        $this->genre      = $genre;\n        $this->categories = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Card.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass Card\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Suit */\n    #[Column(type: 'string', enumType: Suit::class)]\n    public $suit;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'suit',\n                'type' => 'string',\n                'enumType' => Suit::class,\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/CardNativeEnum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass CardNativeEnum\n{\n    /** @var int|null */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: Types::INTEGER)]\n    public $id;\n\n    /** @var Suit */\n    #[Column(type: Types::ENUM, enumType: Suit::class, options: ['values' => ['H', 'D', 'C', 'S', 'Z']])]\n    public $suit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/CardWithDefault.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass CardWithDefault\n{\n    #[Id]\n    #[Column]\n    public string $id;\n\n    #[Column(options: ['default' => Suit::Hearts])]\n    public Suit $suit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/CardWithNullable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass CardWithNullable\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var ?Suit */\n    #[Column(type: 'string', nullable: true, enumType: Suit::class)]\n    public $suit;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'suit',\n                'type' => 'string',\n                'enumType' => Suit::class,\n                'nullable' => true,\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/City.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum City: string\n{\n    case Paris    = 'Paris';\n    case Cannes   = 'Cannes';\n    case StJulien = 'St Julien';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/FaultySwitch.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\n\nclass FaultySwitch\n{\n    #[Column(type: 'string')]\n    public string $value;\n\n    #[Column(enumType: SwitchStatus::class)]\n    public SwitchStatus $status;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Library.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table('`library`')]\nclass Library\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    public int $id;\n\n    #[OneToMany(targetEntity: BookWithGenre::class, mappedBy: 'library')]\n    public Collection $books;\n\n    public function __construct()\n    {\n        $this->books = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Product.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass Product\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public int $id;\n\n    #[Embedded(class: Quantity::class)]\n    public Quantity $quantity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Quantity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\n\n#[Embeddable]\nclass Quantity\n{\n    #[Column(type: 'integer')]\n    public int $value;\n\n    #[Column(type: 'string', enumType: Unit::class)]\n    public Unit $unit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Scale.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass Scale\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Unit[] */\n    #[Column(type: 'simple_array', enumType: Unit::class)]\n    public $supportedUnits;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'supportedUnits',\n                'type' => 'simple_array',\n                'enumType' => Unit::class,\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Suit.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum Suit: string\n{\n    case Hearts   = 'H';\n    case Diamonds = 'D';\n    case Clubs    = 'C';\n    case Spades   = 'S';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/SwitchStatus.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum SwitchStatus\n{\n    case ON;\n    case OFF;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/TypedCard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass TypedCard\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    public int $id;\n\n    #[Column]\n    public Suit $suit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/TypedCardEnumCompositeId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass TypedCardEnumCompositeId\n{\n    #[Id]\n    #[Column(type: 'string', enumType: Suit::class)]\n    public Suit $suit;\n\n    #[Id]\n    #[Column(type: 'string', enumType: Unit::class)]\n    public Unit $unit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/TypedCardEnumId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass TypedCardEnumId\n{\n    #[Id]\n    #[Column(type: 'string', enumType: Suit::class)]\n    public Suit $suit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/TypedCardNativeEnum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass TypedCardNativeEnum\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    public int $id;\n\n    #[Column(type: Types::ENUM)]\n    public Suit $suit;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/Unit.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum Unit: string\n{\n    case Gram  = 'g';\n    case Meter = 'm';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Enums/UserStatus.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Enums;\n\nenum UserStatus: string\n{\n    case Active   = 'active';\n    case Inactive = 'inactive';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Forum/ForumAvatar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Forum;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'forum_avatars')]\n#[Entity]\nclass ForumAvatar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Forum/ForumBoard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Forum;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Represents a board in a forum.\n */\n#[Table(name: 'forum_boards')]\n#[Entity]\nclass ForumBoard\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $position;\n    /** @var ForumCategory */\n    #[ManyToOne(targetEntity: 'ForumCategory', inversedBy: 'boards')]\n    #[JoinColumn(name: 'category_id', referencedColumnName: 'id')]\n    public $category;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Forum/ForumCategory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Forum;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'forum_categories')]\n#[Entity]\nclass ForumCategory\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    private int $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $position;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, ForumBoard> */\n    #[OneToMany(targetEntity: 'ForumBoard', mappedBy: 'category')]\n    public $boards;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Forum/ForumEntry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Forum;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'forum_entries')]\n#[Entity]\nclass ForumEntry\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 50)]\n    public $topic;\n\n    public function &getTopicByReference(): string\n    {\n        return $this->topic;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Forum/ForumUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Forum;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'forum_users')]\n#[Entity]\nclass ForumUser\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 50)]\n    public $username;\n\n    /** @var ForumAvatar */\n    #[OneToOne(targetEntity: 'ForumAvatar', cascade: ['persist'])]\n    #[JoinColumn(name: 'avatar_id', referencedColumnName: 'id')]\n    public $avatar;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getUsername(): string\n    {\n        return $this->username;\n    }\n\n    public function getAvatar(): ForumAvatar\n    {\n        return $this->avatar;\n    }\n\n    public function setAvatar(ForumAvatar $avatar): void\n    {\n        $this->avatar = $avatar;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10132/Complex.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10132;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\n\n#[Entity]\nclass Complex\n{\n    #[Id]\n    #[Column(type: 'string', enumType: Suit::class)]\n    protected Suit $type;\n\n    #[OneToMany(targetEntity: ComplexChild::class, mappedBy: 'complex', cascade: ['persist'])]\n    protected Collection $complexChildren;\n\n    public function __construct()\n    {\n        $this->complexChildren = new ArrayCollection();\n    }\n\n    public function getType(): Suit\n    {\n        return $this->type;\n    }\n\n    public function setType(Suit $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getComplexChildren(): Collection\n    {\n        return $this->complexChildren;\n    }\n\n    public function addComplexChild(ComplexChild $complexChild): void\n    {\n        $this->complexChildren->add($complexChild);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10132/ComplexChild.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10132;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\n\n#[Entity]\nclass ComplexChild\n{\n    #[ManyToOne(inversedBy: 'complexChildren')]\n    #[JoinColumn(name: 'complexType', referencedColumnName: 'type', nullable: false)]\n    protected Complex $complex;\n\n    #[Id]\n    #[Column(type: 'string', enumType: Suit::class)]\n    protected Suit $complexType;\n\n    public function setComplex(Complex $complex): void\n    {\n        $complex->addComplexChild($this);\n        $this->complexType = $complex->getType();\n        $this->complex     = $complex;\n    }\n\n    public function getComplexType(): Suit\n    {\n        return $this->complexType;\n    }\n\n    public function getComplex(): Complex\n    {\n        return $this->complex;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10288/GH10288People.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10288;\n\nenum GH10288People: string\n{\n    case BOSS     = 'boss';\n    case EMPLOYEE = 'employee';\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10334/GH10334Foo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10334;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass GH10334Foo\n{\n    #[Id]\n    #[ManyToOne(targetEntity: GH10334FooCollection::class, inversedBy: 'foos')]\n    #[JoinColumn(name: 'foo_collection_id', referencedColumnName: 'id')]\n    #[GeneratedValue]\n    protected GH10334FooCollection $collection;\n\n    #[Id]\n    #[Column(type: 'string', enumType: 'Doctrine\\Tests\\Models\\GH10334\\GH10334ProductTypeId')]\n    protected GH10334ProductTypeId $productTypeId;\n\n    public function __construct(GH10334FooCollection $collection, GH10334ProductTypeId $productTypeId)\n    {\n        $this->collection    = $collection;\n        $this->productTypeId = $productTypeId;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10334/GH10334FooCollection.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10334;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\n\n#[Entity]\nclass GH10334FooCollection\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected int $id;\n\n    /** @var Collection<GH10334Foo> $foos */\n    #[OneToMany(targetEntity: 'GH10334Foo', mappedBy: 'collection', cascade: ['persist', 'remove'])]\n    private Collection $foos;\n\n    public function __construct()\n    {\n        $this->foos = new ArrayCollection();\n    }\n\n    /** @return Collection<GH10334Foo> */\n    public function getFoos(): Collection\n    {\n        return $this->foos;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10334/GH10334Product.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10334;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass GH10334Product\n{\n    #[Id]\n    #[Column(name: 'product_id', type: 'integer')]\n    #[GeneratedValue]\n    protected int $id;\n\n    #[Column(name: 'name', type: 'string')]\n    private string $name;\n\n    #[ManyToOne(targetEntity: 'GH10334ProductType', inversedBy: 'products')]\n    #[JoinColumn(name: 'product_type_id', referencedColumnName: 'id', nullable: false)]\n    private GH10334ProductType $productType;\n\n    public function __construct(string $name, GH10334ProductType $productType)\n    {\n        $this->name        = $name;\n        $this->productType = $productType;\n    }\n\n    public function getProductType(): GH10334ProductType\n    {\n        return $this->productType;\n    }\n\n    public function setProductType(GH10334ProductType $productType): void\n    {\n        $this->productType = $productType;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10334/GH10334ProductType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10334;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\n\n#[Entity]\nclass GH10334ProductType\n{\n    #[Id]\n    #[Column(type: 'string', enumType: 'Doctrine\\Tests\\Models\\GH10334\\GH10334ProductTypeId', length: 255)]\n    protected GH10334ProductTypeId $id;\n\n    #[Column(type: 'float')]\n    private float $value;\n\n    #[OneToMany(targetEntity: 'GH10334Product', mappedBy: 'productType', cascade: ['persist', 'remove'])]\n    private Collection $products;\n\n    public function __construct(GH10334ProductTypeId $id, float $value)\n    {\n        $this->id       = $id;\n        $this->value    = $value;\n        $this->products = new ArrayCollection();\n    }\n\n    public function getId(): GH10334ProductTypeId\n    {\n        return $this->id;\n    }\n\n    public function addProduct(GH10334Product $product): void\n    {\n        $product->setProductType($this);\n        $this->products->add($product);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10334/GH10334ProductTypeId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10334;\n\nenum GH10334ProductTypeId: string\n{\n    case Jean  = 'jean';\n    case Short = 'short';\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10336/GH10336Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10336;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10336_entities')]\nclass GH10336Entity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\ManyToOne(targetEntity: GH10336Relation::class)]\n    #[ORM\\JoinColumn(name: 'relation_id', referencedColumnName: 'id', nullable: true)]\n    public GH10336Relation|null $relation = null;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH10336/GH10336Relation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH10336;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10336_relations')]\nclass GH10336Relation\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\Column(type: 'string')]\n    public string $value;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH11524/GH11524Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH11524;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh11524_entities')]\nclass GH11524Entity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\ManyToOne(targetEntity: GH11524Relation::class)]\n    #[ORM\\JoinColumn(name: 'relation_id', referencedColumnName: 'id', nullable: true)]\n    public GH11524Relation|null $relation = null;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH11524/GH11524Listener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH11524;\n\nuse Doctrine\\ORM\\Event\\PostLoadEventArgs;\n\nclass GH11524Listener\n{\n    public function postLoad(PostloadEventArgs $eventArgs): void\n    {\n        $object = $eventArgs->getObject();\n\n        if (! $object instanceof GH11524Relation) {\n            return;\n        }\n\n        $object->setCurrentLocale('en');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH11524/GH11524Relation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH11524;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse LogicException;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh11524_relations')]\nclass GH11524Relation\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    private string|null $currentLocale;\n\n    public function setCurrentLocale(string $locale): void\n    {\n        $this->currentLocale = $locale;\n    }\n\n    public function getTranslation(): string\n    {\n        if ($this->currentLocale === null) {\n            throw new LogicException('The current locale must be set to retrieve translation.');\n        }\n\n        return 'fake';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH7141/GH7141Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH7141;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\n\nclass GH7141Article\n{\n    /** @phpstan-var Collection<int, mixed> */\n    private $tags;\n\n    public function __construct()\n    {\n        $this->tags = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH7316/GH7316Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH7316;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\n\nclass GH7316Article\n{\n    /** @phpstan-var Collection<int, mixed> */\n    private $tags;\n\n    public function __construct()\n    {\n        $this->tags = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH7717/GH7717Child.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH7717;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh7717_children')]\nclass GH7717Child\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\Column(type: 'string', nullable: true)]\n    public string|null $nullableProperty = null;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH7717/GH7717Parent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH7717;\n\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh7717_parents')]\nclass GH7717Parent\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    /** @var Selectable<int, GH7717Child> */\n    #[ORM\\ManyToMany(targetEntity: GH7717Child::class, cascade: ['persist'])]\n    public Selectable $children;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH8565/GH8565Employee.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH8565;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\GH8565EmployeePayloadType;\n\n#[Table(name: 'gh8565_employees')]\n#[Entity]\nclass GH8565Employee extends GH8565Person\n{\n    /** @var GH8565EmployeePayloadType */\n    #[Column(type: 'GH8565EmployeePayloadType', nullable: false)]\n    public $type;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH8565/GH8565Manager.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH8565;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\GH8565ManagerPayloadType;\n\n#[Table(name: 'gh8565_managers')]\n#[Entity]\nclass GH8565Manager extends GH8565Person\n{\n    /** @var GH8565ManagerPayloadType */\n    #[Column(type: 'GH8565ManagerPayloadType', nullable: false)]\n    public $type;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GH8565/GH8565Person.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GH8565;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'gh8565_persons')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['person' => 'GH8565Person', 'manager' => 'GH8565Manager', 'employee' => 'GH8565Employee'])]\nclass GH8565Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Generic/BooleanModel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Generic;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'boolean_model')]\n#[Entity]\nclass BooleanModel\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var bool */\n    #[Column(type: 'boolean')]\n    public $booleanField;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Generic/DateTimeModel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Generic;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'date_time_model')]\n#[Entity]\nclass DateTimeModel\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DateTime|null */\n    #[Column(name: 'col_datetime', type: 'datetime', nullable: true)]\n    public $datetime;\n\n    /** @var DateTime|null */\n    #[Column(name: 'col_date', type: 'date', nullable: true)]\n    public $date;\n\n    /** @var DateTime|null */\n    #[Column(name: 'col_time', type: 'time', nullable: true)]\n    public $time;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Generic/DecimalModel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Generic;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'decimal_model')]\n#[Entity]\nclass DecimalModel\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var float */\n    #[Column(name: '`decimal`', type: 'decimal', scale: 2, precision: 5)]\n    public $decimal;\n\n    /** @var float */\n    #[Column(name: '`high_scale`', type: 'decimal', scale: 4, precision: 14)]\n    public $highScale;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Generic/NonAlphaColumnsEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Generic;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`not-a-simple-entity`')]\n#[Entity]\nclass NonAlphaColumnsEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer', name: '`simple-entity-id`')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255, name: '`simple-entity-value`')]\n        public string $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Generic/SerializationModel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Generic;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'serialize_model')]\n#[Entity]\nclass SerializationModel\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var mixed[] */\n    #[Column(name: 'the_array', type: 'array', nullable: true)]\n    public $array;\n\n    /** @var object */\n    #[Column(name: 'the_obj', type: 'object', nullable: true)]\n    public $object;\n}\n"
  },
  {
    "path": "tests/Tests/Models/GeoNames/Admin1.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GeoNames;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'geonames_admin1')]\n#[Entity]\n#[Cache]\nclass Admin1\n{\n    /** @phpstan-var Collection<int, Admin1AlternateName> */\n    #[OneToMany(targetEntity: 'Admin1AlternateName', mappedBy: 'admin1')]\n    #[Cache]\n    public $names = [];\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer', length: 25)]\n        #[GeneratedValue(strategy: 'NONE')]\n        public int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n        #[Id]\n        #[ManyToOne(targetEntity: 'Country')]\n        #[JoinColumn(name: 'country', referencedColumnName: 'id')]\n        #[Cache]\n        public Country $country,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GeoNames/Admin1AlternateName.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GeoNames;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'geonames_admin1_alternate_name')]\n#[Entity]\n#[Cache]\nclass Admin1AlternateName\n{\n    #[Id]\n    #[Column(type: 'string', length: 25)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    public function __construct(\n        int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n        #[JoinColumn(name: 'admin1', referencedColumnName: 'id')]\n        #[JoinColumn(name: 'country', referencedColumnName: 'country')]\n        #[ManyToOne(targetEntity: 'Admin1', inversedBy: 'names')]\n        #[Cache]\n        public Admin1 $admin1,\n    ) {\n        $this->id = (string) $id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GeoNames/City.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GeoNames;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'geonames_city')]\n#[Entity]\n#[Cache]\nclass City\n{\n    #[Id]\n    #[Column(type: 'string', length: 25)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    /** @var Country */\n    #[ManyToOne(targetEntity: 'Country')]\n    #[JoinColumn(name: 'country', referencedColumnName: 'id')]\n    #[Cache]\n    public $country;\n\n    /** @var Admin1 */\n    #[JoinColumn(name: 'admin1', referencedColumnName: 'id')]\n    #[JoinColumn(name: 'country', referencedColumnName: 'country')]\n    #[ManyToOne(targetEntity: 'Admin1')]\n    #[Cache]\n    public $admin1;\n\n    public function __construct(\n        int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n        $this->id = (string) $id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/GeoNames/Country.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\GeoNames;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'geonames_country')]\n#[Entity]\n#[Cache]\nclass Country\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 2)]\n        #[GeneratedValue(strategy: 'NONE')]\n        public string $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Global/GlobalNamespaceModel.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'articles')]\n#[Entity]\nclass DoctrineGlobalArticle\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $headline;\n\n    /** @var string */\n    #[Column(type: 'text')]\n    protected $text;\n\n    /** @phpstan-var Collection<int, DoctrineGlobalUser> */\n    #[JoinTable(name: 'author_articles')]\n    #[JoinColumn(name: 'article_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'author_id', referencedColumnName: 'id', unique: true)]\n    #[ManyToMany(targetEntity: 'DoctrineGlobalUser')]\n    protected $author;\n\n    /** @phpstan-var Collection<int, DoctrineGlobalUser> */\n    #[JoinTable(name: 'editor_articles')]\n    #[JoinColumn(name: 'article_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'editor_id', referencedColumnName: 'id', unique: true)]\n    #[ManyToMany(targetEntity: 'DoctrineGlobalUser')]\n    protected $editor;\n}\n\n#[Table(name: 'users')]\n#[Entity]\nclass DoctrineGlobalUser\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'string', length: 64)]\n    private string $username;\n\n    #[Column(type: 'string', length: 128)]\n    private string $email;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Hydration/EntityWithArrayDefaultArrayValueM2M.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Hydration;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass EntityWithArrayDefaultArrayValueM2M\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @phpstan-var Collection<int, SimpleEntity> */\n    #[ManyToMany(targetEntity: SimpleEntity::class)]\n    public $collection = [];\n}\n"
  },
  {
    "path": "tests/Tests/Models/Hydration/SimpleEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Hydration;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass SimpleEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/InvalidXml.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models;\n\nclass InvalidXml\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/Issue5989/Issue5989Employee.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Issue5989;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'issue5989_employees')]\n#[Entity]\nclass Issue5989Employee extends Issue5989Person\n{\n    /** @var array */\n    #[Column(type: 'simple_array', nullable: true)]\n    public $tags;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Issue5989/Issue5989Manager.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Issue5989;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'issue5989_managers')]\n#[Entity]\nclass Issue5989Manager extends Issue5989Person\n{\n    /** @var array */\n    #[Column(type: 'simple_array', nullable: true)]\n    public $tags;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Issue5989/Issue5989Person.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Issue5989;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'issue5989_persons')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['person' => 'Issue5989Person', 'manager' => 'Issue5989Manager', 'employee' => 'Issue5989Employee'])]\nclass Issue5989Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Issue9300/Issue9300Child.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Issue9300;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass Issue9300Child\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, Issue9300Parent> */\n    #[ManyToMany(targetEntity: Issue9300Parent::class)]\n    public $parents;\n\n    /** @var string */\n    #[Column(type: 'string')]\n    public $name;\n\n    public function __construct()\n    {\n        $this->parents = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Issue9300/Issue9300Parent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Issue9300;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass Issue9300Parent\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string')]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/JoinedInheritanceType/AnotherChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\JoinedInheritanceType;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass AnotherChildClass extends ChildClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/JoinedInheritanceType/ChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\JoinedInheritanceType;\n\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\n\n#[MappedSuperclass]\nabstract class ChildClass extends RootClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/Models/JoinedInheritanceType/RootClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\JoinedInheritanceType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\n\n#[Entity]\n#[InheritanceType('JOINED')]\nclass RootClass\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Legacy/LegacyArticle.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Legacy;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'legacy_articles')]\n#[Entity]\nclass LegacyArticle\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'iArticleId', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'sTopic', type: 'string', length: 255)]\n    public $topic;\n\n    /** @var string */\n    #[Column(name: 'sText', type: 'text')]\n    public $text;\n\n    /** @var LegacyUser */\n    #[ManyToOne(targetEntity: 'LegacyUser', inversedBy: 'articles')]\n    #[JoinColumn(name: 'iUserId', referencedColumnName: 'iUserId')]\n    public $user;\n\n    public function setAuthor(LegacyUser $author): void\n    {\n        $this->user = $author;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Legacy/LegacyCar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Legacy;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'legacy_cars')]\n#[Entity]\nclass LegacyCar\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'iCarId', type: 'integer', nullable: false)]\n    public $id;\n\n    /** @phpstan-var Collection<int, LegacyUser> */\n    #[ManyToMany(targetEntity: 'LegacyUser', mappedBy: 'cars')]\n    public $users;\n\n    /** @var string */\n    #[Column(name: 'sDescription', type: 'string', length: 255, unique: true)]\n    public $description;\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n\n    public function addUser(LegacyUser $user): void\n    {\n        $this->users[] = $user;\n    }\n\n    /** @phpstan-return Collection<int, LegacyUser> */\n    public function getUsers(): Collection\n    {\n        return $this->users;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Legacy/LegacyUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Legacy;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'legacy_users')]\n#[Entity]\nclass LegacyUser\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'iUserId', type: 'integer', nullable: false)]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'sUsername', type: 'string', length: 255, unique: true)]\n    public $username;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'name')]\n    public $name;\n\n    /** @phpstan-var Collection<int, LegacyArticle> */\n    #[OneToMany(targetEntity: 'LegacyArticle', mappedBy: 'user')]\n    public $articles;\n\n    /** @phpstan-var Collection<int, LegacyUserReference> */\n    #[OneToMany(targetEntity: 'LegacyUserReference', mappedBy: '_source', cascade: ['remove'])]\n    public $references;\n\n    /** @phpstan-var Collection<int, LegacyCar> */\n    #[JoinTable(name: 'legacy_users_cars')]\n    #[JoinColumn(name: 'iUserId', referencedColumnName: 'iUserId')]\n    #[InverseJoinColumn(name: 'iCarId', referencedColumnName: 'iCarId')]\n    #[ManyToMany(targetEntity: 'LegacyCar', inversedBy: 'users', cascade: ['persist'])]\n    public $cars;\n\n    public function __construct()\n    {\n        $this->articles   = new ArrayCollection();\n        $this->references = new ArrayCollection();\n        $this->cars       = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getUsername(): string\n    {\n        return $this->username;\n    }\n\n    public function addArticle(LegacyArticle $article): void\n    {\n        $this->articles[] = $article;\n        $article->setAuthor($this);\n    }\n\n    public function addReference(LegacyUserReference $reference): void\n    {\n        $this->references[] = $reference;\n    }\n\n    /** @phpstan-return Collection<int, LegacyUserReference> */\n    public function references(): Collection\n    {\n        return $this->references;\n    }\n\n    public function addCar(LegacyCar $car): void\n    {\n        $this->cars[] = $car;\n        $car->addUser($this);\n    }\n\n    /** @phpstan-return Collection<int, LegacyCar> */\n    public function getCars(): Collection\n    {\n        return $this->cars;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Legacy/LegacyUserReference.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Legacy;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'legacy_users_reference')]\n#[Entity]\nclass LegacyUserReference\n{\n    #[Id]\n    #[ManyToOne(targetEntity: 'LegacyUser', inversedBy: 'references')]\n    #[JoinColumn(name: 'iUserIdSource', referencedColumnName: 'iUserId')]\n    private LegacyUser $_source;\n\n    #[Id]\n    #[ManyToOne(targetEntity: 'LegacyUser')]\n    #[JoinColumn(name: 'iUserIdTarget', referencedColumnName: 'iUserId')]\n    private LegacyUser $_target;\n\n    #[Column(type: 'datetime', name: 'created')]\n    private DateTime $created;\n\n    public function __construct(\n        LegacyUser $source,\n        LegacyUser $target,\n        #[Column(type: 'string', length: 255, name: 'description')]\n        private string $_description,\n    ) {\n        $source->addReference($this);\n        $target->addReference($this);\n\n        $this->_source = $source;\n        $this->_target = $target;\n        $this->created = new DateTime('now');\n    }\n\n    public function source(): LegacyUser\n    {\n        return $this->_source;\n    }\n\n    public function target(): LegacyUser\n    {\n        return $this->_target;\n    }\n\n    public function setDescription(string $desc): void\n    {\n        $this->_description = $desc;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->_description;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ManyToManyPersister/ChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ManyToManyPersister;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'manytomanypersister_child')]\n#[Entity]\nclass ChildClass\n{\n    /**\n     * @var Collection|ParentClass[]\n     * @phpstan-var Collection<ParentClass>\n     */\n    #[JoinTable(name: 'parent_child')]\n    #[JoinColumn(name: 'child_id1', referencedColumnName: 'id1')]\n    #[JoinColumn(name: 'child_id2', referencedColumnName: 'other_parent_id')]\n    #[InverseJoinColumn(name: 'parent_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: ParentClass::class, inversedBy: 'children')]\n    public $parents;\n\n    public function __construct(\n        #[Id]\n        #[Column(name: 'id1', type: 'integer')]\n        public int $id1,\n        #[Id]\n        #[ManyToOne(targetEntity: OtherParentClass::class, cascade: ['persist'])]\n        #[JoinColumn(name: 'other_parent_id', referencedColumnName: 'id')]\n        public OtherParentClass $otherParent,\n    ) {\n        $this->parents = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ManyToManyPersister/OtherParentClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ManyToManyPersister;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'manytomanypersister_other_parent')]\n#[Entity]\nclass OtherParentClass\n{\n    public function __construct(\n        #[Id]\n        #[Column(name: 'id', type: 'integer')]\n        public int $id,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ManyToManyPersister/ParentClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ManyToManyPersister;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'manytomanypersister_parent')]\n#[Entity]\nclass ParentClass\n{\n    /**\n     * @var Collection|ChildClass[]\n     * @phpstan-var Collection<ChildClass>\n     */\n    #[ManyToMany(targetEntity: ChildClass::class, mappedBy: 'parents', orphanRemoval: true, cascade: ['persist'])]\n    public $children;\n\n    public function __construct(\n        #[Id]\n        #[Column(name: 'id', type: 'integer')]\n        public int $id,\n    ) {\n        $this->children = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/MixedToOneIdentity/CompositeToOneKeyState.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\MixedToOneIdentity;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass CompositeToOneKeyState\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $state;\n\n    /** @var Country */\n    #[Id]\n    #[ManyToOne(targetEntity: 'Country', cascade: [])]\n    #[JoinColumn(referencedColumnName: 'country')]\n    public $country;\n}\n"
  },
  {
    "path": "tests/Tests/Models/MixedToOneIdentity/Country.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\MixedToOneIdentity;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass Country\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $country;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Navigation/NavCountry.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Navigation;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'navigation_countries')]\n#[Entity]\nclass NavCountry\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, NavPointOfInterest> */\n    #[OneToMany(targetEntity: 'NavPointOfInterest', mappedBy: 'country')]\n    private $pois;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Navigation/NavPhotos.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Navigation;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'navigation_photos')]\n#[Entity]\nclass NavPhotos\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[JoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')]\n        #[JoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')]\n        #[ManyToOne(targetEntity: 'NavPointOfInterest')]\n        private NavPointOfInterest $poi,\n        #[Column(type: 'string', length: 255, name: 'file_name')]\n        private string $file,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getPointOfInterest(): NavPointOfInterest\n    {\n        return $this->poi;\n    }\n\n    public function getFile(): string\n    {\n        return $this->file;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Navigation/NavPointOfInterest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Navigation;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'navigation_pois')]\n#[Entity]\nclass NavPointOfInterest\n{\n    #[Id]\n    #[Column(type: 'integer', name: 'nav_long')]\n    private int $long;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'nav_lat')]\n    private int $lat;\n\n    /** @phpstan-var Collection<int, NavUser> */\n    #[JoinTable(name: 'navigation_pois_visitors')]\n    #[JoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')]\n    #[JoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')]\n    #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'NavUser', cascade: ['persist'])]\n    private $visitors;\n\n    public function __construct(\n        int $lat,\n        int $long,\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n        #[ManyToOne(targetEntity: 'NavCountry', inversedBy: 'pois')]\n        private NavCountry $country,\n    ) {\n        $this->lat      = $lat;\n        $this->long     = $long;\n        $this->visitors = new ArrayCollection();\n    }\n\n    public function getLong(): int\n    {\n        return $this->long;\n    }\n\n    public function getLat(): int\n    {\n        return $this->lat;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function getCountry(): NavCountry\n    {\n        return $this->country;\n    }\n\n    public function addVisitor(NavUser $user): void\n    {\n        $this->visitors[] = $user;\n    }\n\n    /** @phpstan-var Collection<int, NavUser> */\n    public function getVisitors(): Collection\n    {\n        return $this->visitors;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Navigation/NavTour.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Navigation;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'navigation_tours')]\n#[Entity]\nclass NavTour\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @var Collection<int, NavPointOfInterest> */\n    #[JoinTable(name: 'navigation_tour_pois')]\n    #[JoinColumn(name: 'tour_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')]\n    #[InverseJoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')]\n    #[ManyToMany(targetEntity: 'NavPointOfInterest')]\n    private Collection $pois;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n        $this->pois = new ArrayCollection();\n    }\n\n    public function addPointOfInterest(NavPointOfInterest $poi): void\n    {\n        $this->pois[] = $poi;\n    }\n\n    public function getPointOfInterests(): Collection\n    {\n        return $this->pois;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Navigation/NavUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Navigation;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'navigation_users')]\n#[Entity]\nclass NavUser\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/NonPublicSchemaJoins/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\NonPublicSchemaJoins;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Doctrine\\Tests\\Models\\NonPublicSchemaJoins\\User\n */\n#[Table(name: 'readers.user')]\n#[Entity]\nclass User\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    public $id;\n\n    /** @var User[] */\n    #[JoinTable(name: 'author_reader', schema: 'readers')]\n    #[JoinColumn(name: 'author_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'reader_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\Models\\NonPublicSchemaJoins\\User', inversedBy: 'authors')]\n    public $readers;\n\n    /** @var User[] */\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\Models\\NonPublicSchemaJoins\\User', mappedBy: 'readers')]\n    public $authors;\n}\n"
  },
  {
    "path": "tests/Tests/Models/NullDefault/NullDefaultColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\NullDefault;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass NullDefaultColumn\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var mixed */\n    #[Column(options: ['default' => null])]\n    public $nullDefault;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneInverseSideLoad/InverseSide.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneInverseSideLoad;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'one_to_one_inverse_side_load_inverse')]\n#[Entity]\nclass InverseSide\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    /** @var OwningSide */\n    #[OneToOne(targetEntity: OwningSide::class, mappedBy: 'inverse')]\n    public $owning;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneInverseSideLoad/OwningSide.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneInverseSideLoad;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'one_to_one_inverse_side_load_owning')]\n#[Entity]\nclass OwningSide\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    /**\n     * Owning side\n     *\n     * @var InverseSide\n     */\n    #[OneToOne(targetEntity: InverseSide::class, inversedBy: 'owning')]\n    #[JoinColumn(nullable: false, name: 'inverse')]\n    public $inverse;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSide.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'one_to_one_inverse_side_assoc_id_load_inverse')]\nclass InverseSide\n{\n    /** Associative id (owning identifier) */\n    #[Id]\n    #[OneToOne(targetEntity: InverseSideIdTarget::class, inversedBy: 'inverseSide')]\n    #[JoinColumn(name: 'associativeId')]\n    public InverseSideIdTarget $associativeId;\n\n    #[OneToOne(targetEntity: OwningSide::class, mappedBy: 'inverse')]\n    public OwningSide $owning;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSideIdTarget.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'one_to_one_inverse_side_assoc_id_load_inverse_id_target')]\nclass InverseSideIdTarget\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    #[OneToOne(targetEntity: InverseSide::class, mappedBy: 'associativeId')]\n    public InverseSide $inverseSide;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/OwningSide.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'one_to_one_inverse_side_assoc_id_load_owning')]\nclass OwningSide\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    /** Owning side */\n    #[OneToOne(targetEntity: InverseSide::class, inversedBy: 'owning')]\n    #[JoinColumn(name: 'inverse', referencedColumnName: 'associativeId')]\n    public InverseSide $inverse;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneSingleTableInheritance/Cat.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass Cat extends Pet\n{\n    /** @var LitterBox */\n    #[OneToOne(targetEntity: 'LitterBox')]\n    public $litterBox;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneSingleTableInheritance/LitterBox.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'one_to_one_single_table_inheritance_litter_box')]\n#[Entity]\nclass LitterBox\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/OneToOneSingleTableInheritance/Pet.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'one_to_one_single_table_inheritance_pet')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['cat' => 'Cat'])]\nabstract class Pet\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Pagination/Company.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Pagination;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Company\n */\n#[Table(name: 'pagination_company')]\n#[Entity]\nclass Company\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'jurisdiction_code', nullable: true)]\n    public $jurisdiction;\n\n    /** @var Logo */\n    #[OneToOne(targetEntity: 'Logo', mappedBy: 'company', cascade: ['persist'], orphanRemoval: true)]\n    public $logo;\n\n    /** @phpstan-var Collection<int, Department> */\n    #[OneToMany(targetEntity: 'Department', mappedBy: 'company', cascade: ['persist'], orphanRemoval: true)]\n    public $departments;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Pagination/Department.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Pagination;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Department\n */\n#[Table(name: 'pagination_department')]\n#[Entity]\nclass Department\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var Company */\n    #[ManyToOne(targetEntity: 'Company', inversedBy: 'departments', cascade: ['persist'])]\n    public $company;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Pagination/Logo.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Pagination;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Logo\n */\n#[Table(name: 'pagination_logo')]\n#[Entity]\nclass Logo\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $image;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $imageHeight;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $imageWidth;\n\n    /** @var Company */\n    #[OneToOne(targetEntity: 'Company', inversedBy: 'logo', cascade: ['persist'])]\n    #[JoinColumn(name: 'company_id')]\n    public $company;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Pagination/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Pagination;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'pagination_user')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string', length: 255)]\n#[DiscriminatorMap(['user1' => 'User1'])]\nabstract class User\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Pagination/User1.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Pagination;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass User1 extends User\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $email;\n}\n"
  },
  {
    "path": "tests/Tests/Models/PersistentObject/PersistentCollectionContent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\PersistentObject;\n\nuse Doctrine\\Common\\Persistence\\PersistentObject;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass PersistentCollectionContent extends PersistentObject\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n}\n"
  },
  {
    "path": "tests/Tests/Models/PersistentObject/PersistentCollectionHolder.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\PersistentObject;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Persistence\\PersistentObject;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\n\n#[Entity]\nclass PersistentCollectionHolder extends PersistentObject\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var Collection */\n    #[ManyToMany(targetEntity: 'PersistentCollectionContent', cascade: ['all'], fetch: 'EXTRA_LAZY')]\n    protected $collection;\n\n    public function __construct()\n    {\n        $this->collection = new ArrayCollection();\n    }\n\n    public function addElement(PersistentCollectionContent $element): void\n    {\n        $this->collection->add($element);\n    }\n\n    public function getCollection(): Collection\n    {\n        return clone $this->collection;\n    }\n\n    public function getRawCollection(): Collection\n    {\n        return $this->collection;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/PersistentObject/PersistentEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\PersistentObject;\n\nuse Doctrine\\Common\\Persistence\\PersistentObject;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass PersistentEntity extends PersistentObject\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    protected $name;\n\n    /** @var PersistentEntity */\n    #[ManyToOne(targetEntity: 'PersistentEntity')]\n    protected $parent;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Project/Project.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Project;\n\nclass Project\n{\n    /** @var string */\n    private $id;\n\n    /** @var string */\n    private $name;\n\n    public function __construct(string $id, string $name)\n    {\n        $this->id   = $id;\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Project/ProjectId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Project;\n\nclass ProjectId\n{\n    /** @var string */\n    private $id;\n\n    public function __construct(string $id)\n    {\n        $this->id = $id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Project/ProjectInvalidMapping.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Project;\n\nclass ProjectInvalidMapping\n{\n    /** @var string */\n    private $id;\n\n    /** @var string */\n    private $name;\n\n    public function __construct(string $id, string $name)\n    {\n        $this->id   = $id;\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Project/ProjectName.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Project;\n\nfinal class ProjectName\n{\n    /** @var string */\n    private $name;\n\n    public function __construct(string $name)\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/PropertyHooks/MappingVirtualProperty.php",
    "content": "<?php\n// phpcs:ignoreFile\nnamespace Doctrine\\Tests\\Models\\PropertyHooks;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'property_hooks_user')]\nclass MappingVirtualProperty\n{\n    #[Id, GeneratedValue, Column(type: Types::INTEGER)]\n    public ?int $id;\n\n    #[Column(type: Types::STRING)]\n    public string $first;\n\n    #[Column(type: Types::STRING)]\n    public string $last;\n\n    #[Column(type: Types::STRING)]\n    public string $fullName {\n        get => $this->first . \" \" . $this->last;\n        set {\n            [$this->first, $this->last] = explode(' ', $value, 2);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/PropertyHooks/User.php",
    "content": "<?php\n// phpcs:ignoreFile\nnamespace Doctrine\\Tests\\Models\\PropertyHooks;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'property_hooks_user')]\nclass User\n{\n    #[Id, GeneratedValue, Column(type: Types::INTEGER)]\n    public ?int $id;\n\n    #[Column(type: Types::STRING)]\n    public string $first {\n        set {\n            if (strlen($value) === 0) {\n                throw new ValueError(\"Name must be non-empty\");\n            }\n            $this->first = $value;\n        }\n    }\n\n    #[Column(type: Types::STRING)]\n    public string $last {\n        set {\n            if (strlen($value) === 0) {\n                throw new ValueError(\"Name must be non-empty\");\n            }\n            $this->last = $value;\n        }\n    }\n\n    public string $fullName {\n        get => $this->first . \" \" . $this->last;\n        set {\n            [$this->first, $this->last] = explode(' ', $value, 2);\n        }\n    }\n\n    #[Column(type: Types::STRING)]\n    public string $language = 'de' {\n        // Override the \"read\" action with arbitrary logic.\n        get => strtoupper($this->language);\n\n        // Override the \"write\" action with arbitrary logic.\n        set {\n            $this->language = strtolower($value);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/Address.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`quote-address`')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string', length: 255)]\n#[DiscriminatorMap(['simple' => Address::class, 'full' => FullAddress::class])]\nclass Address\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: '`address-id`')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: '`address-zip`')]\n    public $zip;\n\n    /** @var User */\n    #[OneToOne(targetEntity: 'User', inversedBy: 'address')]\n    #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')]\n    public $user;\n\n    public function setUser(User $user): void\n    {\n        if ($this->user !== $user) {\n            $this->user = $user;\n            $user->setAddress($this);\n        }\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getZip(): string\n    {\n        return $this->zip;\n    }\n\n    public function getUser(): User\n    {\n        return $this->user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/City.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`quote-city`')]\n#[Entity]\nclass City\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: '`city-id`')]\n    public $id;\n\n    public function __construct(\n        #[Column(name: '`city-name`')]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/FullAddress.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass FullAddress extends Address\n{\n    /** @var City */\n    #[OneToOne(targetEntity: City::class, cascade: ['persist'])]\n    #[JoinColumn(name: '`city-id`', referencedColumnName: '`city-id`')]\n    public $city;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/Group.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`quote-group`')]\n#[Entity]\nclass Group\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: '`group-id`')]\n    public $id;\n\n    /** @phpstan-var Collection<int, User> */\n    #[ManyToMany(targetEntity: 'User', mappedBy: 'groups')]\n    public $users;\n\n    public function __construct(\n        #[Column(name: '`group-name`')]\n        public string|null $name = null,\n        #[ManyToOne(targetEntity: 'Group', cascade: ['persist'])]\n        #[JoinColumn(name: '`parent-id`', referencedColumnName: '`group-id`')]\n        public Group|null $parent = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/NumericEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'table')]\n#[Entity]\nclass NumericEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer', name: '`1:1`')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255, name: '`2:2`')]\n        public string $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/Phone.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`quote-phone`')]\n#[Entity]\nclass Phone\n{\n    /** @var string */\n    #[Id]\n    #[Column(name: '`phone-number`')]\n    public $number;\n\n    /** @var User */\n    #[ManyToOne(targetEntity: 'User', inversedBy: 'phones')]\n    #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Quote/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Quote;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: '`quote-user`')]\n#[Entity]\nclass User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: '`user-id`')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: '`user-name`')]\n    public $name;\n\n    /** @phpstan-var Collection<int, Phone> */\n    #[OneToMany(targetEntity: 'Phone', mappedBy: 'user', cascade: ['persist'])]\n    public $phones;\n\n    /** @var Address */\n    #[OneToOne(targetEntity: 'Address', mappedBy: 'user', cascade: ['persist'], fetch: 'EAGER')]\n    public $address;\n\n    /** @phpstan-var Collection<int, Group> */\n    #[JoinTable(name: '`quote-users-groups`')]\n    #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')]\n    #[InverseJoinColumn(name: '`group-id`', referencedColumnName: '`group-id`')]\n    #[ManyToMany(targetEntity: 'Group', inversedBy: 'users', cascade: ['all'], fetch: 'EXTRA_LAZY')]\n    public $groups;\n\n    public function __construct()\n    {\n        $this->phones = new ArrayCollection();\n        $this->groups = new ArrayCollection();\n    }\n\n    /** @phpstan-return Collection<int, Phone> */\n    public function getPhones(): Collection\n    {\n        return $this->phones;\n    }\n\n    public function getAddress(): Address|null\n    {\n        return $this->address;\n    }\n\n    /** @phpstan-return Collection<int, Group> */\n    public function getGroups(): Collection\n    {\n        return $this->groups;\n    }\n\n    public function setAddress(Address $address): void\n    {\n        if ($this->address !== $address) {\n            $this->address = $address;\n            $address->setUser($this);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ReadonlyProperties/Author.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ReadonlyProperties;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'author')]\nclass Author\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    private readonly int $id;\n\n    #[Column]\n    private readonly string $name;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ReadonlyProperties/Book.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ReadonlyProperties;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'book')]\nclass Book\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    private readonly int $id;\n\n    #[Column]\n    private readonly string $title;\n\n    #[ManyToMany(targetEntity: Author::class)]\n    #[JoinTable(name: 'book_author')]\n    private readonly Collection $authors;\n\n    public function __construct()\n    {\n        $this->authors = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    /** @return list<Author> */\n    public function getAuthors(): array\n    {\n        return $this->authors->getValues();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ReadonlyProperties/SimpleBook.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ReadonlyProperties;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'simple_book')]\nclass SimpleBook\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    private readonly int $id;\n\n    #[Column]\n    private readonly string $title;\n\n    #[ManyToOne]\n    #[JoinColumn(nullable: false)]\n    private readonly Author $author;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    public function getAuthor(): Author\n    {\n        return $this->author;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Reflection/AbstractEmbeddable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Reflection;\n\n/**\n * A test asset used to check that embeddables support properties defined in abstract classes\n */\nabstract class AbstractEmbeddable\n{\n    private string $propertyInAbstractClass;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Reflection/ArrayObjectExtendingClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Reflection;\n\nuse ArrayObject;\n\n/**\n * A test asset extending {@see \\ArrayObject}, useful for verifying internal classes issues with reflection\n */\nclass ArrayObjectExtendingClass extends ArrayObject\n{\n    /** @var mixed */\n    private $privateProperty;\n\n    /** @var mixed */\n    protected $protectedProperty;\n\n    /** @var mixed */\n    public $publicProperty;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Reflection/ClassWithMixedProperties.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Reflection;\n\nclass ClassWithMixedProperties extends ParentClass\n{\n    /** @var string */\n    public static $staticProperty = 'staticProperty';\n\n    /** @var string */\n    public $publicProperty = 'publicProperty';\n\n    /** @var string */\n    protected $protectedProperty = 'protectedProperty';\n\n    private string $privateProperty = 'privateProperty';\n\n    private string $privatePropertyOverride = 'privatePropertyOverride';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Reflection/ConcreteEmbeddable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Reflection;\n\n/**\n * A test asset used to check that embeddables support properties defined in abstract classes\n */\nclass ConcreteEmbeddable extends AbstractEmbeddable\n{\n    private string $propertyInConcreteClass;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Reflection/ParentClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Reflection;\n\nclass ParentClass\n{\n    private string $privatePropertyOverride = 'privatePropertyOverride';\n}\n"
  },
  {
    "path": "tests/Tests/Models/Routing/RoutingLeg.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Routing;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass RoutingLeg\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var RoutingLocation */\n    #[ManyToOne(targetEntity: 'RoutingLocation')]\n    #[JoinColumn(name: 'from_id', referencedColumnName: 'id')]\n    public $fromLocation;\n\n    /** @var RoutingLocation */\n    #[ManyToOne(targetEntity: 'RoutingLocation')]\n    #[JoinColumn(name: 'to_id', referencedColumnName: 'id')]\n    public $toLocation;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    public $departureDate;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    public $arrivalDate;\n}\n"
  },
  {
    "path": "tests/Tests/Models/Routing/RoutingLocation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Routing;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass RoutingLocation\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Routing/RoutingRoute.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Routing;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\n\n#[Entity]\nclass RoutingRoute\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var Collection<int, RoutingLeg> */\n    #[JoinTable(name: 'RoutingRouteLegs')]\n    #[JoinColumn(name: 'route_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'leg_id', referencedColumnName: 'id', unique: true)]\n    #[ManyToMany(targetEntity: 'RoutingLeg', cascade: ['all'])]\n    #[OrderBy(['departureDate' => 'ASC'])]\n    public $legs;\n\n    /** @var Collection<int, RoutingRouteBooking> */\n    #[OneToMany(targetEntity: 'RoutingRouteBooking', mappedBy: 'route')]\n    #[OrderBy(['passengerName' => 'ASC'])]\n    public $bookings = [];\n\n    public function __construct()\n    {\n        $this->legs = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Routing/RoutingRouteBooking.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Routing;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\n\n#[Entity]\nclass RoutingRouteBooking\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var RoutingRoute */\n    #[ManyToOne(targetEntity: 'RoutingRoute', inversedBy: 'bookings')]\n    #[JoinColumn(name: 'route_id', referencedColumnName: 'id')]\n    public $route;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $passengerName = null;\n\n    public function getPassengerName(): string\n    {\n        return $this->passengerName;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/StockExchange/Bond.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\StockExchange;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Bonds have many stocks. This uses a many to many association and fails to model how many of a\n * particular stock a bond has. But i Need a many-to-many association, so please bear with my modelling skills ;)\n */\n#[Table(name: 'exchange_bonds')]\n#[Entity]\nclass Bond\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    /** @var Stock[] */\n    #[JoinTable(name: 'exchange_bonds_stocks')]\n    #[ManyToMany(targetEntity: 'Stock', indexBy: 'symbol')]\n    public $stocks;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function addStock(Stock $stock): void\n    {\n        $this->stocks[$stock->getSymbol()] = $stock;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/StockExchange/Market.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\StockExchange;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'exchange_markets')]\n#[Entity]\nclass Market\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var ArrayCollection<string, Stock> */\n    #[OneToMany(targetEntity: 'Stock', mappedBy: 'market', indexBy: 'symbol')]\n    public $stocks;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n        $this->stocks = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function addStock(Stock $stock): void\n    {\n        $this->stocks[$stock->getSymbol()] = $stock;\n    }\n\n    public function getStock(string $symbol): Stock\n    {\n        return $this->stocks[$symbol];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/StockExchange/Stock.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\StockExchange;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'exchange_stocks')]\n#[Entity]\nclass Stock\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'decimal', precision: 10)]\n    private string $price;\n\n    public function __construct(\n        /**\n         * For real this column would have to be unique=true. But I want to test behavior of non-unique overrides.\n         */\n        #[Column(type: 'string', length: 255)]\n        private string $symbol,\n        float $price,\n        #[ManyToOne(targetEntity: 'Market', inversedBy: 'stocks')]\n        private Market $market,\n    ) {\n        $this->price = (string) $price;\n        $market->addStock($this);\n    }\n\n    public function getSymbol(): string\n    {\n        return $this->symbol;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Taxi/Car.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Taxi;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'taxi_car')]\n#[Entity]\nclass Car\n{\n    #[Id]\n    #[Column(type: 'string', length: 25)]\n    #[GeneratedValue(strategy: 'NONE')]\n    private string|null $brand = null;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $model = null;\n\n    /** @phpstan-var Collection<int, Ride> */\n    #[OneToMany(targetEntity: 'Ride', mappedBy: 'car')]\n    private $freeCarRides;\n\n    /** @phpstan-var Collection<int, PaidRide> */\n    #[OneToMany(targetEntity: 'PaidRide', mappedBy: 'car')]\n    private $carRides;\n\n    public function getBrand(): string\n    {\n        return $this->brand;\n    }\n\n    public function setBrand(string $brand): void\n    {\n        $this->brand = $brand;\n    }\n\n    public function setModel(string $model): void\n    {\n        $this->model = $model;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Taxi/Driver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Taxi;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'taxi_driver')]\n#[Entity]\nclass Driver\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $name = null;\n\n    /** @phpstan-var Collection<int, Ride> */\n    #[OneToMany(targetEntity: 'Ride', mappedBy: 'driver')]\n    private $freeDriverRides;\n\n    /** @phpstan-var Collection<int, PaidRide> */\n    #[OneToMany(targetEntity: 'PaidRide', mappedBy: 'driver')]\n    private $driverRides;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Taxi/PaidRide.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Taxi;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Same as Ride but with an extra column that is not part of the composite primary key\n */\n#[Table(name: 'taxi_paid_ride')]\n#[Entity]\nclass PaidRide\n{\n    /** @var float */\n    #[Column(type: 'decimal', precision: 6, scale: 2)]\n    private $fare;\n\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'Driver', inversedBy: 'driverRides')]\n        #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')]\n        private Driver $driver,\n        #[Id]\n        #[ManyToOne(targetEntity: 'Car', inversedBy: 'carRides')]\n        #[JoinColumn(name: 'car', referencedColumnName: 'brand')]\n        private Car $car,\n    ) {\n    }\n\n    public function setFare($fare): void\n    {\n        $this->fare = $fare;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Taxi/Ride.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Taxi;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n/**\n * Test model that contains only Id-columns\n */\n#[Table(name: 'taxi_ride')]\n#[Entity]\nclass Ride\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'Driver', inversedBy: 'freeDriverRides')]\n        #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')]\n        private Driver $driver,\n        #[Id]\n        #[ManyToOne(targetEntity: 'Car', inversedBy: 'freeCarRides')]\n        #[JoinColumn(name: 'car', referencedColumnName: 'brand')]\n        private Car $car,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Tweet/Tweet.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Tweet;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'tweet_tweet')]\n#[Entity]\nclass Tweet\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $content;\n\n    /** @var User */\n    #[ManyToOne(targetEntity: 'User', inversedBy: 'tweets')]\n    public $author;\n\n    public function setAuthor(User $user): void\n    {\n        $this->author = $user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Tweet/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Tweet;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'tweet_user')]\n#[Entity]\nclass User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, Tweet> */\n    #[OneToMany(targetEntity: 'Tweet', mappedBy: 'author', cascade: ['persist'], fetch: 'EXTRA_LAZY')]\n    public $tweets;\n\n    /** @phpstan-var Collection<int, UserList> */\n    #[OneToMany(targetEntity: 'UserList', mappedBy: 'owner', fetch: 'EXTRA_LAZY', orphanRemoval: true)]\n    public $userLists;\n\n    public function __construct()\n    {\n        $this->tweets    = new ArrayCollection();\n        $this->userLists = new ArrayCollection();\n    }\n\n    public function addTweet(Tweet $tweet): void\n    {\n        $tweet->setAuthor($this);\n        $this->tweets->add($tweet);\n    }\n\n    public function addUserList(UserList $userList): void\n    {\n        $userList->owner = $this;\n        $this->userLists->add($userList);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Tweet/UserList.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Tweet;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'tweet_user_list')]\n#[Entity]\nclass UserList\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $listName;\n\n    /** @var User */\n    #[ManyToOne(targetEntity: 'User', inversedBy: 'userLists')]\n    public $owner;\n}\n"
  },
  {
    "path": "tests/Tests/Models/TypedProperties/Contact.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\TypedProperties;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Embeddable]\nclass Contact\n{\n    #[ORM\\Column]\n    public string|null $email = null;\n}\n"
  },
  {
    "path": "tests/Tests/Models/TypedProperties/UserTyped.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\TypedProperties;\n\nuse BcMath\\Number;\nuse DateInterval;\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'cms_users_typed')]\nclass UserTyped\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\Column(length: 50)]\n    public string|null $status = null;\n\n    #[ORM\\Column(length: 255, unique: true)]\n    public string $username;\n\n    #[ORM\\Column]\n    public DateInterval $dateInterval;\n\n    #[ORM\\Column]\n    public DateTime $dateTime;\n\n    #[ORM\\Column]\n    public DateTimeImmutable $dateTimeImmutable;\n\n    #[ORM\\Column]\n    public array $array;\n\n    #[ORM\\Column]\n    public bool $boolean;\n\n    #[ORM\\Column]\n    public float $float;\n\n    #[ORM\\OneToOne(cascade: ['persist'], orphanRemoval: true)]\n    #[ORM\\JoinColumn]\n    public CmsEmail $email;\n\n    #[ORM\\ManyToOne]\n    public CmsEmail|null $mainEmail = null;\n\n    #[ORM\\Embedded]\n    public Contact|null $contact = null;\n\n    #[ORM\\Column(precision: 5, scale: 2)]\n    public Number|null $bodyHeight = null;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable(\n            ['name' => 'cms_users_typed'],\n        );\n\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'status',\n                'length' => 50,\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'username',\n                'length' => 255,\n                'unique' => true,\n            ],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'dateInterval'],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'dateTime'],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'dateTimeImmutable'],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'array'],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'boolean'],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'float'],\n        );\n\n        $metadata->mapOneToOne(\n            [\n                'fieldName' => 'email',\n                'cascade' =>\n                    [0 => 'persist'],\n                'joinColumns' =>\n                    [\n                        0 =>\n                            ['referencedColumnName' => 'id'],\n                    ],\n                'orphanRemoval' => true,\n            ],\n        );\n\n        $metadata->mapManyToOne(\n            ['fieldName' => 'mainEmail'],\n        );\n\n        $metadata->mapEmbedded(['fieldName' => 'contact']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/TypedProperties/UserTypedWithCustomTypedField.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\TypedProperties;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'cms_users_typed_with_custom_typed_field')]\nclass UserTypedWithCustomTypedField\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\Column]\n    public CustomIdObject $customId;\n\n    #[ORM\\Column]\n    public int $customIntTypedField;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable(\n            ['name' => 'cms_users_typed_with_custom_typed_field'],\n        );\n\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        $metadata->mapField(\n            ['fieldName' => 'customId'],\n        );\n\n        $metadata->mapField(\n            ['fieldName' => 'customIntTypedField'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Upsertable/Insertable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Upsertable;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'insertable_column')]\nclass Insertable\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', insertable: false, options: ['default' => '1234'], generated: 'INSERT')]\n    public $nonInsertableContent;\n\n    /** @var string */\n    #[Column(type: 'string', insertable: true)]\n    public $insertableContent;\n\n    public static function loadMetadata(ClassMetadata $metadata): ClassMetadata\n    {\n        $metadata->setPrimaryTable(\n            ['name' => 'insertable_column'],\n        );\n\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'nonInsertableContent',\n                'notInsertable' => true,\n                'options' => ['default' => '1234'],\n                'generated' => ClassMetadata::GENERATED_INSERT,\n            ],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'insertableContent'],\n        );\n\n        return $metadata;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/Upsertable/Updatable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\Upsertable;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'updatable_column')]\nclass Updatable\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'non_updatable_content', type: 'string', length: 255, updatable: false, generated: 'ALWAYS')]\n    public $nonUpdatableContent;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, updatable: true)]\n    public $updatableContent;\n\n    public static function loadMetadata(ClassMetadata $metadata): ClassMetadata\n    {\n        $metadata->setPrimaryTable(\n            ['name' => 'updatable_column'],\n        );\n\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'nonUpdatableContent',\n                'notUpdatable' => true,\n                'generated' => ClassMetadata::GENERATED_ALWAYS,\n            ],\n        );\n        $metadata->mapField(\n            ['fieldName' => 'updatableContent'],\n        );\n\n        return $metadata;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/AuxiliaryEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_auxiliary')]\n#[Entity]\nclass AuxiliaryEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id4;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_manytomany_compositeid')]\n#[Entity]\nclass InversedManyToManyCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @phpstan-var Collection<int, OwningManyToManyCompositeIdEntity> */\n    #[ManyToMany(targetEntity: 'OwningManyToManyCompositeIdEntity', mappedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_manytomany_compositeid_foreignkey')]\n#[Entity]\nclass InversedManyToManyCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var AuxiliaryEntity */\n    #[ManyToOne(targetEntity: 'AuxiliaryEntity')]\n    #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')]\n    #[Id]\n    public $foreignEntity;\n\n    /** @phpstan-var Collection<int, OwningManyToManyCompositeIdForeignKeyEntity> */\n    #[ManyToMany(targetEntity: 'OwningManyToManyCompositeIdForeignKeyEntity', mappedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedManyToManyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_manytomany')]\n#[Entity]\nclass InversedManyToManyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255, nullable: true)]\n    public $field = null;\n\n    /** @phpstan-var Collection<int, OwningManyToManyEntity> */\n    #[ManyToMany(targetEntity: OwningManyToManyEntity::class, mappedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_manytomany_extralazy')]\n#[Entity]\nclass InversedManyToManyExtraLazyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var Collection<int, OwningManyToManyExtraLazyEntity> */\n    #[ManyToMany(targetEntity: 'OwningManyToManyExtraLazyEntity', mappedBy: 'associatedEntities', fetch: 'EXTRA_LAZY', indexBy: 'id2')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetomany_compositeid')]\n#[Entity]\nclass InversedOneToManyCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'some_property')]\n    public $someProperty;\n\n    /** @phpstan-var Collection<int, OwningManyToOneCompositeIdEntity> */\n    #[OneToMany(targetEntity: 'OwningManyToOneCompositeIdEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetomany_compositeid_foreignkey')]\n#[Entity]\nclass InversedOneToManyCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var AuxiliaryEntity */\n    #[ManyToOne(targetEntity: 'AuxiliaryEntity')]\n    #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')]\n    #[Id]\n    public $foreignEntity;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'some_property')]\n    public $someProperty;\n\n    /** @phpstan-var Collection<int, OwningManyToOneCompositeIdForeignKeyEntity> */\n    #[OneToMany(targetEntity: 'OwningManyToOneCompositeIdForeignKeyEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToManyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetomany')]\n#[Entity]\nclass InversedOneToManyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @phpstan-var Collection<int, OwningManyToOneEntity> */\n    #[OneToMany(targetEntity: 'OwningManyToOneEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntities;\n\n    /** @var string */\n    #[Column(type: 'string', name: 'some_property', length: 255, nullable: true)]\n    public $someProperty;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetomany_extralazy')]\n#[Entity]\nclass InversedOneToManyExtraLazyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var Collection<int, OwningManyToOneExtraLazyEntity> */\n    #[OneToMany(targetEntity: 'OwningManyToOneExtraLazyEntity', mappedBy: 'associatedEntity', fetch: 'EXTRA_LAZY', indexBy: 'id2')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetoone_compositeid')]\n#[Entity]\nclass InversedOneToOneCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'some_property')]\n    public $someProperty;\n\n    /** @var OwningOneToOneCompositeIdEntity */\n    #[OneToOne(targetEntity: 'OwningOneToOneCompositeIdEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetoone_compositeid_foreignkey')]\n#[Entity]\nclass InversedOneToOneCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var AuxiliaryEntity */\n    #[ManyToOne(targetEntity: 'AuxiliaryEntity')]\n    #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')]\n    #[Id]\n    public $foreignEntity;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'some_property')]\n    public $someProperty;\n\n    /** @var OwningOneToOneCompositeIdForeignKeyEntity */\n    #[OneToOne(targetEntity: 'OwningOneToOneCompositeIdForeignKeyEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/InversedOneToOneEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_inversed_onetoone')]\n#[Entity]\nclass InversedOneToOneEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id1;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255, name: 'some_property')]\n    public $someProperty;\n\n    /** @var OwningOneToOneEntity */\n    #[OneToOne(targetEntity: 'OwningOneToOneEntity', mappedBy: 'associatedEntity')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytomany_compositeid')]\n#[Entity]\nclass OwningManyToManyCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id3;\n\n    /** @var Collection<int, InversedManyToManyCompositeIdEntity> */\n    #[JoinTable(name: 'vct_xref_manytomany_compositeid')]\n    #[JoinColumn(name: 'owning_id', referencedColumnName: 'id3')]\n    #[InverseJoinColumn(name: 'inversed_id1', referencedColumnName: 'id1')]\n    #[InverseJoinColumn(name: 'inversed_id2', referencedColumnName: 'id2')]\n    #[ManyToMany(targetEntity: 'InversedManyToManyCompositeIdEntity', inversedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytomany_compositeid_foreignkey')]\n#[Entity]\nclass OwningManyToManyCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var Collection<int, InversedManyToManyCompositeIdForeignKeyEntity> */\n    #[JoinTable(name: 'vct_xref_manytomany_compositeid_foreignkey')]\n    #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')]\n    #[InverseJoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    #[InverseJoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')]\n    #[ManyToMany(targetEntity: 'InversedManyToManyCompositeIdForeignKeyEntity', inversedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToManyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytomany')]\n#[Entity]\nclass OwningManyToManyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255, nullable: true)]\n    public $field = null;\n\n    /** @var Collection<int, InversedManyToManyEntity> */\n    #[JoinTable(name: 'vct_xref_manytomany')]\n    #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')]\n    #[InverseJoinColumn(name: 'inversed_id', referencedColumnName: 'id1')]\n    #[ManyToMany(targetEntity: 'InversedManyToManyEntity', inversedBy: 'associatedEntities')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytomany_extralazy')]\n#[Entity]\nclass OwningManyToManyExtraLazyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var Collection<int, InversedManyToManyExtraLazyEntity> */\n    #[JoinTable(name: 'vct_xref_manytomany_extralazy')]\n    #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')]\n    #[InverseJoinColumn(name: 'inversed_id', referencedColumnName: 'id1')]\n    #[ManyToMany(targetEntity: 'InversedManyToManyExtraLazyEntity', inversedBy: 'associatedEntities', fetch: 'EXTRA_LAZY', indexBy: 'id1')]\n    public $associatedEntities;\n\n    public function __construct()\n    {\n        $this->associatedEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytoone_compositeid')]\n#[Entity]\nclass OwningManyToOneCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id3;\n\n    /** @var InversedOneToManyCompositeIdEntity */\n    #[JoinColumn(name: 'associated_id1', referencedColumnName: 'id1')]\n    #[JoinColumn(name: 'associated_id2', referencedColumnName: 'id2')]\n    #[ManyToOne(targetEntity: 'InversedOneToManyCompositeIdEntity', inversedBy: 'associatedEntities')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytoone_compositeid_foreignkey')]\n#[Entity]\nclass OwningManyToOneCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var InversedOneToManyCompositeIdForeignKeyEntity */\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    #[JoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')]\n    #[ManyToOne(targetEntity: 'InversedOneToManyCompositeIdForeignKeyEntity', inversedBy: 'associatedEntities')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToOneEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytoone')]\n#[Entity]\nclass OwningManyToOneEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var string */\n    #[Column(type: 'rot13', length: 255, nullable: true)]\n    public $field = null;\n\n    /** @var InversedOneToManyEntity */\n    #[ManyToOne(targetEntity: InversedOneToManyEntity::class, inversedBy: 'associatedEntities')]\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytoone_extralazy')]\n#[Entity]\nclass OwningManyToOneExtraLazyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var InversedOneToManyExtraLazyEntity */\n    #[ManyToOne(targetEntity: 'InversedOneToManyExtraLazyEntity', inversedBy: 'associatedEntities')]\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningManyToOneIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_manytoone_foreignkey')]\n#[Entity]\nclass OwningManyToOneIdForeignKeyEntity\n{\n    /** @var associatedEntities */\n    #[Id]\n    #[ManyToOne(targetEntity: AuxiliaryEntity::class, inversedBy: 'associatedEntities')]\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id4')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_onetoone_compositeid')]\n#[Entity]\nclass OwningOneToOneCompositeIdEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id3;\n\n    /** @var InversedOneToOneCompositeIdEntity */\n    #[JoinColumn(name: 'associated_id1', referencedColumnName: 'id1')]\n    #[JoinColumn(name: 'associated_id2', referencedColumnName: 'id2')]\n    #[OneToOne(targetEntity: 'InversedOneToOneCompositeIdEntity', inversedBy: 'associatedEntity')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\UniqueConstraint;\n\n#[Table(name: 'vct_owning_onetoone_compositeid_foreignkey')]\n#[UniqueConstraint(name: 'associated_entity_uniq', columns: ['associated_id', 'associated_foreign_id'])]\n#[Entity]\nclass OwningOneToOneCompositeIdForeignKeyEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var InversedOneToOneCompositeIdForeignKeyEntity */\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    #[JoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')]\n    #[OneToOne(targetEntity: 'InversedOneToOneCompositeIdForeignKeyEntity', inversedBy: 'associatedEntity')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueConversionType/OwningOneToOneEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueConversionType;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'vct_owning_onetoone')]\n#[Entity]\nclass OwningOneToOneEntity\n{\n    /** @var string */\n    #[Column(type: 'rot13', length: 255)]\n    #[Id]\n    public $id2;\n\n    /** @var InversedOneToOneEntity */\n    #[OneToOne(targetEntity: 'InversedOneToOneEntity', inversedBy: 'associatedEntity')]\n    #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')]\n    public $associatedEntity;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueObjects/Name.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueObjects;\n\nclass Name\n{\n    private string $firstName;\n\n    private string $lastName;\n}\n"
  },
  {
    "path": "tests/Tests/Models/ValueObjects/Person.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\ValueObjects;\n\nclass Person\n{\n    private int $id;\n\n    private string $name;\n}\n"
  },
  {
    "path": "tests/Tests/Models/VersionedManyToOne/Article.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\VersionedManyToOne;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\n\n#[Table(name: 'versioned_many_to_one_article')]\n#[Entity]\nclass Article\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'name')]\n    public $name;\n\n    /** @var Category */\n    #[ManyToOne(targetEntity: 'Category', cascade: ['persist'])]\n    public $category;\n\n    /**\n     * Version column\n     *\n     * @var int\n     */\n    #[Column(type: 'integer', name: 'version')]\n    #[Version]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/Models/VersionedManyToOne/Category.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\VersionedManyToOne;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\n\n#[Table(name: 'versioned_many_to_one_category')]\n#[Entity]\nclass Category\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /**\n     * Version column\n     *\n     * @var int\n     */\n    #[Column(type: 'integer', name: 'version')]\n    #[Version]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/Models/VersionedOneToOne/FirstRelatedEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\VersionedOneToOne;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\n\n#[Table(name: 'first_entity')]\n#[Entity]\nclass FirstRelatedEntity\n{\n    /** @var SecondRelatedEntity */\n    #[Id]\n    #[OneToOne(targetEntity: 'SecondRelatedEntity', fetch: 'EAGER')]\n    #[JoinColumn(name: 'second_entity_id', referencedColumnName: 'id')]\n    public $secondEntity;\n\n    /** @var string */\n    #[Column(name: 'name')]\n    public $name;\n\n    /**\n     * @var int\n     * Version column\n     */\n    #[Column(type: 'integer', name: 'version')]\n    #[Version]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/Models/VersionedOneToOne/SecondRelatedEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Models\\VersionedOneToOne;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\n\n#[Table(name: 'second_entity')]\n#[Entity]\nclass SecondRelatedEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'name')]\n    public $name;\n\n    /**\n     * @var int\n     * Version column\n     */\n    #[Column(type: 'integer', name: 'version')]\n    #[Version]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/AbstractQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nfinal class AbstractQueryTest extends TestCase\n{\n    public function testItMakesHydrationCacheProfilesAwareOfTheResultCache(): void\n    {\n        $cache = $this->createMock(CacheItemPoolInterface::class);\n\n        $configuration = new Configuration();\n        $configuration->setHydrationCache($cache);\n        $entityManager = $this->createMock(EntityManagerInterface::class);\n        $entityManager->method('getConfiguration')->willReturn($configuration);\n        $query        = new TestQuery($entityManager);\n        $cacheProfile = new QueryCacheProfile();\n\n        $query->setHydrationCacheProfile($cacheProfile);\n        self::assertSame($cache, $query->getHydrationCacheProfile()->getResultCache());\n    }\n\n    public function testItMakesResultCacheProfilesAwareOfTheResultCache(): void\n    {\n        $cache = $this->createMock(CacheItemPoolInterface::class);\n\n        $configuration = new Configuration();\n        $configuration->setResultCache($cache);\n        $entityManager = $this->createMock(EntityManagerInterface::class);\n        $entityManager->method('getConfiguration')->willReturn($configuration);\n        $query = new TestQuery($entityManager);\n        $query->setResultCacheProfile(new QueryCacheProfile());\n\n        self::assertSame($cache, $query->getResultCache());\n    }\n\n    public function testSettingTheResultCacheIsPossibleWithoutCallingDeprecatedMethods(): void\n    {\n        $cache = $this->createMock(CacheItemPoolInterface::class);\n\n        $entityManager = $this->createMock(EntityManagerInterface::class);\n        $entityManager->method('getConfiguration')->willReturn(new Configuration());\n        $query = new TestQuery($entityManager);\n        $query->setResultCache($cache);\n\n        self::assertSame($cache, $query->getResultCache());\n    }\n}\n\nclass TestQuery extends AbstractQuery\n{\n    public function getSQL(): string\n    {\n        return '';\n    }\n\n    protected function _doExecute(): Result|int\n    {\n        return 0;\n    }\n\n    public function getResultCache(): CacheItemPoolInterface|null\n    {\n        return $this->queryCacheProfile->getResultCache();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/CacheConfigTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CacheConfiguration;\nuse Doctrine\\ORM\\Cache\\CacheFactory;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\QueryCacheValidator;\nuse Doctrine\\ORM\\Cache\\TimestampQueryCacheValidator;\nuse Doctrine\\ORM\\Cache\\TimestampRegion;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\n#[CoversClass(CacheConfiguration::class)]\n#[Group('DDC-2183')]\nclass CacheConfigTest extends TestCase\n{\n    private CacheConfiguration $config;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->config = new CacheConfiguration();\n    }\n\n    public function testSetGetRegionLifetime(): void\n    {\n        $config = $this->config->getRegionsConfiguration();\n\n        $config->setDefaultLifetime(111);\n\n        self::assertEquals($config->getDefaultLifetime(), $config->getLifetime('foo_region'));\n\n        $config->setLifetime('foo_region', 222);\n\n        self::assertEquals(222, $config->getLifetime('foo_region'));\n    }\n\n    public function testSetGetCacheLogger(): void\n    {\n        $logger = $this->createMock(CacheLogger::class);\n\n        self::assertNull($this->config->getCacheLogger());\n\n        $this->config->setCacheLogger($logger);\n\n        self::assertEquals($logger, $this->config->getCacheLogger());\n    }\n\n    public function testSetGetCacheFactory(): void\n    {\n        $factory = $this->createMock(CacheFactory::class);\n\n        self::assertNull($this->config->getCacheFactory());\n\n        $this->config->setCacheFactory($factory);\n\n        self::assertEquals($factory, $this->config->getCacheFactory());\n    }\n\n    public function testSetGetQueryValidator(): void\n    {\n        $factory = $this->createMock(CacheFactory::class);\n        $factory->method('getTimestampRegion')->willReturn($this->createMock(TimestampRegion::class));\n\n        $this->config->setCacheFactory($factory);\n\n        $validator = $this->createMock(QueryCacheValidator::class);\n\n        self::assertInstanceOf(TimestampQueryCacheValidator::class, $this->config->getQueryValidator());\n\n        $this->config->setQueryValidator($validator);\n\n        self::assertEquals($validator, $this->config->getQueryValidator());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/CacheKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\n#[Group('DDC-2183')]\nclass CacheKeyTest extends TestCase\n{\n    public function testEntityCacheKeyIdentifierCollision(): void\n    {\n        $key1 = new EntityCacheKey('Foo', ['id' => 1]);\n        $key2 = new EntityCacheKey('Bar', ['id' => 1]);\n\n        self::assertNotEquals($key1->hash, $key2->hash);\n    }\n\n    public function testEntityCacheKeyIdentifierType(): void\n    {\n        $key1 = new EntityCacheKey('Foo', ['id' => 1]);\n        $key2 = new EntityCacheKey('Foo', ['id' => '1']);\n\n        self::assertEquals($key1->hash, $key2->hash);\n    }\n\n    public function testEntityCacheKeyIdentifierOrder(): void\n    {\n        $key1 = new EntityCacheKey('Foo', ['foo_bar' => 1, 'bar_foo' => 2]);\n        $key2 = new EntityCacheKey('Foo', ['bar_foo' => 2, 'foo_bar' => 1]);\n\n        self::assertEquals($key1->hash, $key2->hash);\n    }\n\n    public function testCollectionCacheKeyIdentifierType(): void\n    {\n        $key1 = new CollectionCacheKey('Foo', 'assoc', ['id' => 1]);\n        $key2 = new CollectionCacheKey('Foo', 'assoc', ['id' => '1']);\n\n        self::assertEquals($key1->hash, $key2->hash);\n    }\n\n    public function testCollectionCacheKeyIdentifierOrder(): void\n    {\n        $key1 = new CollectionCacheKey('Foo', 'assoc', ['foo_bar' => 1, 'bar_foo' => 2]);\n        $key2 = new CollectionCacheKey('Foo', 'assoc', ['bar_foo' => 2, 'foo_bar' => 1]);\n\n        self::assertEquals($key1->hash, $key2->hash);\n    }\n\n    public function testCollectionCacheKeyIdentifierCollision(): void\n    {\n        $key1 = new CollectionCacheKey('Foo', 'assoc', ['id' => 1]);\n        $key2 = new CollectionCacheKey('Bar', 'assoc', ['id' => 1]);\n\n        self::assertNotEquals($key1->hash, $key2->hash);\n    }\n\n    public function testCollectionCacheKeyAssociationCollision(): void\n    {\n        $key1 = new CollectionCacheKey('Foo', 'assoc1', ['id' => 1]);\n        $key2 = new CollectionCacheKey('Foo', 'assoc2', ['id' => 1]);\n\n        self::assertNotEquals($key1->hash, $key2->hash);\n    }\n\n    public function testConstructor(): void\n    {\n        $key = new class ('my-hash') extends CacheKey {\n        };\n\n        self::assertSame('my-hash', $key->hash);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/CacheLoggerChainTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLogger;\nuse Doctrine\\ORM\\Cache\\Logging\\CacheLoggerChain;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\n\n#[Group('DDC-2183')]\nclass CacheLoggerChainTest extends TestCase\n{\n    private CacheLoggerChain $logger;\n    private CacheLogger&MockObject $mock;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->logger = new CacheLoggerChain();\n        $this->mock   = $this->createMock(CacheLogger::class);\n    }\n\n    public function testGetAndSetLogger(): void\n    {\n        self::assertEmpty($this->logger->getLoggers());\n\n        self::assertNull($this->logger->getLogger('mock'));\n\n        $this->logger->setLogger('mock', $this->mock);\n\n        self::assertSame($this->mock, $this->logger->getLogger('mock'));\n        self::assertEquals(['mock' => $this->mock], $this->logger->getLoggers());\n    }\n\n    public function testEntityCacheChain(): void\n    {\n        $name = 'my_entity_region';\n        $key  = new EntityCacheKey(State::class, ['id' => 1]);\n\n        $this->logger->setLogger('mock', $this->mock);\n\n        $this->mock->expects(self::once())\n            ->method('entityCacheHit')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('entityCachePut')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('entityCacheMiss')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->logger->entityCacheHit($name, $key);\n        $this->logger->entityCachePut($name, $key);\n        $this->logger->entityCacheMiss($name, $key);\n    }\n\n    public function testCollectionCacheChain(): void\n    {\n        $name = 'my_collection_region';\n        $key  = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->logger->setLogger('mock', $this->mock);\n\n        $this->mock->expects(self::once())\n            ->method('collectionCacheHit')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('collectionCachePut')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('collectionCacheMiss')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->logger->collectionCacheHit($name, $key);\n        $this->logger->collectionCachePut($name, $key);\n        $this->logger->collectionCacheMiss($name, $key);\n    }\n\n    public function testQueryCacheChain(): void\n    {\n        $name = 'my_query_region';\n        $key  = new QueryCacheKey('my_query_hash');\n\n        $this->logger->setLogger('mock', $this->mock);\n\n        $this->mock->expects(self::once())\n            ->method('queryCacheHit')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('queryCachePut')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->mock->expects(self::once())\n            ->method('queryCacheMiss')\n            ->with(self::equalTo($name), self::equalTo($key));\n\n        $this->logger->queryCacheHit($name, $key);\n        $this->logger->queryCachePut($name, $key);\n        $this->logger->queryCacheMiss($name, $key);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultCacheFactoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CacheFactory;\nuse Doctrine\\ORM\\Cache\\DefaultCacheFactory;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region\\DefaultRegion;\nuse Doctrine\\ORM\\Cache\\RegionsConfiguration;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\Tests\\Mocks\\ConcurrentRegionMock;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionContactInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionLocationInfo;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmTestCase;\nuse InvalidArgumentException;\nuse LogicException;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\n#[Group('DDC-2183')]\nclass DefaultCacheFactoryTest extends OrmTestCase\n{\n    private CacheFactory&MockObject $factory;\n    private EntityManagerMock $em;\n    private RegionsConfiguration $regionsConfig;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->em            = $this->getTestEntityManager();\n        $this->regionsConfig = new RegionsConfiguration();\n        $arguments           = [$this->regionsConfig, $this->getSharedSecondLevelCache()];\n        $this->factory       = $this->getMockBuilder(DefaultCacheFactory::class)\n                                    ->onlyMethods(['getRegion'])\n                                    ->setConstructorArgs($arguments)\n                                    ->getMock();\n    }\n\n    public function testImplementsCacheFactory(): void\n    {\n        self::assertInstanceOf(CacheFactory::class, $this->factory);\n    }\n\n    public function testBuildCachedEntityPersisterReadOnly(): void\n    {\n        $em        = $this->em;\n        $metadata  = clone $em->getClassMetadata(State::class);\n        $persister = new BasicEntityPersister($em, $metadata);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $metadata->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_ONLY;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($metadata->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedEntityPersister($em, $persister, $metadata);\n\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister);\n        self::assertInstanceOf(ReadOnlyCachedEntityPersister::class, $cachedPersister);\n    }\n\n    public function testBuildCachedEntityPersisterReadWrite(): void\n    {\n        $em        = $this->em;\n        $metadata  = clone $em->getClassMetadata(State::class);\n        $persister = new BasicEntityPersister($em, $metadata);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $metadata->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_WRITE;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($metadata->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedEntityPersister($em, $persister, $metadata);\n\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister);\n        self::assertInstanceOf(ReadWriteCachedEntityPersister::class, $cachedPersister);\n    }\n\n    public function testBuildCachedEntityPersisterNonStrictReadWrite(): void\n    {\n        $em        = $this->em;\n        $metadata  = clone $em->getClassMetadata(State::class);\n        $persister = new BasicEntityPersister($em, $metadata);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $metadata->cache['usage'] = ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($metadata->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedEntityPersister($em, $persister, $metadata);\n\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister);\n        self::assertInstanceOf(NonStrictReadWriteCachedEntityPersister::class, $cachedPersister);\n    }\n\n    public function testBuildCachedCollectionPersisterReadOnly(): void\n    {\n        $em        = $this->em;\n        $metadata  = $em->getClassMetadata(State::class);\n        $mapping   = $metadata->associationMappings['cities'];\n        $persister = new OneToManyPersister($em);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_ONLY;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($mapping->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping);\n\n        self::assertInstanceOf(CachedCollectionPersister::class, $cachedPersister);\n        self::assertInstanceOf(ReadOnlyCachedCollectionPersister::class, $cachedPersister);\n    }\n\n    public function testBuildCachedCollectionPersisterReadWrite(): void\n    {\n        $em        = $this->em;\n        $metadata  = $em->getClassMetadata(State::class);\n        $mapping   = $metadata->associationMappings['cities'];\n        $persister = new OneToManyPersister($em);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_WRITE;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($mapping->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping);\n\n        self::assertInstanceOf(CachedCollectionPersister::class, $cachedPersister);\n        self::assertInstanceOf(ReadWriteCachedCollectionPersister::class, $cachedPersister);\n    }\n\n    public function testBuildCachedCollectionPersisterNonStrictReadWrite(): void\n    {\n        $em        = $this->em;\n        $metadata  = $em->getClassMetadata(State::class);\n        $mapping   = $metadata->associationMappings['cities'];\n        $persister = new OneToManyPersister($em);\n        $region    = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache()));\n\n        $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE;\n\n        $this->factory->expects(self::once())\n            ->method('getRegion')\n            ->with(self::equalTo($mapping->cache))\n            ->willReturn($region);\n\n        $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping);\n\n        self::assertInstanceOf(CachedCollectionPersister::class, $cachedPersister);\n        self::assertInstanceOf(NonStrictReadWriteCachedCollectionPersister::class, $cachedPersister);\n    }\n\n    public function testInheritedEntityCacheRegion(): void\n    {\n        $em         = $this->em;\n        $metadata1  = clone $em->getClassMetadata(AttractionContactInfo::class);\n        $metadata2  = clone $em->getClassMetadata(AttractionLocationInfo::class);\n        $persister1 = new BasicEntityPersister($em, $metadata1);\n        $persister2 = new BasicEntityPersister($em, $metadata2);\n        $factory    = new DefaultCacheFactory($this->regionsConfig, $this->getSharedSecondLevelCache());\n\n        $cachedPersister1 = $factory->buildCachedEntityPersister($em, $persister1, $metadata1);\n        $cachedPersister2 = $factory->buildCachedEntityPersister($em, $persister2, $metadata2);\n\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister1);\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister2);\n\n        self::assertNotSame($cachedPersister1, $cachedPersister2);\n        self::assertSame($cachedPersister1->getCacheRegion(), $cachedPersister2->getCacheRegion());\n    }\n\n    public function testCreateNewCacheDriver(): void\n    {\n        $em         = $this->em;\n        $metadata1  = clone $em->getClassMetadata(State::class);\n        $metadata2  = clone $em->getClassMetadata(City::class);\n        $persister1 = new BasicEntityPersister($em, $metadata1);\n        $persister2 = new BasicEntityPersister($em, $metadata2);\n        $factory    = new DefaultCacheFactory($this->regionsConfig, $this->getSharedSecondLevelCache());\n\n        $cachedPersister1 = $factory->buildCachedEntityPersister($em, $persister1, $metadata1);\n        $cachedPersister2 = $factory->buildCachedEntityPersister($em, $persister2, $metadata2);\n\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister1);\n        self::assertInstanceOf(CachedEntityPersister::class, $cachedPersister2);\n\n        self::assertNotSame($cachedPersister1, $cachedPersister2);\n        self::assertNotSame($cachedPersister1->getCacheRegion(), $cachedPersister2->getCacheRegion());\n    }\n\n    public function testBuildCachedEntityPersisterNonStrictException(): void\n    {\n        $em        = $this->em;\n        $metadata  = clone $em->getClassMetadata(State::class);\n        $persister = new BasicEntityPersister($em, $metadata);\n\n        $metadata->cache['usage'] = -1;\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Unrecognized access strategy type [-1]');\n\n        $this->factory->buildCachedEntityPersister($em, $persister, $metadata);\n    }\n\n    public function testBuildCachedCollectionPersisterException(): void\n    {\n        $em        = $this->em;\n        $metadata  = $em->getClassMetadata(State::class);\n        $mapping   = $metadata->associationMappings['cities'];\n        $persister = new OneToManyPersister($em);\n\n        $mapping->cache['usage'] = -1;\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Unrecognized access strategy type [-1]');\n\n        $this->factory->buildCachedCollectionPersister($em, $persister, $mapping);\n    }\n\n    public function testInvalidFileLockRegionDirectoryException(): void\n    {\n        $factory = new DefaultCacheFactory($this->regionsConfig, $this->getSharedSecondLevelCache());\n\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(\n            'If you want to use a \"READ_WRITE\" cache an implementation of \"Doctrine\\ORM\\Cache\\ConcurrentRegion\" '\n            . 'is required, The default implementation provided by doctrine is '\n            . '\"Doctrine\\ORM\\Cache\\Region\\FileLockRegion\" if you want to use it please provide a valid directory',\n        );\n\n        $factory->getRegion(\n            [\n                'usage'   => ClassMetadata::CACHE_USAGE_READ_WRITE,\n                'region'  => 'foo',\n            ],\n        );\n    }\n\n    public function testInvalidFileLockRegionDirectoryExceptionWithEmptyString(): void\n    {\n        $factory = new DefaultCacheFactory($this->regionsConfig, $this->getSharedSecondLevelCache());\n\n        $factory->setFileLockRegionDirectory('');\n\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(\n            'If you want to use a \"READ_WRITE\" cache an implementation of \"Doctrine\\ORM\\Cache\\ConcurrentRegion\" '\n            . 'is required, The default implementation provided by doctrine is '\n            . '\"Doctrine\\ORM\\Cache\\Region\\FileLockRegion\" if you want to use it please provide a valid directory',\n        );\n\n        $factory->getRegion(\n            [\n                'usage'   => ClassMetadata::CACHE_USAGE_READ_WRITE,\n                'region'  => 'foo',\n            ],\n        );\n    }\n\n    public function testBuildsDefaultCacheRegionFromGenericCacheRegion(): void\n    {\n        $factory = new DefaultCacheFactory($this->regionsConfig, $this->createMock(CacheItemPoolInterface::class));\n\n        self::assertInstanceOf(\n            DefaultRegion::class,\n            $factory->getRegion(\n                [\n                    'region' => 'bar',\n                    'usage'  => ClassMetadata::CACHE_USAGE_READ_ONLY,\n                ],\n            ),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\DefaultCache;\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\QueryCache;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionMethod;\nuse ReflectionProperty;\n\nuse function array_merge;\n\n#[Group('DDC-2183')]\nclass DefaultCacheTest extends OrmTestCase\n{\n    private DefaultCache $cache;\n    private EntityManagerMock $em;\n\n    protected function setUp(): void\n    {\n        parent::enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->em    = $this->getTestEntityManager();\n        $this->cache = new DefaultCache($this->em);\n    }\n\n    private function putEntityCacheEntry(string $className, array $identifier, array $data): void\n    {\n        $metadata   = $this->em->getClassMetadata($className);\n        $cacheKey   = new EntityCacheKey($metadata->name, $identifier);\n        $cacheEntry = new EntityCacheEntry($metadata->name, $data);\n        $persister  = $this->em->getUnitOfWork()->getEntityPersister($metadata->rootEntityName);\n\n        $persister->getCacheRegion()->put($cacheKey, $cacheEntry);\n    }\n\n    private function putCollectionCacheEntry(string $className, string $association, array $ownerIdentifier, array $data): void\n    {\n        $metadata   = $this->em->getClassMetadata($className);\n        $cacheKey   = new CollectionCacheKey($metadata->name, $association, $ownerIdentifier);\n        $cacheEntry = new CollectionCacheEntry($data);\n        $persister  = $this->em->getUnitOfWork()->getCollectionPersister($metadata->getAssociationMapping($association));\n\n        $persister->getCacheRegion()->put($cacheKey, $cacheEntry);\n    }\n\n    public function testImplementsCache(): void\n    {\n        self::assertInstanceOf(Cache::class, $this->cache);\n    }\n\n    public function testGetEntityCacheRegionAccess(): void\n    {\n        self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(State::class));\n        self::assertNull($this->cache->getEntityCacheRegion(CmsUser::class));\n    }\n\n    public function testGetCollectionCacheRegionAccess(): void\n    {\n        self::assertInstanceOf(Region::class, $this->cache->getCollectionCacheRegion(State::class, 'cities'));\n        self::assertNull($this->cache->getCollectionCacheRegion(CmsUser::class, 'phonenumbers'));\n    }\n\n    public function testContainsEntity(): void\n    {\n        $identifier = ['id' => 1];\n        $className  = Country::class;\n        $cacheEntry = array_merge($identifier, ['name' => 'Brazil']);\n\n        self::assertFalse($this->cache->containsEntity(Country::class, 1));\n\n        $this->putEntityCacheEntry($className, $identifier, $cacheEntry);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, 1));\n        self::assertFalse($this->cache->containsEntity(CmsUser::class, 1));\n    }\n\n    public function testEvictEntity(): void\n    {\n        $identifier = ['id' => 1];\n        $className  = Country::class;\n        $cacheEntry = array_merge($identifier, ['name' => 'Brazil']);\n\n        $this->putEntityCacheEntry($className, $identifier, $cacheEntry);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, 1));\n\n        $this->cache->evictEntity(Country::class, 1);\n        $this->cache->evictEntity(CmsUser::class, 1);\n\n        self::assertFalse($this->cache->containsEntity(Country::class, 1));\n    }\n\n    public function testEvictEntityRegion(): void\n    {\n        $identifier = ['id' => 1];\n        $className  = Country::class;\n        $cacheEntry = array_merge($identifier, ['name' => 'Brazil']);\n\n        $this->putEntityCacheEntry($className, $identifier, $cacheEntry);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, 1));\n\n        $this->cache->evictEntityRegion(Country::class);\n        $this->cache->evictEntityRegion(CmsUser::class);\n\n        self::assertFalse($this->cache->containsEntity(Country::class, 1));\n    }\n\n    public function testEvictEntityRegions(): void\n    {\n        $identifier = ['id' => 1];\n        $className  = Country::class;\n        $cacheEntry = array_merge($identifier, ['name' => 'Brazil']);\n\n        $this->putEntityCacheEntry($className, $identifier, $cacheEntry);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, 1));\n\n        $this->cache->evictEntityRegions();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, 1));\n    }\n\n    public function testContainsCollection(): void\n    {\n        $ownerId     = ['id' => 1];\n        $className   = State::class;\n        $association = 'cities';\n        $cacheEntry  = [\n            ['id' => 11],\n            ['id' => 12],\n        ];\n\n        self::assertFalse($this->cache->containsCollection(State::class, $association, 1));\n\n        $this->putCollectionCacheEntry($className, $association, $ownerId, $cacheEntry);\n\n        self::assertTrue($this->cache->containsCollection(State::class, $association, 1));\n        self::assertFalse($this->cache->containsCollection(CmsUser::class, 'phonenumbers', 1));\n    }\n\n    public function testEvictCollection(): void\n    {\n        $ownerId     = ['id' => 1];\n        $className   = State::class;\n        $association = 'cities';\n        $cacheEntry  = [\n            ['id' => 11],\n            ['id' => 12],\n        ];\n\n        $this->putCollectionCacheEntry($className, $association, $ownerId, $cacheEntry);\n\n        self::assertTrue($this->cache->containsCollection(State::class, $association, 1));\n\n        $this->cache->evictCollection($className, $association, $ownerId);\n        $this->cache->evictCollection(CmsUser::class, 'phonenumbers', 1);\n\n        self::assertFalse($this->cache->containsCollection(State::class, $association, 1));\n    }\n\n    public function testEvictCollectionRegion(): void\n    {\n        $ownerId     = ['id' => 1];\n        $className   = State::class;\n        $association = 'cities';\n        $cacheEntry  = [\n            ['id' => 11],\n            ['id' => 12],\n        ];\n\n        $this->putCollectionCacheEntry($className, $association, $ownerId, $cacheEntry);\n\n        self::assertTrue($this->cache->containsCollection(State::class, $association, 1));\n\n        $this->cache->evictCollectionRegion($className, $association);\n        $this->cache->evictCollectionRegion(CmsUser::class, 'phonenumbers');\n\n        self::assertFalse($this->cache->containsCollection(State::class, $association, 1));\n    }\n\n    public function testEvictCollectionRegions(): void\n    {\n        $ownerId     = ['id' => 1];\n        $className   = State::class;\n        $association = 'cities';\n        $cacheEntry  = [\n            ['id' => 11],\n            ['id' => 12],\n        ];\n\n        $this->putCollectionCacheEntry($className, $association, $ownerId, $cacheEntry);\n\n        self::assertTrue($this->cache->containsCollection(State::class, $association, 1));\n\n        $this->cache->evictCollectionRegions();\n\n        self::assertFalse($this->cache->containsCollection(State::class, $association, 1));\n    }\n\n    public function testQueryCache(): void\n    {\n        self::assertFalse($this->cache->containsQuery('foo'));\n\n        $defaultQueryCache = $this->cache->getQueryCache();\n        $fooQueryCache     = $this->cache->getQueryCache('foo');\n\n        self::assertInstanceOf(QueryCache::class, $defaultQueryCache);\n        self::assertInstanceOf(QueryCache::class, $fooQueryCache);\n        self::assertSame($defaultQueryCache, $this->cache->getQueryCache());\n        self::assertSame($fooQueryCache, $this->cache->getQueryCache('foo'));\n\n        $this->cache->evictQueryRegion();\n        $this->cache->evictQueryRegion('foo');\n        $this->cache->evictQueryRegions();\n\n        self::assertTrue($this->cache->containsQuery('foo'));\n\n        self::assertSame($defaultQueryCache, $this->cache->getQueryCache());\n        self::assertSame($fooQueryCache, $this->cache->getQueryCache('foo'));\n    }\n\n    public function testToIdentifierArrayShouldLookupForEntityIdentifier(): void\n    {\n        $identifier = 123;\n        $entity     = new Country('Foo');\n        $metadata   = $this->em->getClassMetadata(Country::class);\n        $method     = new ReflectionMethod($this->cache, 'toIdentifierArray');\n        $property   = new ReflectionProperty($entity, 'id');\n\n        $property->setValue($entity, $identifier);\n\n        self::assertEquals(['id' => $identifier], $method->invoke($this->cache, $metadata, $identifier));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultCollectionHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\DefaultCollectionHydrator;\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass DefaultCollectionHydratorTest extends OrmFunctionalTestCase\n{\n    private DefaultCollectionHydrator $structure;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->structure = new DefaultCollectionHydrator($this->_em);\n    }\n\n    public function testImplementsCollectionEntryStructure(): void\n    {\n        self::assertInstanceOf(DefaultCollectionHydrator::class, $this->structure);\n    }\n\n    public function testLoadCacheCollection(): void\n    {\n        $targetRegion = $this->_em->getCache()->getEntityCacheRegion(City::class);\n        $entry        = new CollectionCacheEntry(\n            [\n                new EntityCacheKey(City::class, ['id' => 31]),\n                new EntityCacheKey(City::class, ['id' => 32]),\n            ],\n        );\n\n        $targetRegion->put(new EntityCacheKey(City::class, ['id' => 31]), new EntityCacheEntry(City::class, ['id' => 31, 'name' => 'Foo']));\n        $targetRegion->put(new EntityCacheKey(City::class, ['id' => 32]), new EntityCacheEntry(City::class, ['id' => 32, 'name' => 'Bar']));\n\n        $sourceClass = $this->_em->getClassMetadata(State::class);\n        $targetClass = $this->_em->getClassMetadata(City::class);\n        $key         = new CollectionCacheKey($sourceClass->name, 'cities', ['id' => 21]);\n        $collection  = new PersistentCollection($this->_em, $targetClass, new ArrayCollection());\n        $list        = $this->structure->loadCacheEntry($sourceClass, $key, $entry, $collection);\n\n        self::assertNotNull($list);\n        self::assertCount(2, $list);\n        self::assertCount(2, $collection);\n\n        self::assertInstanceOf($targetClass->name, $list[0]);\n        self::assertInstanceOf($targetClass->name, $list[1]);\n        self::assertInstanceOf($targetClass->name, $collection[0]);\n        self::assertInstanceOf($targetClass->name, $collection[1]);\n\n        self::assertSame($list[0], $collection[0]);\n        self::assertSame($list[1], $collection[1]);\n\n        self::assertEquals(31, $list[0]->getId());\n        self::assertEquals(32, $list[1]->getId());\n        self::assertEquals($list[0]->getId(), $collection[0]->getId());\n        self::assertEquals($list[1]->getId(), $collection[1]->getId());\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($collection[0]));\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($collection[1]));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultEntityHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\AssociationCacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\DefaultEntityHydrator;\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityHydrator;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass DefaultEntityHydratorTest extends OrmTestCase\n{\n    private DefaultEntityHydrator $structure;\n    private EntityManagerMock $em;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->em        = $this->getTestEntityManager();\n        $this->structure = new DefaultEntityHydrator($this->em);\n    }\n\n    public function testImplementsEntityEntryStructure(): void\n    {\n        self::assertInstanceOf(EntityHydrator::class, $this->structure);\n    }\n\n    public function testCreateEntity(): void\n    {\n        $metadata = $this->em->getClassMetadata(Country::class);\n        $key      = new EntityCacheKey($metadata->name, ['id' => 1]);\n        $entry    = new EntityCacheEntry($metadata->name, ['id' => 1, 'name' => 'Foo']);\n        $entity   = $this->structure->loadCacheEntry($metadata, $key, $entry);\n\n        self::assertInstanceOf($metadata->name, $entity);\n\n        self::assertEquals(1, $entity->getId());\n        self::assertEquals('Foo', $entity->getName());\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->em->getUnitOfWork()->getEntityState($entity));\n    }\n\n    public function testLoadProxy(): void\n    {\n        $metadata = $this->em->getClassMetadata(Country::class);\n        $key      = new EntityCacheKey($metadata->name, ['id' => 1]);\n        $entry    = new EntityCacheEntry($metadata->name, ['id' => 1, 'name' => 'Foo']);\n        $proxy    = $this->em->getReference($metadata->name, $key->identifier);\n        $entity   = $this->structure->loadCacheEntry($metadata, $key, $entry, $proxy);\n\n        self::assertInstanceOf($metadata->name, $entity);\n        self::assertSame($proxy, $entity);\n\n        self::assertEquals(1, $entity->getId());\n        self::assertEquals('Foo', $entity->getName());\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->em->getUnitOfWork()->getEntityState($proxy));\n    }\n\n    public function testBuildCacheEntry(): void\n    {\n        $entity   = new Country('Foo');\n        $uow      = $this->em->getUnitOfWork();\n        $data     = ['id' => 1, 'name' => 'Foo'];\n        $metadata = $this->em->getClassMetadata(Country::class);\n        $key      = new EntityCacheKey($metadata->name, ['id' => 1]);\n\n        $entity->setId(1);\n        $uow->registerManaged($entity, $key->identifier, $data);\n\n        $cache = $this->structure->buildCacheEntry($metadata, $key, $entity);\n\n        self::assertInstanceOf(CacheEntry::class, $cache);\n        self::assertInstanceOf(EntityCacheEntry::class, $cache);\n\n        self::assertArrayHasKey('id', $cache->data);\n        self::assertArrayHasKey('name', $cache->data);\n        self::assertEquals(\n            [\n                'id'   => 1,\n                'name' => 'Foo',\n            ],\n            $cache->data,\n        );\n    }\n\n    public function testBuildCacheEntryAssociation(): void\n    {\n        $country     = new Country('Foo');\n        $state       = new State('Bat', $country);\n        $uow         = $this->em->getUnitOfWork();\n        $countryData = ['id' => 11, 'name' => 'Foo'];\n        $stateData   = ['id' => 12, 'name' => 'Bar', 'country' => $country];\n        $metadata    = $this->em->getClassMetadata(State::class);\n        $key         = new EntityCacheKey($metadata->name, ['id' => 11]);\n\n        $country->setId(11);\n        $state->setId(12);\n\n        $uow->registerManaged($country, ['id' => 11], $countryData);\n        $uow->registerManaged($state, ['id' => 12], $stateData);\n\n        $cache = $this->structure->buildCacheEntry($metadata, $key, $state);\n\n        self::assertInstanceOf(CacheEntry::class, $cache);\n        self::assertInstanceOf(EntityCacheEntry::class, $cache);\n\n        self::assertArrayHasKey('id', $cache->data);\n        self::assertArrayHasKey('name', $cache->data);\n        self::assertArrayHasKey('country', $cache->data);\n        self::assertEquals(\n            [\n                'id'        => 12,\n                'name'      => 'Bar',\n                'country'   => new AssociationCacheEntry(Country::class, ['id' => 11]),\n            ],\n            $cache->data,\n        );\n    }\n\n    public function testBuildCacheEntryNonInitializedAssocProxy(): void\n    {\n        $proxy      = $this->em->getReference(Country::class, 11);\n        $entity     = new State('Bat', $proxy);\n        $uow        = $this->em->getUnitOfWork();\n        $entityData = ['id' => 12, 'name' => 'Bar', 'country' => $proxy];\n        $metadata   = $this->em->getClassMetadata(State::class);\n        $key        = new EntityCacheKey($metadata->name, ['id' => 11]);\n\n        $entity->setId(12);\n\n        $uow->registerManaged($entity, ['id' => 12], $entityData);\n\n        $cache = $this->structure->buildCacheEntry($metadata, $key, $entity);\n\n        self::assertInstanceOf(CacheEntry::class, $cache);\n        self::assertInstanceOf(EntityCacheEntry::class, $cache);\n\n        self::assertArrayHasKey('id', $cache->data);\n        self::assertArrayHasKey('name', $cache->data);\n        self::assertArrayHasKey('country', $cache->data);\n        self::assertEquals(\n            [\n                'id'        => 12,\n                'name'      => 'Bar',\n                'country'   => new AssociationCacheEntry(Country::class, ['id' => 11]),\n            ],\n            $cache->data,\n        );\n    }\n\n    public function testCacheEntryWithWrongIdentifierType(): void\n    {\n        $proxy      = $this->em->getReference(Country::class, 11);\n        $entity     = new State('Bat', $proxy);\n        $uow        = $this->em->getUnitOfWork();\n        $entityData = ['id' => 12, 'name' => 'Bar', 'country' => $proxy];\n        $metadata   = $this->em->getClassMetadata(State::class);\n        $key        = new EntityCacheKey($metadata->name, ['id' => '12']);\n\n        $entity->setId(12);\n\n        $uow->registerManaged($entity, ['id' => 12], $entityData);\n\n        $cache = $this->structure->buildCacheEntry($metadata, $key, $entity);\n\n        self::assertInstanceOf(CacheEntry::class, $cache);\n        self::assertInstanceOf(EntityCacheEntry::class, $cache);\n\n        self::assertArrayHasKey('id', $cache->data);\n        self::assertArrayHasKey('name', $cache->data);\n        self::assertArrayHasKey('country', $cache->data);\n        self::assertSame($entity->getId(), $cache->data['id']);\n        self::assertEquals(\n            [\n                'id'        => 12,\n                'name'      => 'Bar',\n                'country'   => new AssociationCacheEntry(Country::class, ['id' => 11]),\n            ],\n            $cache->data,\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultQueryCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\DefaultCacheFactory;\nuse Doctrine\\ORM\\Cache\\DefaultQueryCache;\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\nuse Doctrine\\ORM\\Cache\\QueryCache;\nuse Doctrine\\ORM\\Cache\\QueryCacheEntry;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\Tests\\Mocks\\CacheEntryMock;\nuse Doctrine\\Tests\\Mocks\\CacheRegionMock;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Mocks\\TimestampRegionMock;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\Restaurant;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionMethod;\n\nuse function microtime;\nuse function sprintf;\n\n#[Group('DDC-2183')]\nclass DefaultQueryCacheTest extends OrmTestCase\n{\n    private DefaultQueryCache $queryCache;\n    private EntityManagerMock $em;\n    private CacheRegionMock $region;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->enableSecondLevelCache();\n\n        $this->em         = $this->getTestEntityManager();\n        $this->region     = new CacheRegionMock();\n        $this->queryCache = new DefaultQueryCache($this->em, $this->region);\n        $cacheFactory     = new CacheFactoryDefaultQueryCacheTest($this->queryCache, $this->region);\n\n        $this->em->getConfiguration()\n            ->getSecondLevelCacheConfiguration()\n            ->setCacheFactory($cacheFactory);\n    }\n\n    public function testImplementQueryCache(): void\n    {\n        self::assertInstanceOf(QueryCache::class, $this->queryCache);\n    }\n\n    public function testGetRegion(): void\n    {\n        self::assertSame($this->region, $this->queryCache->getRegion());\n    }\n\n    public function testClearShouldEvictRegion(): void\n    {\n        $this->queryCache->clear();\n\n        self::assertArrayHasKey('evictAll', $this->region->calls);\n        self::assertCount(1, $this->region->calls['evictAll']);\n    }\n\n    public function testPutBasicQueryResult(): void\n    {\n        $result   = [];\n        $key      = new QueryCacheKey('query.key1', 0);\n        $rsm      = new ResultSetMappingBuilder($this->em);\n        $metadata = $this->em->getClassMetadata(Country::class);\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        for ($i = 0; $i < 4; $i++) {\n            $name     = 'Country ' . $i;\n            $entity   = new Country($name);\n            $result[] = $entity;\n\n            $metadata->setFieldValue($entity, 'id', $i);\n            $this->em->getUnitOfWork()->registerManaged($entity, ['id' => $i], ['name' => $name]);\n        }\n\n        self::assertTrue($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(5, $this->region->calls['put']);\n\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][0]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][1]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][2]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][3]['key']);\n        self::assertInstanceOf(QueryCacheKey::class, $this->region->calls['put'][4]['key']);\n\n        self::assertInstanceOf(EntityCacheEntry::class, $this->region->calls['put'][0]['entry']);\n        self::assertInstanceOf(EntityCacheEntry::class, $this->region->calls['put'][1]['entry']);\n        self::assertInstanceOf(EntityCacheEntry::class, $this->region->calls['put'][2]['entry']);\n        self::assertInstanceOf(EntityCacheEntry::class, $this->region->calls['put'][3]['entry']);\n        self::assertInstanceOf(QueryCacheEntry::class, $this->region->calls['put'][4]['entry']);\n    }\n\n    public function testPutToOneAssociationQueryResult(): void\n    {\n        $result     = [];\n        $uow        = $this->em->getUnitOfWork();\n        $key        = new QueryCacheKey('query.key1', 0);\n        $rsm        = new ResultSetMappingBuilder($this->em);\n        $cityClass  = $this->em->getClassMetadata(City::class);\n        $stateClass = $this->em->getClassMetadata(State::class);\n\n        $rsm->addRootEntityFromClassMetadata(City::class, 'c');\n        $rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id' => 'state_id', 'name' => 'state_name']);\n\n        for ($i = 0; $i < 4; $i++) {\n            $state    = new State(sprintf('State %d', $i));\n            $city     = new City(sprintf('City %d', $i), $state);\n            $result[] = $city;\n\n            $cityClass->setFieldValue($city, 'id', $i);\n            $stateClass->setFieldValue($state, 'id', $i * 2);\n\n            $uow->registerManaged($state, ['id' => $state->getId()], ['name' => $city->getName()]);\n            $uow->registerManaged($city, ['id' => $city->getId()], ['name' => $city->getName(), 'state' => $state]);\n        }\n\n        self::assertTrue($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(9, $this->region->calls['put']);\n\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][0]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][1]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][2]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][3]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][4]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][5]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][6]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][7]['key']);\n        self::assertInstanceOf(QueryCacheKey::class, $this->region->calls['put'][8]['key']);\n    }\n\n    public function testPutToOneAssociation2LevelsQueryResult(): void\n    {\n        $result       = [];\n        $uow          = $this->em->getUnitOfWork();\n        $key          = new QueryCacheKey('query.key1', 0);\n        $rsm          = new ResultSetMappingBuilder($this->em);\n        $cityClass    = $this->em->getClassMetadata(City::class);\n        $stateClass   = $this->em->getClassMetadata(State::class);\n        $countryClass = $this->em->getClassMetadata(Country::class);\n\n        $rsm->addRootEntityFromClassMetadata(City::class, 'c');\n        $rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id' => 'state_id', 'name' => 'state_name']);\n        $rsm->addJoinedEntityFromClassMetadata(Country::class, 'co', 's', 'country', ['id' => 'country_id', 'name' => 'country_name']);\n\n        for ($i = 0; $i < 4; $i++) {\n            $country = new Country('Country ' . $i);\n            $state   = new State('State ' . $i, $country);\n            $city    = new City('City ' . $i, $state);\n\n            $result[] = $city;\n\n            $cityClass->setFieldValue($city, 'id', $i);\n            $stateClass->setFieldValue($state, 'id', $i * 2);\n            $countryClass->setFieldValue($country, 'id', $i * 3);\n\n            $uow->registerManaged($country, ['id' => $country->getId()], ['name' => $country->getName()]);\n            $uow->registerManaged($state, ['id' => $state->getId()], ['name' => $state->getName(), 'country' => $country]);\n            $uow->registerManaged($city, ['id' => $city->getId()], ['name' => $city->getName(), 'state' => $state]);\n        }\n\n        self::assertTrue($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(13, $this->region->calls['put']);\n\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][0]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][1]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][2]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][3]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][4]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][5]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][6]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][7]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][8]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][9]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][10]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][11]['key']);\n        self::assertInstanceOf(QueryCacheKey::class, $this->region->calls['put'][12]['key']);\n    }\n\n    public function testPutToOneAssociationNullQueryResult(): void\n    {\n        $result    = [];\n        $uow       = $this->em->getUnitOfWork();\n        $key       = new QueryCacheKey('query.key1', 0);\n        $rsm       = new ResultSetMappingBuilder($this->em);\n        $cityClass = $this->em->getClassMetadata(City::class);\n\n        $rsm->addRootEntityFromClassMetadata(City::class, 'c');\n        $rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id' => 'state_id', 'name' => 'state_name']);\n\n        for ($i = 0; $i < 4; $i++) {\n            $city     = new City(sprintf('City %d', $i), null);\n            $result[] = $city;\n\n            $cityClass->setFieldValue($city, 'id', $i);\n\n            $uow->registerManaged($city, ['id' => $city->getId()], ['name' => $city->getName(), 'state' => null]);\n        }\n\n        self::assertTrue($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(5, $this->region->calls['put']);\n\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][0]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][1]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][2]['key']);\n        self::assertInstanceOf(EntityCacheKey::class, $this->region->calls['put'][3]['key']);\n        self::assertInstanceOf(QueryCacheKey::class, $this->region->calls['put'][4]['key']);\n    }\n\n    public function testPutToManyAssociationQueryResult(): void\n    {\n        $result     = [];\n        $uow        = $this->em->getUnitOfWork();\n        $key        = new QueryCacheKey('query.key1', 0);\n        $rsm        = new ResultSetMappingBuilder($this->em);\n        $cityClass  = $this->em->getClassMetadata(City::class);\n        $stateClass = $this->em->getClassMetadata(State::class);\n\n        $rsm->addRootEntityFromClassMetadata(State::class, 's');\n        $rsm->addJoinedEntityFromClassMetadata(City::class, 'c', 's', 'cities', ['id' => 'c_id', 'name' => 'c_name']);\n\n        for ($i = 0; $i < 4; $i++) {\n            $state    = new State(sprintf('State %d', $i));\n            $city1    = new City('City 1', $state);\n            $city2    = new City('City 2', $state);\n            $result[] = $state;\n\n            $cityClass->setFieldValue($city1, 'id', $i + 11);\n            $cityClass->setFieldValue($city2, 'id', $i + 22);\n            $stateClass->setFieldValue($state, 'id', $i);\n\n            $state->addCity($city1);\n            $state->addCity($city2);\n\n            $uow->registerManaged($city1, ['id' => $city1->getId()], ['name' => $city1->getName(), 'state' => $state]);\n            $uow->registerManaged($city2, ['id' => $city2->getId()], ['name' => $city2->getName(), 'state' => $state]);\n            $uow->registerManaged($state, ['id' => $state->getId()], ['name' => $state->getName(), 'cities' => $state->getCities()]);\n        }\n\n        self::assertTrue($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(13, $this->region->calls['put']);\n    }\n\n    public function testGetBasicQueryResult(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0);\n        $entry = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n        );\n\n        $data = [\n            ['id' => 1, 'name' => 'Foo'],\n            ['id' => 2, 'name' => 'Bar'],\n        ];\n\n        $this->region->addReturn('get', $entry);\n\n        $this->region->addReturn(\n            'getMultiple',\n            [\n                new EntityCacheEntry(Country::class, $data[0]),\n                new EntityCacheEntry(Country::class, $data[1]),\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        $result = $this->queryCache->get($key, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertInstanceOf(Country::class, $result[0]);\n        self::assertInstanceOf(Country::class, $result[1]);\n        self::assertEquals(1, $result[0]->getId());\n        self::assertEquals(2, $result[1]->getId());\n        self::assertEquals('Foo', $result[0]->getName());\n        self::assertEquals('Bar', $result[1]->getName());\n    }\n\n    public function testGetWithAssociation(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0);\n        $entry = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n        );\n\n        $data = [\n            ['id' => 1, 'name' => 'Foo'],\n            ['id' => 2, 'name' => 'Bar'],\n        ];\n\n        $this->region->addReturn('get', $entry);\n\n        $this->region->addReturn(\n            'getMultiple',\n            [\n                new EntityCacheEntry(Country::class, $data[0]),\n                new EntityCacheEntry(Country::class, $data[1]),\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        $result = $this->queryCache->get($key, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertInstanceOf(Country::class, $result[0]);\n        self::assertInstanceOf(Country::class, $result[1]);\n        self::assertEquals(1, $result[0]->getId());\n        self::assertEquals(2, $result[1]->getId());\n        self::assertEquals('Foo', $result[0]->getName());\n        self::assertEquals('Bar', $result[1]->getName());\n    }\n\n    public function testGetWithAssociationCacheMiss(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0);\n        $entry = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n        );\n\n        $this->region->addReturn('get', $entry);\n\n        $this->region->addReturn(\n            'getMultiple',\n            [\n                new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']),\n                false,\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        $result = $this->queryCache->get($key, $rsm);\n\n        self::assertNull($result);\n    }\n\n    public function testCancelPutResultIfEntityPutFails(): void\n    {\n        $result   = [];\n        $key      = new QueryCacheKey('query.key1', 0);\n        $rsm      = new ResultSetMappingBuilder($this->em);\n        $metadata = $this->em->getClassMetadata(Country::class);\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        for ($i = 0; $i < 4; $i++) {\n            $name     = 'Country ' . $i;\n            $entity   = new Country($name);\n            $result[] = $entity;\n\n            $metadata->setFieldValue($entity, 'id', $i);\n            $this->em->getUnitOfWork()->registerManaged($entity, ['id' => $i], ['name' => $name]);\n        }\n\n        $this->region->addReturn('put', false);\n\n        self::assertFalse($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(1, $this->region->calls['put']);\n    }\n\n    public function testCancelPutResultIfAssociationEntityPutFails(): void\n    {\n        $result     = [];\n        $uow        = $this->em->getUnitOfWork();\n        $key        = new QueryCacheKey('query.key1', 0);\n        $rsm        = new ResultSetMappingBuilder($this->em);\n        $cityClass  = $this->em->getClassMetadata(City::class);\n        $stateClass = $this->em->getClassMetadata(State::class);\n\n        $rsm->addRootEntityFromClassMetadata(City::class, 'c');\n        $rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id' => 'state_id', 'name' => 'state_name']);\n\n        $state    = new State('State 1');\n        $city     = new City('City 2', $state);\n        $result[] = $city;\n\n        $cityClass->setFieldValue($city, 'id', 1);\n        $stateClass->setFieldValue($state, 'id', 11);\n\n        $uow->registerManaged($state, ['id' => $state->getId()], ['name' => $city->getName()]);\n        $uow->registerManaged($city, ['id' => $city->getId()], ['name' => $city->getName(), 'state' => $state]);\n\n        $this->region->addReturn('put', true);  // put root entity\n        $this->region->addReturn('put', false); // association fails\n\n        self::assertFalse($this->queryCache->put($key, $rsm, $result));\n    }\n\n    public function testCancelPutToManyAssociationQueryResult(): void\n    {\n        $result     = [];\n        $uow        = $this->em->getUnitOfWork();\n        $key        = new QueryCacheKey('query.key1', 0);\n        $rsm        = new ResultSetMappingBuilder($this->em);\n        $cityClass  = $this->em->getClassMetadata(City::class);\n        $stateClass = $this->em->getClassMetadata(State::class);\n\n        $rsm->addRootEntityFromClassMetadata(State::class, 's');\n        $rsm->addJoinedEntityFromClassMetadata(City::class, 'c', 's', 'cities', ['id' => 'c_id', 'name' => 'c_name']);\n\n        $state    = new State('State');\n        $city1    = new City('City 1', $state);\n        $city2    = new City('City 2', $state);\n        $result[] = $state;\n\n        $stateClass->setFieldValue($state, 'id', 1);\n        $cityClass->setFieldValue($city1, 'id', 11);\n        $cityClass->setFieldValue($city2, 'id', 22);\n\n        $state->addCity($city1);\n        $state->addCity($city2);\n\n        $uow->registerManaged($city1, ['id' => $city1->getId()], ['name' => $city1->getName(), 'state' => $state]);\n        $uow->registerManaged($city2, ['id' => $city2->getId()], ['name' => $city2->getName(), 'state' => $state]);\n        $uow->registerManaged($state, ['id' => $state->getId()], ['name' => $state->getName(), 'cities' => $state->getCities()]);\n\n        $this->region->addReturn('put', true);  // put root entity\n        $this->region->addReturn('put', false); // collection association fails\n\n        self::assertFalse($this->queryCache->put($key, $rsm, $result));\n        self::assertArrayHasKey('put', $this->region->calls);\n        self::assertCount(2, $this->region->calls['put']);\n    }\n\n    public function testIgnoreCacheNonGetMode(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0, Cache::MODE_PUT);\n        $entry = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        $this->region->addReturn('get', $entry);\n\n        self::assertNull($this->queryCache->get($key, $rsm));\n    }\n\n    public function testIgnoreCacheNonPutMode(): void\n    {\n        $result   = [];\n        $rsm      = new ResultSetMappingBuilder($this->em);\n        $metadata = $this->em->getClassMetadata(Country::class);\n        $key      = new QueryCacheKey('query.key1', 0, Cache::MODE_GET);\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        for ($i = 0; $i < 4; $i++) {\n            $name     = 'Country ' . $i;\n            $entity   = new Country($name);\n            $result[] = $entity;\n\n            $metadata->setFieldValue($entity, 'id', $i);\n            $this->em->getUnitOfWork()->registerManaged($entity, ['id' => $i], ['name' => $name]);\n        }\n\n        self::assertFalse($this->queryCache->put($key, $rsm, $result));\n    }\n\n    public function testGetShouldIgnoreOldQueryCacheEntryResult(): void\n    {\n        $rsm      = new ResultSetMappingBuilder($this->em);\n        $key      = new QueryCacheKey('query.key1', 50);\n        $entry    = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n            microtime(true) - 100,\n        );\n        $entities = [\n            ['id' => 1, 'name' => 'Foo'],\n            ['id' => 2, 'name' => 'Bar'],\n        ];\n\n        $this->region->addReturn('get', $entry);\n\n        $this->region->addReturn(\n            'getMultiple',\n            [\n                new EntityCacheEntry(Country::class, $entities[0]),\n                new EntityCacheEntry(Country::class, $entities[1]),\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        self::assertNull($this->queryCache->get($key, $rsm));\n    }\n\n    public function testGetShouldIgnoreNonQueryCacheEntryResult(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0);\n        $entry = new CacheEntryMock([\n            ['identifier' => ['id' => 1]],\n            ['identifier' => ['id' => 2]],\n        ]);\n\n        $data = [\n            ['id' => 1, 'name' => 'Foo'],\n            ['id' => 2, 'name' => 'Bar'],\n        ];\n\n        $this->region->addReturn('get', $entry);\n\n        $this->region->addReturn(\n            'getMultiple',\n            [\n                new EntityCacheEntry(Country::class, $data[0]),\n                new EntityCacheEntry(Country::class, $data[1]),\n            ],\n        );\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        self::assertNull($this->queryCache->get($key, $rsm));\n    }\n\n    public function testGetShouldIgnoreMissingEntityQueryCacheEntry(): void\n    {\n        $rsm   = new ResultSetMappingBuilder($this->em);\n        $key   = new QueryCacheKey('query.key1', 0);\n        $entry = new QueryCacheEntry(\n            [\n                ['identifier' => ['id' => 1]],\n                ['identifier' => ['id' => 2]],\n            ],\n        );\n\n        $this->region->addReturn('get', $entry);\n        $this->region->addReturn('getMultiple', [null]);\n\n        $rsm->addRootEntityFromClassMetadata(Country::class, 'c');\n\n        self::assertNull($this->queryCache->get($key, $rsm));\n    }\n\n    public function testGetAssociationValue(): void\n    {\n        $reflection = new ReflectionMethod($this->queryCache, 'getAssociationValue');\n        $rsm        = new ResultSetMappingBuilder($this->em);\n        $key        = new QueryCacheKey('query.key1', 0);\n\n        $germany  = new Country('Germany');\n        $bavaria  = new State('Bavaria', $germany);\n        $wurzburg = new City('Würzburg', $bavaria);\n        $munich   = new City('Munich', $bavaria);\n\n        $bavaria->addCity($munich);\n        $bavaria->addCity($wurzburg);\n\n        $munich->addAttraction(new Restaurant('Reinstoff', $munich));\n        $munich->addAttraction(new Restaurant('Schneider Weisse', $munich));\n        $wurzburg->addAttraction(new Restaurant('Fischers Fritz', $wurzburg));\n\n        $rsm->addRootEntityFromClassMetadata(State::class, 's');\n        $rsm->addJoinedEntityFromClassMetadata(City::class, 'c', 's', 'cities', [\n            'id'   => 'c_id',\n            'name' => 'c_name',\n        ]);\n        $rsm->addJoinedEntityFromClassMetadata(Restaurant::class, 'a', 'c', 'attractions', [\n            'id'   => 'a_id',\n            'name' => 'a_name',\n        ]);\n\n        $cities      = $reflection->invoke($this->queryCache, $rsm, 'c', $bavaria);\n        $attractions = $reflection->invoke($this->queryCache, $rsm, 'a', $bavaria);\n\n        self::assertCount(2, $cities);\n        self::assertCount(2, $attractions);\n\n        self::assertInstanceOf(Collection::class, $cities);\n        self::assertInstanceOf(Collection::class, $attractions[0]);\n        self::assertInstanceOf(Collection::class, $attractions[1]);\n\n        self::assertCount(2, $attractions[0]);\n        self::assertCount(1, $attractions[1]);\n    }\n\n    public function testScalarResultException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second level cache does not support scalar results.');\n        $result = [];\n        $key    = new QueryCacheKey('query.key1', 0);\n        $rsm    = new ResultSetMappingBuilder($this->em);\n\n        $rsm->addScalarResult('id', 'u', 'integer');\n\n        $this->queryCache->put($key, $rsm, $result);\n    }\n\n    public function testSupportMultipleRootEntitiesException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second level cache does not support multiple root entities.');\n        $result = [];\n        $key    = new QueryCacheKey('query.key1', 0);\n        $rsm    = new ResultSetMappingBuilder($this->em);\n\n        $rsm->addEntityResult(City::class, 'e1');\n        $rsm->addEntityResult(State::class, 'e2');\n\n        $this->queryCache->put($key, $rsm, $result);\n    }\n\n    public function testNotCacheableEntityException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Entity \"Doctrine\\Tests\\Models\\Generic\\BooleanModel\" not configured as part of the second-level cache.');\n        $result = [];\n        $key    = new QueryCacheKey('query.key1', 0);\n        $rsm    = new ResultSetMappingBuilder($this->em);\n        $rsm->addRootEntityFromClassMetadata(BooleanModel::class, 'c');\n\n        for ($i = 0; $i < 4; $i++) {\n            $entity  = new BooleanModel();\n            $boolean = ($i % 2 === 0);\n\n            $entity->id           = $i;\n            $entity->booleanField = $boolean;\n            $result[]             = $entity;\n\n            $this->em->getUnitOfWork()->registerManaged($entity, ['id' => $i], ['booleanField' => $boolean]);\n        }\n\n        self::assertFalse($this->queryCache->put($key, $rsm, $result));\n    }\n}\n\nclass CacheFactoryDefaultQueryCacheTest extends DefaultCacheFactory\n{\n    public function __construct(\n        private DefaultQueryCache $queryCache,\n        private CacheRegionMock $region,\n    ) {\n    }\n\n    public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): DefaultQueryCache\n    {\n        return $this->queryCache;\n    }\n\n    public function getRegion(array $cache): CacheRegionMock\n    {\n        return $this->region;\n    }\n\n    public function getTimestampRegion(): TimestampRegionMock\n    {\n        return new TimestampRegionMock();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/DefaultRegionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheEntry;\nuse Doctrine\\ORM\\Cache\\Region\\DefaultRegion;\nuse Doctrine\\Tests\\Mocks\\CacheEntryMock;\nuse Doctrine\\Tests\\Mocks\\CacheKeyMock;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\Test;\n\nuse function array_map;\n\n/** @extends RegionTestCase<DefaultRegion> */\n#[Group('DDC-2183')]\nclass DefaultRegionTest extends RegionTestCase\n{\n    protected function createRegion(): DefaultRegion\n    {\n        return new DefaultRegion('default.region.test', $this->cacheItemPool);\n    }\n\n    public function testGetters(): void\n    {\n        self::assertEquals('default.region.test', $this->region->getName());\n    }\n\n    public function testSharedRegion(): void\n    {\n        $key     = new CacheKeyMock('key');\n        $entry   = new CacheEntryMock(['value' => 'foo']);\n        $region1 = new DefaultRegion('region1', $this->cacheItemPool);\n        $region2 = new DefaultRegion('region2', $this->cacheItemPool);\n\n        self::assertFalse($region1->contains($key));\n        self::assertFalse($region2->contains($key));\n\n        $region1->put($key, $entry);\n        $region2->put($key, $entry);\n\n        self::assertTrue($region1->contains($key));\n        self::assertTrue($region2->contains($key));\n\n        $region1->evictAll();\n\n        self::assertFalse($region1->contains($key));\n        self::assertTrue($region2->contains($key));\n    }\n\n    public function testGetMulti(): void\n    {\n        $key1   = new CacheKeyMock('key.1');\n        $value1 = new CacheEntryMock(['id' => 1, 'name' => 'bar']);\n\n        $key2   = new CacheKeyMock('key.2');\n        $value2 = new CacheEntryMock(['id' => 2, 'name' => 'bar']);\n\n        self::assertFalse($this->region->contains($key1));\n        self::assertFalse($this->region->contains($key2));\n\n        $this->region->put($key1, $value1);\n        $this->region->put($key2, $value2);\n\n        self::assertTrue($this->region->contains($key1));\n        self::assertTrue($this->region->contains($key2));\n\n        $actual = $this->region->getMultiple(new CollectionCacheEntry([$key1, $key2]));\n\n        self::assertEquals($value1, $actual[0]);\n        self::assertEquals($value2, $actual[1]);\n    }\n\n    public function testGetMultiPreservesOrderAndKeys(): void\n    {\n        $key1   = new CacheKeyMock('key.1');\n        $value1 = new CacheEntryMock(['id' => 1]);\n\n        $key2   = new CacheKeyMock('key.2');\n        $value2 = new CacheEntryMock(['id' => 2]);\n\n        $this->region->put($key1, $value1);\n        $this->region->put($key2, $value2);\n\n        $actual = array_map(\n            'iterator_to_array',\n            $this->region->getMultiple(new CollectionCacheEntry(['one' => $key1, 'two' => $key2])),\n        );\n\n        self::assertSame([\n            'one' => ['id' => 1],\n            'two' => ['id' => 2],\n        ], $actual);\n    }\n\n    #[Test]\n    #[Group('GH7266')]\n    public function corruptedDataDoesNotLeakIntoApplicationWhenGettingSingleEntry(): void\n    {\n        $key1 = new CacheKeyMock('key.1');\n        $this->cacheItemPool->save(\n            $this->cacheItemPool\n                ->getItem('DC2_REGION_' . $this->region->getName() . '_' . $key1->hash)\n                ->set('a-very-invalid-value'),\n        );\n\n        self::assertTrue($this->region->contains($key1));\n        self::assertNull($this->region->get($key1));\n    }\n\n    #[Test]\n    #[Group('GH7266')]\n    public function corruptedDataDoesNotLeakIntoApplicationWhenGettingMultipleEntries(): void\n    {\n        $key1 = new CacheKeyMock('key.1');\n        $this->cacheItemPool->save(\n            $this->cacheItemPool\n                ->getItem('DC2_REGION_' . $this->region->getName() . '_' . $key1->hash)\n                ->set('a-very-invalid-value'),\n        );\n\n        self::assertTrue($this->region->contains($key1));\n        self::assertNull($this->region->getMultiple(new CollectionCacheEntry([$key1])));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/FileLockRegionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Region\\DefaultRegion;\nuse Doctrine\\ORM\\Cache\\Region\\FileLockRegion;\nuse Doctrine\\Tests\\Mocks\\CacheEntryMock;\nuse Doctrine\\Tests\\Mocks\\CacheKeyMock;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse RecursiveDirectoryIterator;\nuse RecursiveIteratorIterator;\nuse ReflectionMethod;\n\nuse function file_put_contents;\nuse function is_dir;\nuse function rmdir;\nuse function sys_get_temp_dir;\nuse function uniqid;\nuse function unlink;\n\n/** @extends RegionTestCase<FileLockRegion> */\n#[Group('DDC-2183')]\nclass FileLockRegionTest extends RegionTestCase\n{\n    protected string $directory;\n\n    public function tearDown(): void\n    {\n        $this->cleanTestDirectory($this->directory);\n    }\n\n    private function getFileName(FileLockRegion $region, CacheKey $key): string\n    {\n        $reflection = new ReflectionMethod($region, 'getLockFileName');\n\n        return $reflection->invoke($region, $key);\n    }\n\n    protected function createRegion(int $lockLifetime = 60): FileLockRegion\n    {\n        $this->directory = sys_get_temp_dir() . '/doctrine_lock_' . uniqid();\n\n        $region = new DefaultRegion('concurren_region_test', $this->cacheItemPool);\n\n        return new FileLockRegion($region, $this->directory, $lockLifetime);\n    }\n\n    public function testGetRegionName(): void\n    {\n        self::assertEquals('concurren_region_test', $this->region->getName());\n    }\n\n    public function testLockAndUnlock(): void\n    {\n        $key   = new CacheKeyMock('key');\n        $entry = new CacheEntryMock(['foo' => 'bar']);\n        $file  = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        $lock = $this->region->lock($key);\n\n        self::assertFileExists($file);\n        self::assertInstanceOf(Lock::class, $lock);\n        self::assertStringEqualsFile($file, $lock->value);\n\n        // should be not available after lock\n        self::assertFalse($this->region->contains($key));\n        self::assertNull($this->region->get($key));\n\n        self::assertTrue($this->region->unlock($key, $lock));\n        self::assertFileDoesNotExist($file);\n    }\n\n    public function testLockWithExistingLock(): void\n    {\n        $key   = new CacheKeyMock('key');\n        $entry = new CacheEntryMock(['foo' => 'bar']);\n        $file  = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        file_put_contents($file, 'foo');\n        self::assertFileExists($file);\n        self::assertStringEqualsFile($file, 'foo');\n\n        self::assertNull($this->region->lock($key));\n        self::assertStringEqualsFile($file, 'foo');\n        self::assertFileExists($file);\n\n        // should be not available\n        self::assertFalse($this->region->contains($key));\n        self::assertNull($this->region->get($key));\n    }\n\n    public function testUnlockWithExistingLock(): void\n    {\n        $key   = new CacheKeyMock('key');\n        $entry = new CacheEntryMock(['foo' => 'bar']);\n        $file  = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));\n        self::assertStringEqualsFile($file, $lock->value);\n        self::assertFileExists($file);\n\n        // change the lock\n        file_put_contents($file, 'foo');\n        self::assertFileExists($file);\n        self::assertStringEqualsFile($file, 'foo');\n\n        //try to unlock\n        self::assertFalse($this->region->unlock($key, $lock));\n        self::assertStringEqualsFile($file, 'foo');\n        self::assertFileExists($file);\n\n        // should be not available\n        self::assertFalse($this->region->contains($key));\n        self::assertNull($this->region->get($key));\n    }\n\n    public function testPutWithExistingLock(): void\n    {\n        $key   = new CacheKeyMock('key');\n        $entry = new CacheEntryMock(['foo' => 'bar']);\n        $file  = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        // create lock\n        file_put_contents($file, 'foo');\n        self::assertFileExists($file);\n        self::assertStringEqualsFile($file, 'foo');\n\n        self::assertFalse($this->region->contains($key));\n        self::assertFalse($this->region->put($key, $entry));\n        self::assertFalse($this->region->contains($key));\n\n        self::assertFileExists($file);\n        self::assertStringEqualsFile($file, 'foo');\n    }\n\n    public function testLockedEvict(): void\n    {\n        $key   = new CacheKeyMock('key');\n        $entry = new CacheEntryMock(['foo' => 'bar']);\n        $file  = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));\n        self::assertStringEqualsFile($file, $lock->value);\n        self::assertFileExists($file);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->evict($key));\n        self::assertFalse($this->region->contains($key));\n        self::assertFileDoesNotExist($file);\n    }\n\n    public function testLockedEvictAll(): void\n    {\n        $key1   = new CacheKeyMock('key1');\n        $entry1 = new CacheEntryMock(['foo1' => 'bar1']);\n        $file1  = $this->getFileName($this->region, $key1);\n\n        $key2   = new CacheKeyMock('key2');\n        $entry2 = new CacheEntryMock(['foo2' => 'bar2']);\n        $file2  = $this->getFileName($this->region, $key2);\n\n        self::assertFalse($this->region->contains($key1));\n        self::assertTrue($this->region->put($key1, $entry1));\n        self::assertTrue($this->region->contains($key1));\n\n        self::assertFalse($this->region->contains($key2));\n        self::assertTrue($this->region->put($key2, $entry2));\n        self::assertTrue($this->region->contains($key2));\n\n        self::assertInstanceOf(Lock::class, $lock1 = $this->region->lock($key1));\n        self::assertInstanceOf(Lock::class, $lock2 = $this->region->lock($key2));\n\n        self::assertStringEqualsFile($file2, $lock2->value);\n        self::assertStringEqualsFile($file1, $lock1->value);\n\n        self::assertFileExists($file1);\n        self::assertFileExists($file2);\n\n        self::assertTrue($this->region->evictAll());\n\n        self::assertFileDoesNotExist($file1);\n        self::assertFileDoesNotExist($file2);\n\n        self::assertFalse($this->region->contains($key1));\n        self::assertFalse($this->region->contains($key2));\n    }\n\n    public function testLockLifetime(): void\n    {\n        $this->region = $this->createRegion(-10);\n        $key          = new CacheKeyMock('key');\n        $entry        = new CacheEntryMock(['foo' => 'bar']);\n        $file         = $this->getFileName($this->region, $key);\n\n        self::assertFalse($this->region->contains($key));\n        self::assertTrue($this->region->put($key, $entry));\n        self::assertTrue($this->region->contains($key));\n\n        self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));\n        self::assertStringEqualsFile($file, $lock->value);\n        self::assertFileExists($file);\n\n        // outdated lock should be removed\n        self::assertTrue($this->region->contains($key));\n        self::assertNotNull($this->region->get($key));\n        self::assertFileDoesNotExist($file);\n    }\n\n    private function cleanTestDirectory(string|null $path): void\n    {\n        $path = $path ?: $this->directory;\n\n        if (! is_dir($path)) {\n            return;\n        }\n\n        $directoryIterator = new RecursiveIteratorIterator(\n            new RecursiveDirectoryIterator($path),\n            RecursiveIteratorIterator::CHILD_FIRST,\n        );\n\n        foreach ($directoryIterator as $file) {\n            if ($file->isFile()) {\n                @unlink((string) $file->getRealPath());\n            } else {\n                @rmdir((string) $file->getRealPath());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Collection/CollectionPersisterTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\n\n#[Group('DDC-2183')]\nabstract class CollectionPersisterTestCase extends OrmTestCase\n{\n    protected Region&MockObject $region;\n    protected CollectionPersister&MockObject $collectionPersister;\n    protected EntityManagerMock $em;\n\n    abstract protected function createPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        Region $region,\n        AssociationMapping $mapping,\n    ): AbstractCollectionPersister;\n\n    protected function setUp(): void\n    {\n        $this->getSharedSecondLevelCache()->clear();\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->em                  = $this->getTestEntityManager();\n        $this->region              = $this->createRegion();\n        $this->collectionPersister = $this->createMock(CollectionPersister::class);\n    }\n\n    protected function createRegion(): Region&MockObject\n    {\n        return $this->createMock(Region::class);\n    }\n\n    protected function createCollection(object $owner): PersistentCollection\n    {\n        $class = $this->em->getClassMetadata(State::class);\n        $assoc = $class->associationMappings['cities'];\n        $coll  = new PersistentCollection($this->em, $class, new ArrayCollection());\n\n        $coll->setOwner($owner, $assoc);\n        $coll->setInitialized(true);\n\n        return $coll;\n    }\n\n    protected function createPersisterDefault(): AbstractCollectionPersister\n    {\n        $assoc = $this->em->getClassMetadata(State::class)->associationMappings['cities'];\n\n        return $this->createPersister($this->em, $this->collectionPersister, $this->region, $assoc);\n    }\n\n    public function testImplementsEntityPersister(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        self::assertInstanceOf(CollectionPersister::class, $persister);\n        self::assertInstanceOf(CachedPersister::class, $persister);\n        self::assertInstanceOf(CachedCollectionPersister::class, $persister);\n    }\n\n    public function testInvokeDelete(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('delete')\n            ->with(self::identicalTo($collection));\n\n        $persister->delete($collection);\n    }\n\n    public function testInvokeUpdate(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $collection->setDirty(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('update')\n            ->with(self::identicalTo($collection));\n\n        $persister->update($collection);\n    }\n\n    public function testInvokeCount(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('count')\n            ->with(self::identicalTo($collection))\n            ->willReturn(0);\n\n        self::assertSame(0, $persister->count($collection));\n    }\n\n    public function testInvokeSlice(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $slice      = [(object) [], (object) []];\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('slice')\n            ->with(self::identicalTo($collection), self::identicalTo(1), self::identicalTo(2))\n            ->willReturn($slice);\n\n        self::assertEquals($slice, $persister->slice($collection, 1, 2));\n    }\n\n    public function testInvokeContains(): void\n    {\n        $entity     = new State('Foo');\n        $element    = new State('Bar');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('contains')\n            ->with(self::identicalTo($collection), self::identicalTo($element))\n            ->willReturn(false);\n\n        self::assertFalse($persister->contains($collection, $element));\n    }\n\n    public function testInvokeContainsKey(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('containsKey')\n            ->with(self::identicalTo($collection), self::identicalTo(0))\n            ->willReturn(false);\n\n        self::assertFalse($persister->containsKey($collection, 0));\n    }\n\n    public function testInvokeGet(): void\n    {\n        $entity     = new State('Foo');\n        $element    = new State('Bar');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('get')\n            ->with(self::identicalTo($collection), self::identicalTo(0))\n            ->willReturn($element);\n\n        self::assertEquals($element, $persister->get($collection, 0));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass NonStrictReadWriteCachedCollectionPersisterTest extends CollectionPersisterTestCase\n{\n    protected function createPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        Region $region,\n        AssociationMapping $mapping,\n    ): AbstractCollectionPersister {\n        return new NonStrictReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass ReadOnlyCachedCollectionPersisterTest extends CollectionPersisterTestCase\n{\n    protected function createPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        Region $region,\n        AssociationMapping $mapping,\n    ): AbstractCollectionPersister {\n        return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Collection;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse ReflectionProperty;\n\n#[Group('DDC-2183')]\nclass ReadWriteCachedCollectionPersisterTest extends CollectionPersisterTestCase\n{\n    protected function createPersister(\n        EntityManagerInterface $em,\n        CollectionPersister $persister,\n        Region $region,\n        AssociationMapping $mapping,\n    ): AbstractCollectionPersister {\n        return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);\n    }\n\n    protected function createRegion(): ConcurrentRegion&MockObject\n    {\n        return $this->createMock(ConcurrentRegion::class);\n    }\n\n    public function testDeleteShouldLockItem(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($collection);\n    }\n\n    public function testUpdateShouldLockItem(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($collection);\n    }\n\n    public function testUpdateTransactionRollBackShouldEvictItem(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($collection);\n        $persister->afterTransactionRolledBack();\n    }\n\n    public function testDeleteTransactionRollBackShouldEvictItem(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($collection);\n        $persister->afterTransactionRolledBack();\n    }\n\n    public function testTransactionRollBackDeleteShouldClearQueue(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($collection);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionRolledBack();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testTransactionRollBackUpdateShouldClearQueue(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($collection);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionRolledBack();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testTransactionRollCommitDeleteShouldClearQueue(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($collection);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testTransactionRollCommitUpdateShouldClearQueue(): void\n    {\n        $entity     = new State('Foo');\n        $lock       = Lock::createLockRead();\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($collection);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testDeleteLockFailureShouldIgnoreQueue(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn(null);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('delete')\n            ->with(self::identicalTo($collection));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($collection);\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testUpdateLockFailureShouldIgnoreQueue(): void\n    {\n        $entity     = new State('Foo');\n        $persister  = $this->createPersisterDefault();\n        $collection = $this->createCollection($entity);\n        $key        = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $property   = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn(null);\n\n        $this->collectionPersister->expects(self::once())\n            ->method('update')\n            ->with(self::identicalTo($collection));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($collection);\n        self::assertCount(0, $property->getValue($persister));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Entity/EntityPersisterTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Cache\\Persister\\CachedPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\CachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\n\n#[Group('DDC-2183')]\nabstract class EntityPersisterTestCase extends OrmTestCase\n{\n    protected Region&MockObject $region;\n    protected EntityPersister&MockObject $entityPersister;\n    protected EntityManagerMock $em;\n\n    abstract protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister;\n\n    protected function setUp(): void\n    {\n        $this->getSharedSecondLevelCache()->clear();\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->em              = $this->getTestEntityManager();\n        $this->region          = $this->createRegion();\n        $this->entityPersister = $this->createMock(EntityPersister::class);\n    }\n\n    protected function createRegion(): Region&MockObject\n    {\n        return $this->createMock(Region::class);\n    }\n\n    protected function createPersisterDefault(): AbstractEntityPersister\n    {\n        return $this->createPersister($this->em, $this->entityPersister, $this->region, $this->em->getClassMetadata(Country::class));\n    }\n\n    public function testImplementsEntityPersister(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        self::assertInstanceOf(EntityPersister::class, $persister);\n        self::assertInstanceOf(CachedPersister::class, $persister);\n        self::assertInstanceOf(CachedEntityPersister::class, $persister);\n    }\n\n    public function testInvokeAddInsert(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('addInsert')\n            ->with(self::identicalTo($entity));\n\n        $persister->addInsert($entity);\n    }\n\n    public function testInvokeGetInserts(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('getInserts')\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->getInserts());\n    }\n\n    public function testInvokeGetSelectSQL(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('getSelectSQL')\n            ->with(\n                self::identicalTo(['name' => 'Foo']),\n                self::identicalTo($associationMapping),\n                self::identicalTo(LockMode::OPTIMISTIC),\n                self::identicalTo(2),\n                self::identicalTo(3),\n                self::identicalTo([4]),\n            )\n            ->willReturn('SELECT * FROM foo WERE name = ?');\n\n        self::assertSame('SELECT * FROM foo WERE name = ?', $persister->getSelectSQL(\n            ['name' => 'Foo'],\n            $associationMapping,\n            LockMode::OPTIMISTIC,\n            2,\n            3,\n            [4],\n        ));\n    }\n\n    public function testInvokeGetInsertSQL(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('getInsertSQL')\n            ->willReturn('INSERT INTO foo (?)');\n\n        self::assertSame('INSERT INTO foo (?)', $persister->getInsertSQL());\n    }\n\n    public function testInvokeExpandParameters(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('expandParameters')\n            ->with(self::identicalTo(['name' => 'Foo']))\n            ->willReturn(['name' => 'Foo']);\n\n        self::assertSame(['name' => 'Foo'], $persister->expandParameters(['name' => 'Foo']));\n    }\n\n    public function testInvokeExpandCriteriaParameters(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $criteria  = Criteria::create(true);\n\n        $this->entityPersister->expects(self::once())\n            ->method('expandCriteriaParameters')\n            ->with(self::identicalTo($criteria))\n            ->willReturn(['name' => 'Foo']);\n\n        self::assertSame(['name' => 'Foo'], $persister->expandCriteriaParameters($criteria));\n    }\n\n    public function testInvokeSelectConditionStatementSQL(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('getSelectConditionStatementSQL')\n            ->with(\n                self::identicalTo('id'),\n                self::identicalTo(1),\n                self::identicalTo($associationMapping),\n                self::identicalTo('='),\n            )->willReturn('name = 1');\n\n        self::assertSame('name = 1', $persister->getSelectConditionStatementSQL('id', 1, $associationMapping, '='));\n    }\n\n    public function testInvokeExecuteInserts(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('executeInserts');\n\n        $persister->executeInserts();\n    }\n\n    public function testInvokeUpdate(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('update')\n            ->with(self::identicalTo($entity));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n    }\n\n    public function testInvokeDelete(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('delete')\n            ->with(self::identicalTo($entity))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        self::assertTrue($persister->delete($entity));\n    }\n\n    public function testInvokeGetOwningTable(): void\n    {\n        $persister = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('getOwningTable')\n            ->with(self::identicalTo('name'))\n            ->willReturn('t');\n\n        self::assertSame('t', $persister->getOwningTable('name'));\n    }\n\n    public function testInvokeLoad(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('load')\n            ->with(\n                self::identicalTo(['id' => 1]),\n                self::identicalTo($entity),\n                self::identicalTo($associationMapping),\n                self::identicalTo([1]),\n                self::identicalTo(LockMode::PESSIMISTIC_READ),\n                self::identicalTo(3),\n                self::identicalTo([4]),\n            )\n            ->willReturn($entity);\n\n        self::assertSame($entity, $persister->load(\n            ['id' => 1],\n            $entity,\n            $associationMapping,\n            [1],\n            LockMode::PESSIMISTIC_READ,\n            3,\n            [4],\n        ));\n    }\n\n    public function testInvokeLoadAll(): void\n    {\n        $rsm       = new ResultSetMappingBuilder($this->em);\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $rsm->addEntityResult(Country::class, 'c');\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $this->entityPersister->method('expandParameters')->willReturn([[], []]);\n        $this->entityPersister->expects(self::once())\n            ->method('loadAll')\n            ->with(self::identicalTo(['id' => 1]), self::identicalTo([0]), self::identicalTo(1), self::identicalTo(2))\n            ->willReturn([$entity]);\n\n        $this->entityPersister->expects(self::once())\n            ->method('getResultSetMapping')\n            ->willReturn($rsm);\n\n        self::assertSame([$entity], $persister->loadAll(['id' => 1], [0], 1, 2));\n    }\n\n    public function testInvokeLoadById(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('loadById')\n            ->with(self::identicalTo(['id' => 1]), self::identicalTo($entity))\n            ->willReturn($entity);\n\n        self::assertSame($entity, $persister->loadById(['id' => 1], $entity));\n    }\n\n    public function testInvokeLoadOneToOneEntity(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $owner     = (object) [];\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('loadOneToOneEntity')\n            ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(['id' => 11]))\n            ->willReturn($entity);\n\n        self::assertSame($entity, $persister->loadOneToOneEntity($associationMapping, $owner, ['id' => 11]));\n    }\n\n    public function testInvokeRefresh(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $this->entityPersister->expects(self::once())\n            ->method('refresh')\n            ->with(self::identicalTo(['id' => 1]), self::identicalTo($entity), self::identicalTo(null));\n\n        $persister->refresh(['id' => 1], $entity);\n    }\n\n    public function testInvokeLoadCriteria(): void\n    {\n        $rsm       = new ResultSetMappingBuilder($this->em);\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $criteria  = Criteria::create(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n        $rsm->addEntityResult(Country::class, 'c');\n\n        $this->entityPersister->method('expandCriteriaParameters')->willReturn([[], []]);\n        $this->entityPersister->expects(self::once())\n            ->method('getResultSetMapping')\n            ->willReturn($rsm);\n\n        $this->entityPersister->expects(self::once())\n            ->method('loadCriteria')\n            ->with(self::identicalTo($criteria))\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->loadCriteria($criteria));\n    }\n\n    public function testInvokeGetManyToManyCollection(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $owner     = (object) [];\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('getManyToManyCollection')\n            ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2))\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->getManyToManyCollection($associationMapping, $owner, 1, 2));\n    }\n\n    public function testInvokeGetOneToManyCollection(): void\n    {\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $owner     = (object) [];\n\n        $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n\n        $this->entityPersister->expects(self::once())\n            ->method('getOneToManyCollection')\n            ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2))\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->getOneToManyCollection($associationMapping, $owner, 1, 2));\n    }\n\n    public function testInvokeLoadManyToManyCollection(): void\n    {\n        $mapping   = $this->em->getClassMetadata(Country::class);\n        $assoc     = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n        $coll      = new PersistentCollection($this->em, $mapping, new ArrayCollection());\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $owner     = (object) [];\n\n        $this->entityPersister->expects(self::once())\n            ->method('loadManyToManyCollection')\n            ->with(self::identicalTo($assoc), self::identicalTo($owner), self::identicalTo($coll))\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->loadManyToManyCollection($assoc, $owner, $coll));\n    }\n\n    public function testInvokeLoadOneToManyCollection(): void\n    {\n        $mapping   = $this->em->getClassMetadata(Country::class);\n        $assoc     = new OneToOneInverseSideMapping('foo', 'bar', 'baz');\n        $coll      = new PersistentCollection($this->em, $mapping, new ArrayCollection());\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n        $owner     = (object) [];\n\n        $this->entityPersister->expects(self::once())\n            ->method('loadOneToManyCollection')\n            ->with(self::identicalTo($assoc), self::identicalTo($owner), self::identicalTo($coll))\n            ->willReturn([$entity]);\n\n        self::assertSame([$entity], $persister->loadOneToManyCollection($assoc, $owner, $coll));\n    }\n\n    public function testInvokeLock(): void\n    {\n        $identifier = ['id' => 1];\n        $persister  = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('lock')\n            ->with(self::identicalTo($identifier), self::identicalTo(LockMode::OPTIMISTIC));\n\n        $persister->lock($identifier, LockMode::OPTIMISTIC);\n    }\n\n    public function testInvokeExists(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n\n        $this->entityPersister->expects(self::once())\n            ->method('exists')\n            ->with(self::identicalTo($entity), self::identicalTo(null))\n            ->willReturn(true);\n\n        self::assertTrue($persister->exists($entity));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionProperty;\n\n#[Group('DDC-2183')]\nclass NonStrictReadWriteCachedEntityPersisterTest extends EntityPersisterTestCase\n{\n    protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister\n    {\n        return new NonStrictReadWriteCachedEntityPersister($persister, $region, $em, $metadata);\n    }\n\n    public function testTransactionRollBackShouldClearQueue(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $property  = new ReflectionProperty($persister, 'queuedCache');\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n        $persister->delete($entity);\n\n        self::assertCount(2, $property->getValue($persister));\n\n        $persister->afterTransactionRolledBack();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testInsertTransactionCommitShouldPutCache(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $entry     = new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']);\n        $property  = new ReflectionProperty($persister, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('put')\n            ->with(self::equalTo($key), self::equalTo($entry));\n\n        $this->entityPersister->expects(self::once())\n            ->method('addInsert')\n            ->with(self::equalTo($entity));\n\n        $this->entityPersister->expects(self::once())\n            ->method('getInserts')\n            ->willReturn([$entity]);\n\n        $this->entityPersister->expects(self::once())\n            ->method('executeInserts');\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->addInsert($entity);\n        $persister->executeInserts();\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testUpdateTransactionCommitShouldPutCache(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $entry     = new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']);\n        $property  = new ReflectionProperty($persister, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('put')\n            ->with(self::equalTo($key), self::equalTo($entry));\n\n        $this->entityPersister->expects(self::once())\n            ->method('update')\n            ->with(self::equalTo($entity));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testDeleteTransactionCommitShouldEvictCache(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $property  = new ReflectionProperty($persister, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key));\n\n        $this->entityPersister->expects(self::once())\n            ->method('delete')\n            ->with(self::equalTo($entity));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($entity);\n\n        self::assertCount(1, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass ReadOnlyCachedEntityPersisterTest extends EntityPersisterTestCase\n{\n    protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister\n    {\n        return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata);\n    }\n\n    public function testInvokeUpdate(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Cannot update a readonly entity \"Doctrine\\Tests\\Models\\Cache\\Country\"');\n        $persister = $this->createPersisterDefault();\n        $entity    = new Country('Foo');\n\n        $persister->update($entity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache\\Persister\\Entity;\n\nuse Doctrine\\ORM\\Cache\\ConcurrentRegion;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister;\nuse Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse ReflectionProperty;\n\n#[Group('DDC-2183')]\nclass ReadWriteCachedEntityPersisterTest extends EntityPersisterTestCase\n{\n    protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister\n    {\n        return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);\n    }\n\n    protected function createRegion(): ConcurrentRegion&MockObject\n    {\n        return $this->createMock(ConcurrentRegion::class);\n    }\n\n    public function testDeleteShouldLockItem(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($entity);\n    }\n\n    public function testUpdateShouldLockItem(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n    }\n\n    public function testUpdateTransactionRollBackShouldEvictItem(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key))\n            ->willReturn(true);\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n        $persister->afterTransactionRolledBack();\n    }\n\n    public function testDeleteTransactionRollBackShouldEvictItem(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::once())\n            ->method('evict')\n            ->with(self::equalTo($key));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($entity);\n        $persister->afterTransactionRolledBack();\n    }\n\n    public function testTransactionRollBackShouldClearQueue(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $property  = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache');\n\n        $this->region->expects(self::exactly(2))\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::exactly(2))\n            ->method('evict')\n            ->with(self::equalTo($key));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n        $persister->delete($entity);\n\n        self::assertCount(2, $property->getValue($persister));\n\n        $persister->afterTransactionRolledBack();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testTransactionCommitShouldClearQueue(): void\n    {\n        $entity    = new Country('Foo');\n        $lock      = Lock::createLockRead();\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $property  = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache');\n\n        $this->region->expects(self::exactly(2))\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn($lock);\n\n        $this->region->expects(self::exactly(2))\n            ->method('evict')\n            ->with(self::equalTo($key));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n        $persister->delete($entity);\n\n        self::assertCount(2, $property->getValue($persister));\n\n        $persister->afterTransactionComplete();\n\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testDeleteLockFailureShouldIgnoreQueue(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $property  = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn(null);\n\n        $this->entityPersister->expects(self::once())\n            ->method('delete')\n            ->with(self::equalTo($entity));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->delete($entity);\n        self::assertCount(0, $property->getValue($persister));\n    }\n\n    public function testUpdateLockFailureShouldIgnoreQueue(): void\n    {\n        $entity    = new Country('Foo');\n        $persister = $this->createPersisterDefault();\n        $key       = new EntityCacheKey(Country::class, ['id' => 1]);\n        $property  = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache');\n\n        $this->region->expects(self::once())\n            ->method('lock')\n            ->with(self::equalTo($key))\n            ->willReturn(null);\n\n        $this->entityPersister->expects(self::once())\n            ->method('update')\n            ->with(self::equalTo($entity));\n\n        $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']);\n\n        $persister->update($entity);\n        self::assertCount(0, $property->getValue($persister));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/RegionTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CacheEntry;\nuse Doctrine\\ORM\\Cache\\CacheKey;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\Tests\\Mocks\\CacheEntryMock;\nuse Doctrine\\Tests\\Mocks\\CacheKeyMock;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\n/** @template TRegion of Region */\n#[Group('DDC-2183')]\nabstract class RegionTestCase extends OrmFunctionalTestCase\n{\n    /** @phpstan-var TRegion */\n    protected Region $region;\n    protected CacheItemPoolInterface $cacheItemPool;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->cacheItemPool = new ArrayAdapter();\n        $this->region        = $this->createRegion();\n    }\n\n    /** @phpstan-return TRegion */\n    abstract protected function createRegion(): Region;\n\n    /** @phpstan-return list<array{CacheKeyMock, CacheEntryMock}> */\n    public static function dataProviderCacheValues(): array\n    {\n        return [\n            [new CacheKeyMock('key.1'), new CacheEntryMock(['id' => 1, 'name' => 'bar'])],\n            [new CacheKeyMock('key.2'), new CacheEntryMock(['id' => 2, 'name' => 'foo'])],\n        ];\n    }\n\n    #[DataProvider('dataProviderCacheValues')]\n    public function testPutGetContainsEvict(CacheKey $key, CacheEntry $value): void\n    {\n        self::assertFalse($this->region->contains($key));\n\n        $this->region->put($key, $value);\n\n        self::assertTrue($this->region->contains($key));\n\n        $actual = $this->region->get($key);\n\n        self::assertEquals($value, $actual);\n\n        $this->region->evict($key);\n\n        self::assertFalse($this->region->contains($key));\n    }\n\n    public function testEvictAll(): void\n    {\n        $key1 = new CacheKeyMock('key.1');\n        $key2 = new CacheKeyMock('key.2');\n\n        self::assertFalse($this->region->contains($key1));\n        self::assertFalse($this->region->contains($key2));\n\n        $this->region->put($key1, new CacheEntryMock(['value' => 'foo']));\n        $this->region->put($key2, new CacheEntryMock(['value' => 'bar']));\n\n        self::assertTrue($this->region->contains($key1));\n        self::assertTrue($this->region->contains($key2));\n\n        $this->region->evictAll();\n\n        self::assertFalse($this->region->contains($key1));\n        self::assertFalse($this->region->contains($key2));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Cache/StatisticsCacheLoggerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Cache;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\n#[Group('DDC-2183')]\nclass StatisticsCacheLoggerTest extends TestCase\n{\n    private StatisticsCacheLogger $logger;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->logger = new StatisticsCacheLogger();\n    }\n\n    public function testEntityCache(): void\n    {\n        $name = 'my_entity_region';\n        $key  = new EntityCacheKey(State::class, ['id' => 1]);\n\n        $this->logger->entityCacheHit($name, $key);\n        $this->logger->entityCachePut($name, $key);\n        $this->logger->entityCacheMiss($name, $key);\n\n        self::assertEquals(1, $this->logger->getHitCount());\n        self::assertEquals(1, $this->logger->getPutCount());\n        self::assertEquals(1, $this->logger->getMissCount());\n        self::assertEquals(1, $this->logger->getRegionHitCount($name));\n        self::assertEquals(1, $this->logger->getRegionPutCount($name));\n        self::assertEquals(1, $this->logger->getRegionMissCount($name));\n    }\n\n    public function testCollectionCache(): void\n    {\n        $name = 'my_collection_region';\n        $key  = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n\n        $this->logger->collectionCacheHit($name, $key);\n        $this->logger->collectionCachePut($name, $key);\n        $this->logger->collectionCacheMiss($name, $key);\n\n        self::assertEquals(1, $this->logger->getHitCount());\n        self::assertEquals(1, $this->logger->getPutCount());\n        self::assertEquals(1, $this->logger->getMissCount());\n        self::assertEquals(1, $this->logger->getRegionHitCount($name));\n        self::assertEquals(1, $this->logger->getRegionPutCount($name));\n        self::assertEquals(1, $this->logger->getRegionMissCount($name));\n    }\n\n    public function testQueryCache(): void\n    {\n        $name = 'my_query_region';\n        $key  = new QueryCacheKey('my_query_hash');\n\n        $this->logger->queryCacheHit($name, $key);\n        $this->logger->queryCachePut($name, $key);\n        $this->logger->queryCacheMiss($name, $key);\n\n        self::assertEquals(1, $this->logger->getHitCount());\n        self::assertEquals(1, $this->logger->getPutCount());\n        self::assertEquals(1, $this->logger->getMissCount());\n        self::assertEquals(1, $this->logger->getRegionHitCount($name));\n        self::assertEquals(1, $this->logger->getRegionPutCount($name));\n        self::assertEquals(1, $this->logger->getRegionMissCount($name));\n    }\n\n    public function testMultipleCaches(): void\n    {\n        $coolRegion   = 'my_collection_region';\n        $entityRegion = 'my_entity_region';\n        $queryRegion  = 'my_query_region';\n\n        $coolKey   = new CollectionCacheKey(State::class, 'cities', ['id' => 1]);\n        $entityKey = new EntityCacheKey(State::class, ['id' => 1]);\n        $queryKey  = new QueryCacheKey('my_query_hash');\n\n        $this->logger->queryCacheHit($queryRegion, $queryKey);\n        $this->logger->queryCachePut($queryRegion, $queryKey);\n        $this->logger->queryCacheMiss($queryRegion, $queryKey);\n\n        $this->logger->entityCacheHit($entityRegion, $entityKey);\n        $this->logger->entityCachePut($entityRegion, $entityKey);\n        $this->logger->entityCacheMiss($entityRegion, $entityKey);\n\n        $this->logger->collectionCacheHit($coolRegion, $coolKey);\n        $this->logger->collectionCachePut($coolRegion, $coolKey);\n        $this->logger->collectionCacheMiss($coolRegion, $coolKey);\n\n        self::assertEquals(3, $this->logger->getHitCount());\n        self::assertEquals(3, $this->logger->getPutCount());\n        self::assertEquals(3, $this->logger->getMissCount());\n\n        self::assertEquals(1, $this->logger->getRegionHitCount($queryRegion));\n        self::assertEquals(1, $this->logger->getRegionPutCount($queryRegion));\n        self::assertEquals(1, $this->logger->getRegionMissCount($queryRegion));\n\n        self::assertEquals(1, $this->logger->getRegionHitCount($coolRegion));\n        self::assertEquals(1, $this->logger->getRegionPutCount($coolRegion));\n        self::assertEquals(1, $this->logger->getRegionMissCount($coolRegion));\n\n        self::assertEquals(1, $this->logger->getRegionHitCount($entityRegion));\n        self::assertEquals(1, $this->logger->getRegionPutCount($entityRegion));\n        self::assertEquals(1, $this->logger->getRegionMissCount($entityRegion));\n\n        $miss = $this->logger->getRegionsMiss();\n        $hit  = $this->logger->getRegionsHit();\n        $put  = $this->logger->getRegionsPut();\n\n        self::assertArrayHasKey($coolRegion, $miss);\n        self::assertArrayHasKey($queryRegion, $miss);\n        self::assertArrayHasKey($entityRegion, $miss);\n\n        self::assertArrayHasKey($coolRegion, $put);\n        self::assertArrayHasKey($queryRegion, $put);\n        self::assertArrayHasKey($entityRegion, $put);\n\n        self::assertArrayHasKey($coolRegion, $hit);\n        self::assertArrayHasKey($queryRegion, $hit);\n        self::assertArrayHasKey($entityRegion, $hit);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/ConfigurationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Cache\\CacheConfiguration;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Exception\\InvalidEntityRepository;\nuse Doctrine\\ORM\\Mapping as MappingNamespace;\nuse Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\EntityListenerResolver;\nuse Doctrine\\ORM\\Mapping\\NamingStrategy;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753CustomRepository;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\n/**\n * Tests for the Configuration object\n */\nclass ConfigurationTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    private Configuration $configuration;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->configuration = new Configuration();\n    }\n\n    #[IgnoreDeprecations]\n    public function testSetGetProxyDir(): void\n    {\n        self::assertNull($this->configuration->getProxyDir()); // defaults\n\n        $this->configuration->setProxyDir(__DIR__);\n        self::assertSame(__DIR__, $this->configuration->getProxyDir());\n    }\n\n    #[IgnoreDeprecations]\n    public function testSetGetAutoGenerateProxyClasses(): void\n    {\n        self::assertSame(ProxyFactory::AUTOGENERATE_ALWAYS, $this->configuration->getAutoGenerateProxyClasses()); // defaults\n\n        $this->configuration->setAutoGenerateProxyClasses(false);\n        self::assertSame(ProxyFactory::AUTOGENERATE_NEVER, $this->configuration->getAutoGenerateProxyClasses());\n\n        $this->configuration->setAutoGenerateProxyClasses(true);\n        self::assertSame(ProxyFactory::AUTOGENERATE_ALWAYS, $this->configuration->getAutoGenerateProxyClasses());\n\n        $this->configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS);\n        self::assertSame(ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS, $this->configuration->getAutoGenerateProxyClasses());\n    }\n\n    #[IgnoreDeprecations]\n    public function testSetGetProxyNamespace(): void\n    {\n        self::assertNull($this->configuration->getProxyNamespace()); // defaults\n\n        $this->configuration->setProxyNamespace(__NAMESPACE__);\n        self::assertSame(__NAMESPACE__, $this->configuration->getProxyNamespace());\n    }\n\n    public function testSetGetMetadataDriverImpl(): void\n    {\n        self::assertNull($this->configuration->getMetadataDriverImpl()); // defaults\n\n        $metadataDriver = $this->createMock(MappingDriver::class);\n        $this->configuration->setMetadataDriverImpl($metadataDriver);\n        self::assertSame($metadataDriver, $this->configuration->getMetadataDriverImpl());\n    }\n\n    public function testSetGetQueryCache(): void\n    {\n        self::assertNull($this->configuration->getQueryCache()); // defaults\n        $queryCache = $this->createMock(CacheItemPoolInterface::class);\n        $this->configuration->setQueryCache($queryCache);\n        self::assertSame($queryCache, $this->configuration->getQueryCache());\n    }\n\n    public function testSetGetHydrationCache(): void\n    {\n        self::assertNull($this->configuration->getHydrationCache()); // defaults\n        $hydrationCache = $this->createStub(CacheItemPoolInterface::class);\n        $this->configuration->setHydrationCache($hydrationCache);\n        self::assertSame($hydrationCache, $this->configuration->getHydrationCache());\n    }\n\n    public function testSetGetMetadataCache(): void\n    {\n        self::assertNull($this->configuration->getMetadataCache());\n        $cache = $this->createStub(CacheItemPoolInterface::class);\n        $this->configuration->setMetadataCache($cache);\n        self::assertSame($cache, $this->configuration->getMetadataCache());\n    }\n\n    public function testAddGetCustomStringFunction(): void\n    {\n        $this->configuration->addCustomStringFunction('FunctionName', self::class);\n        self::assertSame(self::class, $this->configuration->getCustomStringFunction('FunctionName'));\n        self::assertNull($this->configuration->getCustomStringFunction('NonExistingFunction'));\n        $this->configuration->setCustomStringFunctions(['OtherFunctionName' => self::class]);\n        self::assertSame(self::class, $this->configuration->getCustomStringFunction('OtherFunctionName'));\n    }\n\n    public function testAddGetCustomNumericFunction(): void\n    {\n        $this->configuration->addCustomNumericFunction('FunctionName', self::class);\n        self::assertSame(self::class, $this->configuration->getCustomNumericFunction('FunctionName'));\n        self::assertNull($this->configuration->getCustomNumericFunction('NonExistingFunction'));\n        $this->configuration->setCustomNumericFunctions(['OtherFunctionName' => self::class]);\n        self::assertSame(self::class, $this->configuration->getCustomNumericFunction('OtherFunctionName'));\n    }\n\n    public function testAddGetCustomDatetimeFunction(): void\n    {\n        $this->configuration->addCustomDatetimeFunction('FunctionName', self::class);\n        self::assertSame(self::class, $this->configuration->getCustomDatetimeFunction('FunctionName'));\n        self::assertNull($this->configuration->getCustomDatetimeFunction('NonExistingFunction'));\n        $this->configuration->setCustomDatetimeFunctions(['OtherFunctionName' => self::class]);\n        self::assertSame(self::class, $this->configuration->getCustomDatetimeFunction('OtherFunctionName'));\n    }\n\n    public function testAddGetCustomHydrationMode(): void\n    {\n        self::assertNull($this->configuration->getCustomHydrationMode('NonExisting'));\n        $this->configuration->addCustomHydrationMode('HydrationModeName', self::class);\n        self::assertSame(self::class, $this->configuration->getCustomHydrationMode('HydrationModeName'));\n    }\n\n    public function testSetCustomHydrationModes(): void\n    {\n        $this->configuration->addCustomHydrationMode('HydrationModeName', self::class);\n        self::assertSame(self::class, $this->configuration->getCustomHydrationMode('HydrationModeName'));\n\n        $this->configuration->setCustomHydrationModes(\n            ['AnotherHydrationModeName' => self::class],\n        );\n\n        self::assertNull($this->configuration->getCustomHydrationMode('HydrationModeName'));\n        self::assertSame(self::class, $this->configuration->getCustomHydrationMode('AnotherHydrationModeName'));\n    }\n\n    public function testSetGetClassMetadataFactoryName(): void\n    {\n        self::assertSame(MappingNamespace\\ClassMetadataFactory::class, $this->configuration->getClassMetadataFactoryName());\n        $this->configuration->setClassMetadataFactoryName(self::class);\n        self::assertSame(self::class, $this->configuration->getClassMetadataFactoryName());\n    }\n\n    public function testAddGetFilters(): void\n    {\n        self::assertNull($this->configuration->getFilterClassName('NonExistingFilter'));\n        $this->configuration->addFilter('FilterName', self::class);\n        self::assertSame(self::class, $this->configuration->getFilterClassName('FilterName'));\n    }\n\n    public function setDefaultRepositoryClassName(): void\n    {\n        self::assertSame(EntityRepository::class, $this->configuration->getDefaultRepositoryClassName());\n        $this->configuration->setDefaultRepositoryClassName(DDC753CustomRepository::class);\n        self::assertSame(DDC753CustomRepository::class, $this->configuration->getDefaultRepositoryClassName());\n        $this->expectException(InvalidEntityRepository::class);\n        $this->expectExceptionMessage('Invalid repository class \\'Doctrine\\Tests\\ORM\\ConfigurationTest\\'. It must be a Doctrine\\ORM\\EntityRepository.');\n        $this->configuration->setDefaultRepositoryClassName(self::class);\n    }\n\n    public function testSetGetNamingStrategy(): void\n    {\n        self::assertInstanceOf(NamingStrategy::class, $this->configuration->getNamingStrategy());\n        $namingStrategy = $this->createMock(NamingStrategy::class);\n        $this->configuration->setNamingStrategy($namingStrategy);\n        self::assertSame($namingStrategy, $this->configuration->getNamingStrategy());\n    }\n\n    public function testSetGetQuoteStrategy(): void\n    {\n        self::assertInstanceOf(QuoteStrategy::class, $this->configuration->getQuoteStrategy());\n        $quoteStrategy = $this->createMock(QuoteStrategy::class);\n        $this->configuration->setQuoteStrategy($quoteStrategy);\n        self::assertSame($quoteStrategy, $this->configuration->getQuoteStrategy());\n    }\n\n    #[Group('DDC-1955')]\n    public function testSetGetEntityListenerResolver(): void\n    {\n        self::assertInstanceOf(EntityListenerResolver::class, $this->configuration->getEntityListenerResolver());\n        self::assertInstanceOf(MappingNamespace\\DefaultEntityListenerResolver::class, $this->configuration->getEntityListenerResolver());\n        $resolver = $this->createMock(EntityListenerResolver::class);\n        $this->configuration->setEntityListenerResolver($resolver);\n        self::assertSame($resolver, $this->configuration->getEntityListenerResolver());\n    }\n\n    #[Group('DDC-2183')]\n    public function testSetGetSecondLevelCacheConfig(): void\n    {\n        $mockClass = $this->createMock(CacheConfiguration::class);\n\n        self::assertNull($this->configuration->getSecondLevelCacheConfiguration());\n        $this->configuration->setSecondLevelCacheConfiguration($mockClass);\n        self::assertEquals($mockClass, $this->configuration->getSecondLevelCacheConfiguration());\n    }\n\n    #[Group('GH10313')]\n    public function testSetGetTypedFieldMapper(): void\n    {\n        self::assertEmpty($this->configuration->getTypedFieldMapper());\n        $defaultTypedFieldMapper = new DefaultTypedFieldMapper();\n        $this->configuration->setTypedFieldMapper($defaultTypedFieldMapper);\n        self::assertSame($defaultTypedFieldMapper, $this->configuration->getTypedFieldMapper());\n    }\n\n    #[RequiresPhp('8.4')]\n    #[IgnoreDeprecations]\n    public function testDisablingNativeLazyObjectsIsDeprecated(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n\n        $this->configuration->enableNativeLazyObjects(false);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Entity/ConstructorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Entity;\n\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass ConstructorTest extends OrmTestCase\n{\n    public function testFieldInitializationInConstructor(): void\n    {\n        $entity = new ConstructorTestEntity1('romanb');\n        self::assertEquals('romanb', $entity->username);\n    }\n}\n\nclass ConstructorTestEntity1\n{\n    private int $id;\n\n    /** @var string|null */\n    public $username;\n\n    public function __construct(string|null $username = null)\n    {\n        if ($username !== null) {\n            $this->username = $username;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/EntityManagerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\EntityManagerClosed;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\QueryBuilder;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Exception;\nuse Generator;\nuse PHPUnit\\Framework\\Assert;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse stdClass;\nuse TypeError;\n\nclass EntityManagerTest extends OrmTestCase\n{\n    private EntityManagerMock $entityManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    #[Group('DDC-899')]\n    public function testIsOpen(): void\n    {\n        self::assertTrue($this->entityManager->isOpen());\n        $this->entityManager->close();\n        self::assertFalse($this->entityManager->isOpen());\n    }\n\n    public function testGetConnection(): void\n    {\n        self::assertInstanceOf(Connection::class, $this->entityManager->getConnection());\n    }\n\n    public function testGetMetadataFactory(): void\n    {\n        self::assertInstanceOf(ClassMetadataFactory::class, $this->entityManager->getMetadataFactory());\n    }\n\n    public function testGetConfiguration(): void\n    {\n        self::assertInstanceOf(Configuration::class, $this->entityManager->getConfiguration());\n    }\n\n    public function testGetUnitOfWork(): void\n    {\n        self::assertInstanceOf(UnitOfWork::class, $this->entityManager->getUnitOfWork());\n    }\n\n    public function testGetProxyFactory(): void\n    {\n        self::assertInstanceOf(ProxyFactory::class, $this->entityManager->getProxyFactory());\n    }\n\n    public function testGetEventManager(): void\n    {\n        self::assertInstanceOf(EventManager::class, $this->entityManager->getEventManager());\n    }\n\n    public function testCreateNativeQuery(): void\n    {\n        $rsm   = new ResultSetMapping();\n        $query = $this->entityManager->createNativeQuery('SELECT foo', $rsm);\n\n        self::assertSame('SELECT foo', $query->getSql());\n    }\n\n    public function testCreateQueryBuilder(): void\n    {\n        self::assertInstanceOf(QueryBuilder::class, $this->entityManager->createQueryBuilder());\n    }\n\n    public function testCreateQueryBuilderAliasValid(): void\n    {\n        $q  = $this->entityManager->createQueryBuilder()\n             ->select('u')->from(CmsUser::class, 'u');\n        $q2 = clone $q;\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $q->getQuery()->getDql());\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $q2->getQuery()->getDql());\n\n        $q3 = clone $q;\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $q3->getQuery()->getDql());\n    }\n\n    public function testCreateQueryDqlIsOptional(): void\n    {\n        self::assertInstanceOf(Query::class, $this->entityManager->createQuery());\n    }\n\n    public function testCreateQuery(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT 1');\n        self::assertInstanceOf(Query::class, $q);\n        self::assertEquals('SELECT 1', $q->getDql());\n    }\n\n    /** @phpstan-return list<array{string}> */\n    public static function dataAffectedByErrorIfClosedException(): array\n    {\n        return [\n            ['flush'],\n            ['persist'],\n            ['remove'],\n            ['refresh'],\n        ];\n    }\n\n    #[DataProvider('dataAffectedByErrorIfClosedException')]\n    public function testAffectedByErrorIfClosedException(string $methodName): void\n    {\n        $this->expectException(EntityManagerClosed::class);\n        $this->expectExceptionMessage('closed');\n\n        $this->entityManager->close();\n        $this->entityManager->$methodName(new stdClass());\n    }\n\n    /** @return Generator<array{mixed}> */\n    public static function dataToBeReturnedByWrapInTransaction(): Generator\n    {\n        yield [[]];\n        yield [[1]];\n        yield [0];\n        yield [100.5];\n        yield [null];\n        yield [true];\n        yield [false];\n        yield ['foo'];\n    }\n\n    #[DataProvider('dataToBeReturnedByWrapInTransaction')]\n    #[Group('DDC-1125')]\n    public function testWrapInTransactionAcceptsReturn(mixed $expectedValue): void\n    {\n        $return = $this->entityManager->wrapInTransaction(\n            static fn (EntityManagerInterface $em): mixed => $expectedValue,\n        );\n\n        $this->assertSame($expectedValue, $return);\n    }\n\n    #[Group('#5796')]\n    public function testWrapInTransactionReThrowsThrowables(): void\n    {\n        try {\n            $this->entityManager->wrapInTransaction(static function (): void {\n                (static function (array $value): void {\n                    // this only serves as an IIFE that throws a `TypeError`\n                })(null);\n            });\n\n            self::fail('TypeError expected to be thrown');\n        } catch (TypeError) {\n            self::assertFalse($this->entityManager->isOpen());\n        }\n    }\n\n    /** Resetting the EntityManager relies on lazy objects until https://github.com/doctrine/orm/issues/5933 is resolved */\n    #[RequiresPhp('8.4')]\n    public function testLazyGhostEntityManager(): void\n    {\n        $reflector = new ReflectionClass(EntityManager::class);\n\n        $em = $reflector->newLazyGhost($initializer = static function (EntityManager $em): void {\n            $r = new ReflectionProperty(EntityManager::class, 'unitOfWork');\n            $r->setValue($em, new class () extends UnitOfWork {\n                public function __construct()\n                {\n                }\n\n                public function clear(): void\n                {\n                }\n            });\n        });\n\n        $this->assertTrue($em->isOpen());\n        $em->close();\n        $this->assertFalse($em->isOpen());\n\n        $reflector->resetAsLazyGhost($em, $initializer);\n        $this->assertTrue($em->isOpen());\n    }\n\n    public function testItPreservesTheOriginalExceptionOnRollbackFailure(): void\n    {\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n\n        $entityManager = new EntityManagerMock(new class ([], $driver) extends Connection {\n            public function rollBack(): void\n            {\n                throw new Exception('Rollback exception');\n            }\n        });\n\n        try {\n            $entityManager->wrapInTransaction(static function (): void {\n                throw new Exception('Original exception');\n            });\n            self::fail('Exception expected');\n        } catch (Exception $e) {\n            self::assertSame('Rollback exception', $e->getMessage());\n            self::assertNotNull($e->getPrevious());\n            self::assertSame('Original exception', $e->getPrevious()->getMessage());\n        }\n    }\n\n    public function testItDoesNotAttemptToRollbackIfNoTransactionIsActive(): void\n    {\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n\n        $entityManager = new EntityManagerMock(\n            new class ([], $driver) extends Connection {\n                public function commit(): void\n                {\n                    throw new Exception('Commit exception that happens after doing the actual commit');\n                }\n\n                public function rollBack(): void\n                {\n                    Assert::fail('Should not attempt to rollback if no transaction is active');\n                }\n\n                public function isTransactionActive(): bool\n                {\n                    return false;\n                }\n            },\n        );\n\n        $this->expectExceptionMessage('Commit exception');\n        $entityManager->wrapInTransaction(static function (): void {\n        });\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/EntityNotFoundExceptionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\ORM\\EntityNotFoundException;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\EntityNotFoundException}\n */\n#[CoversClass(EntityNotFoundException::class)]\nclass EntityNotFoundExceptionTest extends TestCase\n{\n    public function testFromClassNameAndIdentifier(): void\n    {\n        $exception = EntityNotFoundException::fromClassNameAndIdentifier(\n            'foo',\n            ['foo' => 'bar'],\n        );\n\n        self::assertInstanceOf(EntityNotFoundException::class, $exception);\n        self::assertSame('Entity of type \\'foo\\' for IDs foo(bar) was not found', $exception->getMessage());\n\n        $exception = EntityNotFoundException::fromClassNameAndIdentifier(\n            'foo',\n            [],\n        );\n\n        self::assertInstanceOf(EntityNotFoundException::class, $exception);\n        self::assertSame('Entity of type \\'foo\\' was not found', $exception->getMessage());\n    }\n\n    public function testNoIdentifierFound(): void\n    {\n        $exception = EntityNotFoundException::noIdentifierFound('foo');\n\n        self::assertInstanceOf(EntityNotFoundException::class, $exception);\n        self::assertSame('Unable to find \"foo\" entity identifier associated with the UnitOfWork', $exception->getMessage());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs;\nuse Doctrine\\Persistence\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\ObjectManager;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs}\n */\n#[CoversClass(OnClassMetadataNotFoundEventArgs::class)]\nclass OnClassMetadataNotFoundEventArgsTest extends TestCase\n{\n    public function testEventArgsMutability(): void\n    {\n        $objectManager = $this->createMock(ObjectManager::class);\n        assert($objectManager instanceof ObjectManager);\n\n        $args = new OnClassMetadataNotFoundEventArgs('foo', $objectManager);\n\n        self::assertSame('foo', $args->getClassName());\n        self::assertSame($objectManager, $args->getObjectManager());\n\n        self::assertNull($args->getFoundMetadata());\n\n        $metadata = $this->createMock(ClassMetadata::class);\n        assert($metadata instanceof ClassMetadata);\n\n        $args->setFoundMetadata($metadata);\n\n        self::assertSame($metadata, $args->getFoundMetadata());\n\n        $args->setFoundMetadata(null);\n\n        self::assertNull($args->getFoundMetadata());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/AbstractFetchEagerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\AbstractFetchEager\\AbstractRemoteControl;\nuse Doctrine\\Tests\\Models\\AbstractFetchEager\\MobileRemoteControl;\nuse Doctrine\\Tests\\Models\\AbstractFetchEager\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class AbstractFetchEagerTest extends OrmFunctionalTestCase\n{\n    public function testWithAbstractFetchEager(): void\n    {\n        $this->createSchemaForModels(\n            AbstractRemoteControl::class,\n            User::class,\n        );\n\n        $control = new MobileRemoteControl('smart');\n        $user    = new User($control);\n\n        $entityManage = $this->getEntityManager();\n\n        $entityManage->persist($control);\n        $entityManage->persist($user);\n        $entityManage->flush();\n        $entityManage->clear();\n\n        $user = $entityManage->find(User::class, $user->id);\n\n        self::assertNotNull($user);\n        self::assertEquals('smart', $user->remoteControl->name);\n        self::assertTrue($user->remoteControl->users->contains($user));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function count;\nuse function sprintf;\n\n/**\n * Base class for testing a many-to-many association mapping (without inheritance).\n */\nclass AbstractManyToManyAssociationTestCase extends OrmFunctionalTestCase\n{\n    /** @var string */\n    protected $firstField;\n\n    /** @var string */\n    protected $secondField;\n\n    /** @var string */\n    protected $table;\n\n    public function assertForeignKeysContain($firstId, $secondId): void\n    {\n        self::assertEquals(1, $this->countForeignKeys($firstId, $secondId));\n    }\n\n    public function assertForeignKeysNotContain($firstId, $secondId): void\n    {\n        self::assertEquals(0, $this->countForeignKeys($firstId, $secondId));\n    }\n\n    protected function countForeignKeys($firstId, $secondId): int\n    {\n        return count($this->_em->getConnection()->fetchAllAssociative(sprintf(\n            <<<'SQL'\n            SELECT %s\n              FROM %s\n             WHERE %s = ?\n               AND %s = ?\nSQL\n            ,\n            $this->firstField,\n            $this->table,\n            $this->firstField,\n            $this->secondField,\n        ), [$firstId, $secondId]));\n    }\n\n    public function assertCollectionEquals(Collection $first, Collection $second): bool\n    {\n        return $first->forAll(static fn ($k, $e): bool => $second->contains($e));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/AdvancedAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function assert;\nuse function count;\nuse function is_numeric;\n\n/**\n * Functional tests for the Single Table Inheritance mapping strategy.\n */\nclass AdvancedAssociationTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            Phrase::class,\n            PhraseType::class,\n            Definition::class,\n            Lemma::class,\n            Type::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        //setup\n        $phrase = new Phrase();\n        $phrase->setPhrase('lalala');\n\n        $type = new PhraseType();\n        $type->setType('nonsense');\n        $type->setAbbreviation('non');\n\n        $def1 = new Definition();\n        $def1->setDefinition('def1');\n        $def2 = new Definition();\n        $def2->setDefinition('def2');\n\n        $phrase->setType($type);\n        $phrase->addDefinition($def1);\n        $phrase->addDefinition($def2);\n\n        $this->_em->persist($phrase);\n        $this->_em->persist($type);\n\n        $this->_em->flush();\n        $this->_em->clear();\n        //end setup\n\n        // test1 - lazy-loading many-to-one after find()\n        $phrase2 = $this->_em->find(Phrase::class, $phrase->getId());\n        self::assertTrue(is_numeric($phrase2->getType()->getId()));\n\n        $this->_em->clear();\n\n        // test2 - eager load in DQL query\n        $query = $this->_em->createQuery('SELECT p,t FROM Doctrine\\Tests\\ORM\\Functional\\Phrase p JOIN p.type t');\n        $res   = $query->getResult();\n        self::assertCount(1, $res);\n        self::assertInstanceOf(PhraseType::class, $res[0]->getType());\n        self::assertInstanceOf(PersistentCollection::class, $res[0]->getType()->getPhrases());\n        self::assertFalse($res[0]->getType()->getPhrases()->isInitialized());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n\n        $this->_em->clear();\n\n        // test2 - eager load in DQL query with double-join back and forth\n        $query = $this->_em->createQuery('SELECT p,t,pp FROM Doctrine\\Tests\\ORM\\Functional\\Phrase p JOIN p.type t JOIN t.phrases pp');\n        $res   = $query->getResult();\n        self::assertCount(1, $res);\n        self::assertInstanceOf(PhraseType::class, $res[0]->getType());\n        self::assertInstanceOf(PersistentCollection::class, $res[0]->getType()->getPhrases());\n        self::assertTrue($res[0]->getType()->getPhrases()->isInitialized());\n\n        $this->_em->clear();\n\n        // test3 - lazy-loading one-to-many after find()\n        $phrase3     = $this->_em->find(Phrase::class, $phrase->getId());\n        $definitions = $phrase3->getDefinitions();\n        self::assertInstanceOf(PersistentCollection::class, $definitions);\n        self::assertInstanceOf(Definition::class, $definitions[0]);\n\n        $this->_em->clear();\n\n        // test4 - lazy-loading after DQL query\n        $query       = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Phrase p');\n        $res         = $query->getResult();\n        $definitions = $res[0]->getDefinitions();\n\n        self::assertEquals(1, count($res));\n\n        self::assertInstanceOf(Definition::class, $definitions[0]);\n        self::assertEquals(2, $definitions->count());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testManyToMany(): void\n    {\n        $lemma = new Lemma();\n        $lemma->setLemma('abu');\n\n        $type = new Type();\n        $type->setType('nonsense');\n        $type->setAbbreviation('non');\n\n        $lemma->addType($type);\n\n        $this->_em->persist($lemma);\n        $this->_em->persist($type);\n        $this->_em->flush();\n\n        // test5 ManyToMany\n        $query = $this->_em->createQuery('SELECT l FROM Doctrine\\Tests\\ORM\\Functional\\Lemma l');\n        $res   = $query->getResult();\n        $types = $res[0]->getTypes();\n\n        self::assertInstanceOf(Type::class, $types[0]);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n}\n\n#[Table(name: 'lemma')]\n#[Entity]\nclass Lemma\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'lemma_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'lemma_name', unique: true, length: 255)]\n    private string|null $lemma = null;\n\n    #[ManyToMany(targetEntity: 'Type', mappedBy: 'lemmas', cascade: ['persist'])]\n    private Collection $types;\n\n    public function __construct()\n    {\n        $this->types = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setLemma(string $lemma): void\n    {\n        $this->lemma = $lemma;\n    }\n\n    public function getLemma(): string\n    {\n        return $this->lemma;\n    }\n\n    public function addType(Type $type): void\n    {\n        if (! $this->types->contains($type)) {\n            $this->types[] = $type;\n            $type->addLemma($this);\n        }\n    }\n\n    public function removeType(Type $type): void\n    {\n        $removed = $this->sources->removeElement($type);\n        if ($removed !== null) {\n            $removed->removeLemma($this);\n        }\n    }\n\n    public function getTypes(): Collection\n    {\n        return $this->types;\n    }\n}\n\n#[Table(name: 'type')]\n#[Entity]\nclass Type\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'type_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'type_name', unique: true)]\n    private string|null $type = null;\n\n    #[Column(type: 'string', name: 'type_abbreviation', unique: true)]\n    private string|null $abbreviation = null;\n\n    #[JoinTable(name: 'lemma_type')]\n    #[JoinColumn(name: 'type_id', referencedColumnName: 'type_id')]\n    #[InverseJoinColumn(name: 'lemma_id', referencedColumnName: 'lemma_id')]\n    #[ManyToMany(targetEntity: 'Lemma')]\n    private Collection $lemmas;\n\n    public function __construct()\n    {\n        $this->lemmas = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setType(string $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): string\n    {\n        return $this->type;\n    }\n\n    public function setAbbreviation(string $abbreviation): void\n    {\n        $this->abbreviation = $abbreviation;\n    }\n\n    public function getAbbreviation(): string\n    {\n        return $this->abbreviation;\n    }\n\n    public function addLemma(Lemma $lemma): void\n    {\n        if (! $this->lemmas->contains($lemma)) {\n            $this->lemmas[] = $lemma;\n            $lemma->addType($this);\n        }\n    }\n\n    public function removeLEmma(Lemma $lemma): void\n    {\n        $removed = $this->lemmas->removeElement($lemma);\n        if ($removed !== null) {\n            $removed->removeType($this);\n        }\n    }\n\n    public function getCategories(): Collection\n    {\n        return $this->categories;\n    }\n}\n\n\n#[Table(name: 'phrase')]\n#[Entity]\nclass Phrase\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'phrase_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'phrase_name', unique: true, length: 255)]\n    private string|null $phrase = null;\n\n    #[ManyToOne(targetEntity: 'PhraseType')]\n    #[JoinColumn(name: 'phrase_type_id', referencedColumnName: 'phrase_type_id')]\n    private PhraseType|null $type = null;\n\n    /** @phpstan-var Collection<int, Definition> */\n    #[OneToMany(targetEntity: 'Definition', mappedBy: 'phrase', cascade: ['persist'])]\n    private $definitions;\n\n    public function __construct()\n    {\n        $this->definitions = new ArrayCollection();\n    }\n\n    public function addDefinition(Definition $definition): void\n    {\n        $this->definitions[] = $definition;\n        $definition->setPhrase($this);\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setPhrase(string $phrase): void\n    {\n        $this->phrase = $phrase;\n    }\n\n    public function getPhrase(): string\n    {\n        return $this->phrase;\n    }\n\n    public function setType(PhraseType $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): PhraseType\n    {\n        return $this->type;\n    }\n\n    public function getDefinitions(): Collection\n    {\n        return $this->definitions;\n    }\n}\n\n#[Table(name: 'phrase_type')]\n#[Entity]\nclass PhraseType\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'phrase_type_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'phrase_type_name', unique: true)]\n    private string|null $type = null;\n\n    #[Column(type: 'string', name: 'phrase_type_abbreviation', unique: true)]\n    private string|null $abbreviation = null;\n\n    /** @phpstan-var Collection<int, Phrase> */\n    #[OneToMany(targetEntity: 'Phrase', mappedBy: 'type')]\n    private $phrases;\n\n    public function __construct()\n    {\n        $this->phrases = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setType(string $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): string\n    {\n        return $this->type;\n    }\n\n    public function setAbbreviation(string $abbreviation): void\n    {\n        $this->abbreviation = $abbreviation;\n    }\n\n    public function getAbbreviation(): string\n    {\n        return $this->abbreviation;\n    }\n\n    public function setPhrases(ArrayCollection $phrases): void\n    {\n        $this->phrases = $phrases;\n    }\n\n    public function getPhrases(): Collection\n    {\n        return $this->phrases;\n    }\n}\n\n#[Table(name: 'definition')]\n#[Entity]\nclass Definition\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'definition_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'Phrase')]\n    #[JoinColumn(name: 'definition_phrase_id', referencedColumnName: 'phrase_id')]\n    private Phrase|null $phrase = null;\n\n    #[Column(type: 'text', name: 'definition_text')]\n    private string|null $definition = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setPhrase(Phrase $phrase): void\n    {\n        $this->phrase = $phrase;\n    }\n\n    public function getPhrase(): Phrase\n    {\n        return $this->phrase;\n    }\n\n    public function removePhrase(): void\n    {\n        if ($this->phrase !== null) {\n            $phrase = $this->phrase;\n            assert($phrase instanceof Phrase);\n            $this->phrase = null;\n            $phrase->removeDefinition($this);\n        }\n    }\n\n    public function setDefinition(string $definition): void\n    {\n        $this->definition = $definition;\n    }\n\n    public function getDefinition(): string\n    {\n        return $this->definition;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/AdvancedDqlQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\Company\\CompanyCar;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function count;\n\n/**\n * Functional Query tests.\n */\nclass AdvancedDqlQueryTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->generateFixture();\n    }\n\n    public function testAggregateWithHavingClause(): void\n    {\n        $dql = 'SELECT p.department, AVG(p.salary) AS avgSalary ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'GROUP BY p.department HAVING SUM(p.salary) > 200000 ORDER BY p.department';\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertEquals(2, count($result));\n        self::assertEquals('IT', $result[0]['department']);\n        self::assertEquals(150000, $result[0]['avgSalary']);\n        self::assertEquals('IT2', $result[1]['department']);\n        self::assertEquals(600000, $result[1]['avgSalary']);\n    }\n\n    public function testCommentsInDQL(): void\n    {\n        //same test than testAggregateWithHavingClause but with comments into the DQL\n        $dql = \"SELECT p.department, AVG(p.salary) AS avgSalary -- comment end of line\n-- comment with 'strange chars', & $\n  FROM Doctrine\\\\Tests\\\\Models\\\\Company\\\\CompanyEmployee p\n  -- comment beginning of line GROUP BY\nGROUP BY p.department HAVING SUM(p.salary) > 200000 ORDER BY p.department -- comment end of line\";\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertCount(2, $result);\n        self::assertEquals('IT', $result[0]['department']);\n        self::assertEquals(150000, $result[0]['avgSalary']);\n        self::assertEquals('IT2', $result[1]['department']);\n        self::assertEquals(600000, $result[1]['avgSalary']);\n    }\n\n    public function testUnnamedScalarResultsAreOneBased(): void\n    {\n        $dql = 'SELECT p.department, AVG(p.salary) ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'GROUP BY p.department HAVING SUM(p.salary) > 200000 ORDER BY p.department';\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertCount(2, $result);\n        self::assertEquals(150000, $result[0][1]);\n        self::assertEquals(600000, $result[1][1]);\n    }\n\n    public function testOrderByResultVariableCollectionSize(): void\n    {\n        $dql = 'SELECT p.name, size(p.friends) AS friends ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p ' .\n               'WHERE p.friends IS NOT EMPTY ' .\n               'ORDER BY friends DESC, p.name DESC';\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertCount(4, $result);\n\n        self::assertEquals('Jonathan W.', $result[0]['name']);\n        self::assertEquals(3, $result[0]['friends']);\n\n        self::assertEquals('Guilherme B.', $result[1]['name']);\n        self::assertEquals(2, $result[1]['friends']);\n\n        self::assertEquals('Benjamin E.', $result[2]['name']);\n        self::assertEquals(2, $result[2]['friends']);\n\n        self::assertEquals('Roman B.', $result[3]['name']);\n        self::assertEquals(1, $result[3]['friends']);\n    }\n\n    public function testOrderBySimpleCaseExpression(): void\n    {\n        $dql = <<<'DQL'\n            SELECT p.name\n            FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p\n            ORDER BY CASE p.name\n            WHEN 'Jonathan W.' THEN 1\n            WHEN 'Roman B.' THEN 2\n            WHEN 'Guilherme B.' THEN 3\n            ELSE 4\n            END DESC\nDQL;\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertCount(4, $result);\n\n        self::assertEquals('Benjamin E.', $result[0]['name']);\n        self::assertEquals('Guilherme B.', $result[1]['name']);\n        self::assertEquals('Roman B.', $result[2]['name']);\n        self::assertEquals('Jonathan W.', $result[3]['name']);\n    }\n\n    public function testOrderByGeneralCaseExpression(): void\n    {\n        $dql = <<<'DQL'\n            SELECT p.name\n            FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p\n            ORDER BY CASE\n            WHEN p.name='Jonathan W.' THEN 1\n            WHEN p.name='Roman B.' THEN 2\n            WHEN p.name='Guilherme B.' THEN 3\n            ELSE 4\n            END DESC\nDQL;\n\n        $result = $this->_em->createQuery($dql)->getScalarResult();\n\n        self::assertCount(4, $result);\n\n        self::assertEquals('Benjamin E.', $result[0]['name']);\n        self::assertEquals('Guilherme B.', $result[1]['name']);\n        self::assertEquals('Roman B.', $result[2]['name']);\n        self::assertEquals('Jonathan W.', $result[3]['name']);\n    }\n\n    public function testIsNullAssociation(): void\n    {\n        $dql    = 'SELECT p FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p ' .\n               'WHERE p.spouse IS NULL';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n        self::assertGreaterThan(0, $result[0]->getId());\n        self::assertNull($result[0]->getSpouse());\n\n        self::assertGreaterThan(0, $result[1]->getId());\n        self::assertNull($result[1]->getSpouse());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testSelectSubselect(): void\n    {\n        $dql    = 'SELECT p, (SELECT c.brand FROM Doctrine\\Tests\\Models\\Company\\CompanyCar c WHERE p.car = c) brandName ' .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager p';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getArrayResult();\n\n        self::assertCount(1, $result);\n        self::assertEquals('Caramba', $result[0]['brandName']);\n\n        $this->_em->clear();\n    }\n\n    public function testInSubselect(): void\n    {\n        $dql    = <<<'DQL'\nSELECT p.name FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p\nWHERE p.name IN (SELECT n.name FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson n WHERE n.name = 'Roman B.')\nDQL;\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getScalarResult();\n\n        self::assertCount(1, $result);\n        self::assertEquals('Roman B.', $result[0]['name']);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testGroupByMultipleFields(): void\n    {\n        $dql    = 'SELECT p.department, p.name, count(p.id) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p ' .\n               'GROUP BY p.department, p.name';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(4, $result);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testUpdateAs(): void\n    {\n        $dql = 'UPDATE Doctrine\\Tests\\Models\\Company\\CompanyEmployee AS p SET p.salary = 1';\n        $this->_em->createQuery($dql)->execute();\n\n        $query = $this->_em->createQuery(\n            'SELECT count(p.id) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p WHERE p.salary = 1',\n        );\n        self::assertGreaterThan(0, $query->getResult());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testDeleteAs(): void\n    {\n        $dql = 'DELETE Doctrine\\Tests\\Models\\Company\\CompanyEmployee AS p';\n        $this->_em->createQuery($dql)->getResult();\n\n        $dql    = 'SELECT count(p) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p';\n        $result = $this->_em->createQuery($dql)->getSingleScalarResult();\n\n        self::assertEquals(0, $result);\n    }\n\n    public function generateFixture(): void\n    {\n        $car = new CompanyCar('Caramba');\n\n        $manager1 = new CompanyManager();\n        $manager1->setName('Roman B.');\n        $manager1->setTitle('Foo');\n        $manager1->setDepartment('IT');\n        $manager1->setSalary(100000);\n        $manager1->setCar($car);\n\n        $person2 = new CompanyEmployee();\n        $person2->setName('Benjamin E.');\n        $person2->setDepartment('IT');\n        $person2->setSalary(200000);\n\n        $person3 = new CompanyEmployee();\n        $person3->setName('Guilherme B.');\n        $person3->setDepartment('IT2');\n        $person3->setSalary(400000);\n\n        $person4 = new CompanyEmployee();\n        $person4->setName('Jonathan W.');\n        $person4->setDepartment('IT2');\n        $person4->setSalary(800000);\n\n        $person2->setSpouse($person3);\n\n        $manager1->addFriend($person4);\n        $person2->addFriend($person3);\n        $person2->addFriend($person4);\n        $person3->addFriend($person4);\n\n        $this->_em->persist($car);\n        $this->_em->persist($manager1);\n        $this->_em->persist($person2);\n        $this->_em->persist($person3);\n        $this->_em->persist($person4);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/BasicFunctionalTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\EntityIdentityCollisionException;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass BasicFunctionalTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testBasicUnitsOfWorkWithOneToManyAssociation(): void\n    {\n        // Create\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n\n        self::assertIsNumeric($user->id);\n        self::assertTrue($this->_em->contains($user));\n\n        // Read\n        $user2 = $this->_em->find(CmsUser::class, $user->id);\n        self::assertSame($user, $user2);\n\n        // Add a phonenumber\n        $ph              = new CmsPhonenumber();\n        $ph->phonenumber = '12345';\n        $user->addPhonenumber($ph);\n        $this->_em->flush();\n        self::assertTrue($this->_em->contains($ph));\n        self::assertTrue($this->_em->contains($user));\n\n        // Update name\n        $user->name = 'guilherme';\n        $this->_em->flush();\n        self::assertEquals('guilherme', $user->name);\n\n        // Add another phonenumber\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '6789';\n        $user->addPhonenumber($ph2);\n        $this->_em->flush();\n        self::assertTrue($this->_em->contains($ph2));\n\n        // Delete\n        $this->_em->remove($user);\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($user));\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($ph));\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($ph2));\n        $this->_em->flush();\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($user));\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($ph));\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($ph2));\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($ph));\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($ph2));\n    }\n\n    public function testOneToManyAssociationModification(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n\n        $ph1              = new CmsPhonenumber();\n        $ph1->phonenumber = '0301234';\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '987654321';\n\n        $user->addPhonenumber($ph1);\n        $user->addPhonenumber($ph2);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        // Remove the first element from the collection\n        unset($user->phonenumbers[0]);\n        $ph1->user = null; // owning side!\n\n        $this->_em->flush();\n\n        self::assertCount(1, $user->phonenumbers);\n        self::assertNull($ph1->user);\n    }\n\n    public function testBasicOneToOne(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $user->address = $address; // inverse side\n        $address->user = $user; // owning side!\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        // Check that the foreign key has been set\n        $userId = $this->_em->getConnection()->executeQuery(\n            'SELECT user_id FROM cms_addresses WHERE id=?',\n            [$address->id],\n        )->fetchOne();\n        self::assertIsNumeric($userId);\n\n        $this->_em->clear();\n\n        $user2 = $this->_em->createQuery('select u from \\Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.id=?1')\n                ->setParameter(1, $userId)\n                ->getSingleResult();\n\n        // Address has been eager-loaded because it cant be lazy\n        self::assertInstanceOf(CmsAddress::class, $user2->address);\n        self::assertFalse($this->isUninitializedObject($user2->address));\n    }\n\n    #[Group('DDC-1230')]\n    public function testRemove(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), 'State should be UnitOfWork::STATE_NEW');\n\n        $this->_em->persist($user);\n\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user), 'State should be UnitOfWork::STATE_MANAGED');\n\n        $this->_em->remove($user);\n\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), 'State should be UnitOfWork::STATE_NEW');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $id = $user->getId();\n\n        $this->_em->remove($user);\n\n        self::assertEquals(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($user), 'State should be UnitOfWork::STATE_REMOVED');\n        $this->_em->flush();\n\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), 'State should be UnitOfWork::STATE_NEW');\n\n        self::assertNull($this->_em->find(CmsUser::class, $id));\n    }\n\n    public function testOneToManyOrphanRemoval(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        for ($i = 0; $i < 3; ++$i) {\n            $phone              = new CmsPhonenumber();\n            $phone->phonenumber = 100 + $i;\n            $user->addPhonenumber($phone);\n        }\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n\n        $user->getPhonenumbers()->remove(0);\n        self::assertCount(2, $user->getPhonenumbers());\n\n        $this->_em->flush();\n\n        // Check that there are just 2 phonenumbers left\n        $count = $this->_em->getConnection()->fetchOne('SELECT COUNT(*) FROM cms_phonenumbers');\n        self::assertEquals(2, $count); // only 2 remaining\n\n        // check that clear() removes the others via orphan removal\n        $user->getPhonenumbers()->clear();\n        $this->_em->flush();\n        self::assertEquals(0, $this->_em->getConnection()->fetchOne('select count(*) from cms_phonenumbers'));\n    }\n\n    public function testBasicQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertEquals('Guilherme', $users[0]->name);\n        self::assertEquals('gblanco', $users[0]->username);\n        self::assertEquals('developer', $users[0]->status);\n        //$this->assertNull($users[0]->phonenumbers);\n        //$this->assertNull($users[0]->articles);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n\n        $usersArray = $query->getArrayResult();\n\n        self::assertIsArray($usersArray);\n        self::assertCount(1, $usersArray);\n        self::assertEquals('Guilherme', $usersArray[0]['name']);\n        self::assertEquals('gblanco', $usersArray[0]['username']);\n        self::assertEquals('developer', $usersArray[0]['status']);\n\n        $usersScalar = $query->getScalarResult();\n\n        self::assertIsArray($usersScalar);\n        self::assertCount(1, $usersScalar);\n        self::assertEquals('Guilherme', $usersScalar[0]['u_name']);\n        self::assertEquals('gblanco', $usersScalar[0]['u_username']);\n        self::assertEquals('developer', $usersScalar[0]['u_status']);\n    }\n\n    public function testSingleColumnQuery(): void\n    {\n        $gregoire           = new CmsUser();\n        $gregoire->name     = 'Gregoire';\n        $gregoire->username = 'greg0ire';\n        $gregoire->status   = 'developer';\n        $this->_em->persist($gregoire);\n\n        $bhushan           = new CmsUser();\n        $bhushan->name     = 'Bhushan';\n        $bhushan->username = 'bhushan';\n        $bhushan->status   = 'developer';\n        $this->_em->persist($bhushan);\n\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select u.username from Doctrine\\Tests\\Models\\CMS\\CmsUser u order by u.username DESC');\n\n        $users = $query->getSingleColumnResult();\n\n        $expected = [\n            'greg0ire',\n            'bhushan',\n        ];\n\n        self::assertCount(2, $users);\n        self::assertSame($expected, $users);\n    }\n\n    public function testBasicOneToManyInnerJoin(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p');\n\n        $users = $query->getResult();\n\n        self::assertCount(0, $users);\n    }\n\n    public function testBasicOneToManyLeftJoin(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select u,p from Doctrine\\Tests\\Models\\CMS\\CmsUser u left join u.phonenumbers p');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertEquals('Guilherme', $users[0]->name);\n        self::assertEquals('gblanco', $users[0]->username);\n        self::assertEquals('developer', $users[0]->status);\n        self::assertInstanceOf(PersistentCollection::class, $users[0]->phonenumbers);\n        self::assertTrue($users[0]->phonenumbers->isInitialized());\n        self::assertEquals(0, $users[0]->phonenumbers->count());\n    }\n\n    public function testBasicRefresh(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $user->status = 'mascot';\n\n        self::assertEquals('mascot', $user->status);\n        $this->_em->refresh($user);\n        self::assertEquals('developer', $user->status);\n    }\n\n    #[Group('DDC-833')]\n    public function testRefreshResetsCollection(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        // Add a phonenumber\n        $ph1              = new CmsPhonenumber();\n        $ph1->phonenumber = '12345';\n        $user->addPhonenumber($ph1);\n\n        // Add a phonenumber\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '54321';\n\n        $this->_em->persist($user);\n        $this->_em->persist($ph1);\n        $this->_em->persist($ph2);\n        $this->_em->flush();\n\n        $user->addPhonenumber($ph2);\n\n        self::assertCount(2, $user->phonenumbers);\n        $this->_em->refresh($user);\n\n        self::assertCount(1, $user->phonenumbers);\n    }\n\n    #[Group('DDC-833')]\n    public function testDqlRefreshResetsCollection(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        // Add a phonenumber\n        $ph1              = new CmsPhonenumber();\n        $ph1->phonenumber = '12345';\n        $user->addPhonenumber($ph1);\n\n        // Add a phonenumber\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '54321';\n\n        $this->_em->persist($user);\n        $this->_em->persist($ph1);\n        $this->_em->persist($ph2);\n        $this->_em->flush();\n\n        $user->addPhonenumber($ph2);\n\n        self::assertCount(2, $user->phonenumbers);\n        $dql  = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1';\n        $user = $this->_em->createQuery($dql)\n                          ->setParameter(1, $user->id)\n                          ->setHint(Query::HINT_REFRESH, true)\n                          ->getSingleResult();\n\n        self::assertCount(1, $user->phonenumbers);\n    }\n\n    #[Group('DDC-833')]\n    public function testCreateEntityOfProxy(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        // Add a phonenumber\n        $ph1              = new CmsPhonenumber();\n        $ph1->phonenumber = '12345';\n        $user->addPhonenumber($ph1);\n\n        // Add a phonenumber\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '54321';\n\n        $this->_em->persist($user);\n        $this->_em->persist($ph1);\n        $this->_em->persist($ph2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userId = $user->id;\n        $user   = $this->_em->getReference(CmsUser::class, $user->id);\n\n        $dql  = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1';\n        $user = $this->_em->createQuery($dql)\n                          ->setParameter(1, $userId)\n                          ->getSingleResult();\n\n        self::assertCount(1, $user->phonenumbers);\n    }\n\n    public function testAddToCollectionDoesNotInitialize(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        for ($i = 0; $i < 3; ++$i) {\n            $phone              = new CmsPhonenumber();\n            $phone->phonenumber = 100 + $i;\n            $user->addPhonenumber($phone);\n        }\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals(3, $user->getPhonenumbers()->count());\n\n        $query = $this->_em->createQuery(\"select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username='gblanco'\");\n\n        $gblanco = $query->getSingleResult();\n\n        self::assertFalse($gblanco->getPhonenumbers()->isInitialized());\n\n        $newPhone              = new CmsPhonenumber();\n        $newPhone->phonenumber = 555;\n        $gblanco->addPhonenumber($newPhone);\n\n        self::assertFalse($gblanco->getPhonenumbers()->isInitialized());\n        $this->_em->persist($gblanco);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query    = $this->_em->createQuery(\"select u, p from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p where u.username='gblanco'\");\n        $gblanco2 = $query->getSingleResult();\n        self::assertEquals(4, $gblanco2->getPhonenumbers()->count());\n    }\n\n    public function testInitializeCollectionWithNewObjectsRetainsNewObjects(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        for ($i = 0; $i < 3; ++$i) {\n            $phone              = new CmsPhonenumber();\n            $phone->phonenumber = 100 + $i;\n            $user->addPhonenumber($phone);\n        }\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals(3, $user->getPhonenumbers()->count());\n\n        $query = $this->_em->createQuery(\"select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username='gblanco'\");\n\n        $gblanco = $query->getSingleResult();\n\n        self::assertFalse($gblanco->getPhonenumbers()->isInitialized());\n\n        $newPhone              = new CmsPhonenumber();\n        $newPhone->phonenumber = 555;\n        $gblanco->addPhonenumber($newPhone);\n\n        self::assertFalse($gblanco->getPhonenumbers()->isInitialized());\n        self::assertEquals(4, $gblanco->getPhonenumbers()->count());\n        self::assertTrue($gblanco->getPhonenumbers()->isInitialized());\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query    = $this->_em->createQuery(\"select u, p from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p where u.username='gblanco'\");\n        $gblanco2 = $query->getSingleResult();\n        self::assertEquals(4, $gblanco2->getPhonenumbers()->count());\n    }\n\n    public function testSetToOneAssociationWithGetReference(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n        $this->_em->persist($address);\n\n        $this->_em->flush();\n        $userId = $user->getId();\n        $this->_em->clear();\n        $user = $this->_em->find(CmsUser::class, $userId);\n\n        // Assume we only got the identifier of the address and now want to attach\n        // that address to the user without actually loading it, using getReference().\n        $addressRef = $this->_em->getReference(CmsAddress::class, $address->getId());\n\n        $user->setAddress($addressRef); // Ugh! Initializes address 'cause of $address->setUser($user)!\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Assume we only got the identifier of the user and now want to attach\n        // the article to the user without actually loading it, using getReference().\n        $userRef = $this->_em->getReference(CmsUser::class, $user->getId());\n        self::assertTrue($this->isUninitializedObject($userRef));\n\n        $article        = new CmsArticle();\n        $article->topic = 'topic';\n        $article->text  = 'text';\n        $article->setAuthor($userRef);\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        self::assertTrue($this->isUninitializedObject($userRef));\n\n        $this->_em->clear();\n\n        // Check with a fresh load that the association is indeed there\n        $query   = $this->_em->createQuery(\"select u, a from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.articles a where u.username='gblanco'\");\n        $gblanco = $query->getSingleResult();\n\n        self::assertInstanceOf(CmsUser::class, $gblanco);\n        self::assertInstanceOf(CmsArticle::class, $gblanco->articles[0]);\n        self::assertSame($article->id, $gblanco->articles[0]->id);\n        self::assertSame('text', $gblanco->articles[0]->text);\n    }\n\n    public function testAddToToManyAssociationWithGetReference(): void\n    {\n        $group       = new CmsGroup();\n        $group->name = 'admins';\n        $this->_em->persist($group);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Assume we only got the identifier of the user and now want to attach\n        // the article to the user without actually loading it, using getReference().\n        $groupRef = $this->_em->getReference(CmsGroup::class, $group->id);\n        self::assertTrue($this->isUninitializedObject($groupRef));\n\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->groups->add($groupRef);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        self::assertTrue($this->isUninitializedObject($groupRef));\n\n        $this->_em->clear();\n\n        // Check with a fresh load that the association is indeed there\n        $query   = $this->_em->createQuery(\"select u, a from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.groups a where u.username='gblanco'\");\n        $gblanco = $query->getSingleResult();\n\n        self::assertInstanceOf(CmsUser::class, $gblanco);\n        self::assertInstanceOf(CmsGroup::class, $gblanco->groups[0]);\n        self::assertSame($group->id, $gblanco->groups[0]->id);\n        self::assertSame('admins', $gblanco->groups[0]->name);\n    }\n\n    public function testOneToManyCascadeRemove(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        for ($i = 0; $i < 3; ++$i) {\n            $phone              = new CmsPhonenumber();\n            $phone->phonenumber = 100 + $i;\n            $user->addPhonenumber($phone);\n        }\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query   = $this->_em->createQuery(\"select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username='gblanco'\");\n        $gblanco = $query->getSingleResult();\n\n        $this->_em->remove($gblanco);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        self::assertEquals(0, $this->_em->createQuery(\n            'select count(p.phonenumber) from Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p',\n        )\n                ->getSingleScalarResult());\n\n        self::assertEquals(0, $this->_em->createQuery(\n            'select count(u.id) from Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n        )\n                ->getSingleScalarResult());\n    }\n\n    public function testTextColumnSaveAndRetrieve(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n\n        $article        = new CmsArticle();\n        $article->text  = 'Lorem ipsum dolor sunt.';\n        $article->topic = 'A Test Article!';\n        $article->setAuthor($user);\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n        $articleId = $article->id;\n\n        $this->_em->clear();\n\n        // test find() with leading backslash at the same time\n        $articleNew = $this->_em->find(CmsArticle::class, $articleId);\n        self::assertTrue($this->_em->contains($articleNew));\n        self::assertEquals('Lorem ipsum dolor sunt.', $articleNew->text);\n\n        self::assertNotSame($article, $articleNew);\n\n        $articleNew->text = 'Lorem ipsum dolor sunt. And stuff!';\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $articleNew = $this->_em->find(CmsArticle::class, $articleId);\n        self::assertEquals('Lorem ipsum dolor sunt. And stuff!', $articleNew->text);\n        self::assertTrue($this->_em->contains($articleNew));\n    }\n\n    public function testFlushDoesNotIssueUnnecessaryUpdates(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $address->user = $user;\n        $user->address = $address;\n\n        $article        = new CmsArticle();\n        $article->text  = 'Lorem ipsum dolor sunt.';\n        $article->topic = 'A Test Article!';\n        $article->setAuthor($user);\n\n        $this->_em->persist($article);\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select u,a,ad from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.articles a join u.address ad');\n        $user2 = $query->getSingleResult();\n\n        self::assertCount(1, $user2->articles);\n        self::assertInstanceOf(CmsAddress::class, $user2->address);\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->flush();\n\n        $this->assertQueryCount(0);\n    }\n\n    public function testRemoveEntityByReference(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userRef = $this->_em->getReference(CmsUser::class, $user->getId());\n        $this->_em->remove($userRef);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals(0, $this->_em->getConnection()->fetchOne('select count(*) from cms_users'));\n    }\n\n    public function testQueryEntityByReference(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $user->setAddress($address);\n\n        $this->_em->wrapInTransaction(static function (EntityManagerInterface $em) use ($user): void {\n            $em->persist($user);\n        });\n        $this->_em->clear();\n\n        $userRef  = $this->_em->getReference(CmsUser::class, $user->getId());\n        $address2 = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsAddress a where a.user = :user')\n                ->setParameter('user', $userRef)\n                ->getSingleResult();\n\n        self::assertTrue($userRef === $address2->getUser());\n        self::assertTrue($this->isUninitializedObject($userRef));\n        self::assertEquals('Germany', $address2->country);\n        self::assertEquals('Berlin', $address2->city);\n        self::assertEquals('12345', $address2->zip);\n    }\n\n    public function testOneToOneNullUpdate(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin E.';\n        $user->status   = 'active';\n\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->zip     = '12354';\n        $address->country = 'Germany';\n        $address->street  = 'somestreet';\n        $address->user    = $user;\n\n        $this->_em->persist($address);\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        self::assertEquals(1, $this->_em->getConnection()->fetchOne('select 1 from cms_addresses where user_id = ' . $user->id));\n\n        $address->user = null;\n        $this->_em->flush();\n\n        self::assertNotEquals(1, $this->_em->getConnection()->fetchOne('select 1 from cms_addresses where user_id = ' . $user->id));\n    }\n\n    #[Group('DDC-600')]\n    #[Group('DDC-455')]\n    public function testNewAssociatedEntityDuringFlushThrowsException(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin E.';\n        $user->status   = 'active';\n\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->zip     = '12354';\n        $address->country = 'Germany';\n        $address->street  = 'somestreet';\n        $address->user    = $user;\n\n        $this->_em->persist($address);\n\n        // flushing without persisting $user should raise an exception\n        $this->expectException(InvalidArgumentException::class);\n        $this->_em->flush();\n    }\n\n    #[Group('DDC-600')]\n    #[Group('DDC-455')]\n    public function testNewAssociatedEntityDuringFlushThrowsException2(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin E.';\n        $user->status   = 'active';\n\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->zip     = '12354';\n        $address->country = 'Germany';\n        $address->street  = 'somestreet';\n        $address->user    = $user;\n\n        $this->_em->persist($address);\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $u2            = new CmsUser();\n        $u2->username  = 'beberlei';\n        $u2->name      = 'Benjamin E.';\n        $u2->status    = 'inactive';\n        $address->user = $u2;\n\n        // flushing without persisting $u2 should raise an exception\n        $this->expectException(InvalidArgumentException::class);\n        $this->_em->flush();\n    }\n\n    #[Group('DDC-600')]\n    #[Group('DDC-455')]\n    public function testNewAssociatedEntityDuringFlushThrowsException3(): void\n    {\n        $art        = new CmsArticle();\n        $art->topic = 'topic';\n        $art->text  = 'the text';\n\n        $com        = new CmsComment();\n        $com->topic = 'Good';\n        $com->text  = 'Really good!';\n        $art->addComment($com);\n\n        $this->_em->persist($art);\n\n        // flushing without persisting $com should raise an exception\n        $this->expectException(InvalidArgumentException::class);\n        $this->_em->flush();\n    }\n\n    public function testOneToOneOrphanRemoval(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin E.';\n        $user->status   = 'active';\n\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->zip     = '12354';\n        $address->country = 'Germany';\n        $address->street  = 'somestreet';\n        $address->user    = $user;\n        $user->address    = $address;\n\n        $this->_em->persist($address);\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $addressId = $address->getId();\n\n        $user->address = null;\n\n        $this->_em->flush();\n\n        self::assertEquals(0, $this->_em->getConnection()->fetchOne('select count(*) from cms_addresses'));\n\n        // check orphan removal through replacement\n        $user->address = $address;\n        $address->user = $user;\n\n        $this->_em->flush();\n        self::assertEquals(1, $this->_em->getConnection()->fetchOne('select count(*) from cms_addresses'));\n\n        // remove $address to free up unique key id\n        $this->_em->remove($address);\n        $this->_em->flush();\n\n        $newAddress          = new CmsAddress();\n        $newAddress->city    = 'NewBonn';\n        $newAddress->zip     = '12354';\n        $newAddress->country = 'NewGermany';\n        $newAddress->street  = 'somenewstreet';\n        $newAddress->user    = $user;\n        $user->address       = $newAddress;\n\n        $this->_em->flush();\n        self::assertEquals(1, $this->_em->getConnection()->fetchOne('select count(*) from cms_addresses'));\n    }\n\n    #[Group('DDC-952')]\n    public function testManyToOneFetchModeQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin E.';\n        $user->status   = 'active';\n\n        $article        = new CmsArticle();\n        $article->topic = 'foo';\n        $article->text  = 'bar';\n        $article->user  = $user;\n\n        $this->_em->persist($article);\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.id = ?1';\n        $article = $this->_em->createQuery($dql)\n                             ->setParameter(1, $article->id)\n                             ->setFetchMode(CmsArticle::class, 'user', ClassMetadata::FETCH_EAGER)\n                             ->getSingleResult();\n        self::assertFalse($this->isUninitializedObject($article->user));\n        $this->assertQueryCount(2);\n    }\n\n    #[Group('DDC-720')]\n    public function testFlushSingleManagedEntity(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Dominik';\n        $user->username = 'domnikl';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $user->status = 'administrator';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n        self::assertEquals('administrator', $user->status);\n    }\n\n    #[Group('DDC-720')]\n    public function testFlushAndCascadePersist(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Dominik';\n        $user->username = 'domnikl';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $address          = new CmsAddress();\n        $address->city    = 'Springfield';\n        $address->zip     = '12354';\n        $address->country = 'Germany';\n        $address->street  = 'Foo Street';\n        $address->user    = $user;\n        $user->address    = $address;\n\n        $this->_em->flush();\n\n        self::assertTrue($this->_em->contains($address), 'Other user is contained in EntityManager');\n        self::assertTrue($address->id > 0, 'other user has an id');\n    }\n\n    #[Group('DDC-720')]\n    public function testFlushSingleAndNoCascade(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Dominik';\n        $user->username = 'domnikl';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $article1         = new CmsArticle();\n        $article1->topic  = 'Foo';\n        $article1->text   = 'Foo Text';\n        $article1->user   = $user;\n        $user->articles[] = $article1;\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"A new entity was found through the relationship 'Doctrine\\Tests\\Models\\CMS\\CmsUser#articles'\");\n\n        $this->_em->flush();\n    }\n\n    #[Group('DDC-720')]\n    #[Group('DDC-1612')]\n    #[Group('DDC-2267')]\n    public function testFlushSingleNewEntityThenRemove(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Dominik';\n        $user->username = 'domnikl';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $userId = $user->id;\n\n        $this->_em->remove($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find($user::class, $userId));\n    }\n\n    #[Group('DDC-1585')]\n    public function testWrongAssociationInstance(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Dominik';\n        $user->username = 'domnikl';\n        $user->status   = 'developer';\n        $user->address  = $user;\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage(\n            'Expected value of type \"Doctrine\\Tests\\Models\\CMS\\CmsAddress\" for association field ' .\n            '\"Doctrine\\Tests\\Models\\CMS\\CmsUser#$address\", got \"Doctrine\\Tests\\Models\\CMS\\CmsUser\" instead.',\n        );\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n    }\n\n    public function testItThrowsWhenReferenceUsesIdAssignedByDatabase(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'test';\n        $user->username = 'test';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        // Obtain a reference object for the next ID. This is a user error - references\n        // should be fetched only for existing IDs\n        $ref = $this->_em->getReference(CmsUser::class, $user->id + 1);\n\n        $user2           = new CmsUser();\n        $user2->name     = 'test2';\n        $user2->username = 'test2';\n\n        // Now the database will assign an ID to the $user2 entity, but that place\n        // in the identity map is already taken by user error.\n        $this->expectException(EntityIdentityCollisionException::class);\n        $this->expectExceptionMessageMatches('/another object .* was already present for the same ID/');\n\n        // depending on ID generation strategy, the ID may be asssigned already here\n        // and the entity be put in the identity map\n        $this->_em->persist($user2);\n\n        // post insert IDs will be assigned during flush\n        $this->_em->flush();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CascadeRemoveOrderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('CascadeRemoveOrderTest')]\nclass CascadeRemoveOrderTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            CascadeRemoveOrderEntityO::class,\n            CascadeRemoveOrderEntityG::class,\n        );\n    }\n\n    public function testSingle(): void\n    {\n        $eO = new CascadeRemoveOrderEntityO();\n        $eG = new CascadeRemoveOrderEntityG($eO);\n\n        $this->_em->persist($eO);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $eOloaded = $this->_em->find(CascadeRemoveOrderEntityO::class, $eO->getId());\n\n        $this->_em->remove($eOloaded);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(CascadeRemoveOrderEntityG::class, $eG->getId()));\n    }\n\n    public function testMany(): void\n    {\n        $eO  = new CascadeRemoveOrderEntityO();\n        $eG1 = new CascadeRemoveOrderEntityG($eO);\n        $eG2 = new CascadeRemoveOrderEntityG($eO);\n        $eG3 = new CascadeRemoveOrderEntityG($eO);\n\n        $eO->setOneToOneG($eG2);\n\n        $this->_em->persist($eO);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $eOloaded = $this->_em->find(CascadeRemoveOrderEntityO::class, $eO->getId());\n\n        $this->_em->remove($eOloaded);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(CascadeRemoveOrderEntityG::class, $eG1->getId()));\n        self::assertNull($this->_em->find(CascadeRemoveOrderEntityG::class, $eG2->getId()));\n        self::assertNull($this->_em->find(CascadeRemoveOrderEntityG::class, $eG3->getId()));\n    }\n}\n\n#[Entity]\nclass CascadeRemoveOrderEntityO\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'Doctrine\\Tests\\ORM\\Functional\\CascadeRemoveOrderEntityG')]\n    #[JoinColumn(nullable: true, onDelete: 'SET NULL')]\n    private CascadeRemoveOrderEntityG|null $oneToOneG = null;\n\n    /** @phpstan-var Collection<int, CascadeRemoveOrderEntityG> */\n    #[OneToMany(targetEntity: 'Doctrine\\Tests\\ORM\\Functional\\CascadeRemoveOrderEntityG', mappedBy: 'ownerO', cascade: ['persist', 'remove'])]\n    private $oneToManyG;\n\n    public function __construct()\n    {\n        $this->oneToManyG = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setOneToOneG(CascadeRemoveOrderEntityG $eG): void\n    {\n        $this->oneToOneG = $eG;\n    }\n\n    public function getOneToOneG(): CascadeRemoveOrderEntityG\n    {\n        return $this->oneToOneG;\n    }\n\n    public function addOneToManyG(CascadeRemoveOrderEntityG $eG): void\n    {\n        $this->oneToManyG->add($eG);\n    }\n\n    /** @phpstan-return array<int, CascadeRemoveOrderEntityG> */\n    public function getOneToManyGs(): array\n    {\n        return $this->oneToManyG->toArray();\n    }\n}\n\n#[Entity]\nclass CascadeRemoveOrderEntityG\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'Doctrine\\Tests\\ORM\\Functional\\CascadeRemoveOrderEntityO', inversedBy: 'oneToMany')]\n        private CascadeRemoveOrderEntityO $ownerO,\n        private int $position = 1,\n    ) {\n        $this->ownerO->addOneToManyG($this);\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ClassTableInheritanceSecondTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function count;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy.\n */\nclass ClassTableInheritanceSecondTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            CTIParent::class,\n            CTIChild::class,\n            CTIRelated::class,\n            CTIRelated2::class,\n        );\n    }\n\n    public function testOneToOneAssocToBaseTypeBidirectional(): void\n    {\n        $child = new CTIChild();\n        $child->setData('hello');\n\n        $related = new CTIRelated();\n        $related->setCTIParent($child);\n\n        $this->_em->persist($related);\n        $this->_em->persist($child);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $relatedId = $related->getId();\n\n        $related2 = $this->_em->find(CTIRelated::class, $relatedId);\n\n        self::assertInstanceOf(CTIRelated::class, $related2);\n        self::assertInstanceOf(CTIChild::class, $related2->getCTIParent());\n        self::assertEquals('hello', $related2->getCTIParent()->getData());\n\n        self::assertSame($related2, $related2->getCTIParent()->getRelated());\n    }\n\n    public function testManyToManyToCTIHierarchy(): void\n    {\n        $mmrel = new CTIRelated2();\n        $child = new CTIChild();\n        $child->setData('child');\n        $mmrel->addCTIChild($child);\n\n        $this->_em->persist($mmrel);\n        $this->_em->persist($child);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $mmrel2 = $this->_em->find($mmrel::class, $mmrel->getId());\n        self::assertFalse($mmrel2->getCTIChildren()->isInitialized());\n        self::assertEquals(1, count($mmrel2->getCTIChildren()));\n        self::assertTrue($mmrel2->getCTIChildren()->isInitialized());\n        self::assertInstanceOf(CTIChild::class, $mmrel2->getCTIChildren()->get(0));\n    }\n}\n\n#[Table(name: 'cti_parents')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['parent' => 'CTIParent', 'child' => 'CTIChild'])]\nclass CTIParent\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'CTIRelated', mappedBy: 'ctiParent')]\n    private CTIRelated|null $related = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getRelated(): CTIRelated\n    {\n        return $this->related;\n    }\n\n    public function setRelated(CTIRelated $related): void\n    {\n        $this->related = $related;\n        $related->setCTIParent($this);\n    }\n}\n\n#[Table(name: 'cti_children')]\n#[Entity]\nclass CTIChild extends CTIParent\n{\n    #[Column(type: 'string', length: 255)]\n    private string|null $data = null;\n\n    public function getData(): string\n    {\n        return $this->data;\n    }\n\n    public function setData(string $data): void\n    {\n        $this->data = $data;\n    }\n}\n\n#[Entity]\nclass CTIRelated\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'CTIParent')]\n    #[JoinColumn(name: 'ctiparent_id', referencedColumnName: 'id')]\n    private CTIParent|null $ctiParent = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getCTIParent(): CTIParent\n    {\n        return $this->ctiParent;\n    }\n\n    public function setCTIParent(CTIParent $ctiParent): void\n    {\n        $this->ctiParent = $ctiParent;\n    }\n}\n\n#[Entity]\nclass CTIRelated2\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, CTIChild> */\n    #[ManyToMany(targetEntity: 'CTIChild')]\n    private $ctiChildren;\n\n    public function __construct()\n    {\n        $this->ctiChildren = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function addCTIChild(CTIChild $child): void\n    {\n        $this->ctiChildren->add($child);\n    }\n\n    /** @phpstan-return Collection<int, CTIChild> */\n    public function getCTIChildren(): Collection\n    {\n        return $this->ctiChildren;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ClassTableInheritanceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEvent;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyOrganization;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\Company\\CompanyRaffle;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function get_debug_type;\nuse function sprintf;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy.\n */\nclass ClassTableInheritanceTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function testCRUD(): void\n    {\n        $person = new CompanyPerson();\n        $person->setName('Roman S. Borschel');\n\n        $this->_em->persist($person);\n\n        $employee = new CompanyEmployee();\n        $employee->setName('Roman S. Borschel');\n        $employee->setSalary(100000);\n        $employee->setDepartment('IT');\n\n        $this->_em->persist($employee);\n\n        $employee->setName('Guilherme Blanco');\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select p from ' . CompanyPerson::class . ' p order by p.name desc');\n\n        $entities = $query->getResult();\n\n        self::assertCount(2, $entities);\n        self::assertInstanceOf(CompanyPerson::class, $entities[0]);\n        self::assertInstanceOf(CompanyEmployee::class, $entities[1]);\n        self::assertIsNumeric($entities[0]->getId());\n        self::assertIsNumeric($entities[1]->getId());\n        self::assertEquals('Roman S. Borschel', $entities[0]->getName());\n        self::assertEquals('Guilherme Blanco', $entities[1]->getName());\n        self::assertEquals(100000, $entities[1]->getSalary());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select p from ' . CompanyEmployee::class . ' p');\n\n        $entities = $query->getResult();\n\n        self::assertCount(1, $entities);\n        self::assertInstanceOf(CompanyEmployee::class, $entities[0]);\n        self::assertIsNumeric($entities[0]->getId());\n        self::assertEquals('Guilherme Blanco', $entities[0]->getName());\n        self::assertEquals(100000, $entities[0]->getSalary());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n\n        $this->_em->clear();\n\n        $guilherme = $this->_em->getRepository($employee::class)->findOneBy(['name' => 'Guilherme Blanco']);\n        self::assertInstanceOf(CompanyEmployee::class, $guilherme);\n        self::assertEquals('Guilherme Blanco', $guilherme->getName());\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('update ' . CompanyEmployee::class . \" p set p.name = ?1, p.department = ?2 where p.name='Guilherme Blanco' and p.salary = ?3\");\n        $query->setParameter(1, 'NewName', 'string');\n        $query->setParameter(2, 'NewDepartment');\n        $query->setParameter(3, 100000);\n        $query->getSQL();\n        $numUpdated = $query->execute();\n        self::assertEquals(1, $numUpdated);\n\n        $query      = $this->_em->createQuery('delete from ' . CompanyPerson::class . ' p');\n        $numDeleted = $query->execute();\n        self::assertEquals(2, $numDeleted);\n    }\n\n    public function testMultiLevelUpdateAndFind(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('Roman S. Borschel');\n        $manager->setSalary(100000);\n        $manager->setDepartment('IT');\n        $manager->setTitle('CTO');\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        $manager->setName('Roman B.');\n        $manager->setSalary(119000);\n        $manager->setTitle('CEO');\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $manager = $this->_em->find(CompanyManager::class, $manager->getId());\n\n        self::assertInstanceOf(CompanyManager::class, $manager);\n        self::assertEquals('Roman B.', $manager->getName());\n        self::assertEquals(119000, $manager->getSalary());\n        self::assertEquals('CEO', $manager->getTitle());\n        self::assertIsNumeric($manager->getId());\n    }\n\n    public function testFindOnBaseClass(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('Roman S. Borschel');\n        $manager->setSalary(100000);\n        $manager->setDepartment('IT');\n        $manager->setTitle('CTO');\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $person = $this->_em->find(CompanyPerson::class, $manager->getId());\n\n        self::assertInstanceOf(CompanyManager::class, $person);\n        self::assertEquals('Roman S. Borschel', $person->getName());\n        self::assertEquals(100000, $person->getSalary());\n        self::assertEquals('CTO', $person->getTitle());\n        self::assertIsNumeric($person->getId());\n    }\n\n    public function testSelfReferencingOneToOne(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('John Smith');\n        $manager->setSalary(100000);\n        $manager->setDepartment('IT');\n        $manager->setTitle('CTO');\n\n        $wife = new CompanyPerson();\n        $wife->setName('Mary Smith');\n        $wife->setSpouse($manager);\n\n        self::assertSame($manager, $wife->getSpouse());\n        self::assertSame($wife, $manager->getSpouse());\n\n        $this->_em->persist($manager);\n        $this->_em->persist($wife);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select p, s from ' . CompanyPerson::class . ' p join p.spouse s where p.name=\\'Mary Smith\\'');\n\n        $result = $query->getResult();\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CompanyPerson::class, $result[0]);\n        self::assertEquals('Mary Smith', $result[0]->getName());\n        self::assertInstanceOf(CompanyEmployee::class, $result[0]->getSpouse());\n        self::assertEquals('John Smith', $result[0]->getSpouse()->getName());\n        self::assertSame($result[0], $result[0]->getSpouse()->getSpouse());\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testSelfReferencingManyToMany(): void\n    {\n        $person1 = new CompanyPerson();\n        $person1->setName('Roman');\n\n        $person2 = new CompanyPerson();\n        $person2->setName('Jonathan');\n\n        $person1->addFriend($person2);\n\n        self::assertCount(1, $person1->getFriends());\n        self::assertCount(1, $person2->getFriends());\n\n        $this->_em->persist($person1);\n        $this->_em->persist($person2);\n\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select p, f from ' . CompanyPerson::class . ' p join p.friends f where p.name=?1');\n        $query->setParameter(1, 'Roman');\n\n        $result = $query->getResult();\n        self::assertCount(1, $result);\n        self::assertCount(1, $result[0]->getFriends());\n        self::assertEquals('Roman', $result[0]->getName());\n\n        $friends = $result[0]->getFriends();\n        self::assertEquals('Jonathan', $friends[0]->getName());\n    }\n\n    public function testLazyLoading1(): void\n    {\n        $org    = new CompanyOrganization();\n        $event1 = new CompanyAuction();\n        $event1->setData('auction');\n        $org->addEvent($event1);\n        $event2 = new CompanyRaffle();\n        $event2->setData('raffle');\n        $org->addEvent($event2);\n\n        $this->_em->persist($org);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $orgId = $org->getId();\n\n        $q = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\Company\\CompanyOrganization a where a.id = ?1');\n        $q->setParameter(1, $orgId);\n\n        $result = $q->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CompanyOrganization::class, $result[0]);\n        self::assertNull($result[0]->getMainEvent());\n\n        $events = $result[0]->getEvents();\n\n        self::assertInstanceOf(PersistentCollection::class, $events);\n        self::assertFalse($events->isInitialized());\n\n        self::assertCount(2, $events);\n        if ($events[0] instanceof CompanyAuction) {\n            self::assertInstanceOf(CompanyRaffle::class, $events[1]);\n        } else {\n            self::assertInstanceOf(CompanyRaffle::class, $events[0]);\n            self::assertInstanceOf(CompanyAuction::class, $events[1]);\n        }\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($q);\n    }\n\n    public function testLazyLoading2(): void\n    {\n        $org    = new CompanyOrganization();\n        $event1 = new CompanyAuction();\n        $event1->setData('auction');\n        $org->setMainEvent($event1);\n\n        $this->_em->persist($org);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('select a from ' . CompanyEvent::class . ' a where a.id = ?1');\n        $q->setParameter(1, $event1->getId());\n\n        $result = $q->getResult();\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CompanyAuction::class, $result[0], sprintf('Is of class %s', get_debug_type($result[0])));\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($q);\n\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('select a from ' . CompanyOrganization::class . ' a where a.id = ?1');\n        $q->setParameter(1, $org->getId());\n\n        $result = $q->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CompanyOrganization::class, $result[0]);\n\n        $mainEvent = $result[0]->getMainEvent();\n        // mainEvent should have been loaded because it can't be lazy\n        self::assertInstanceOf(CompanyAuction::class, $mainEvent);\n        self::assertFalse($this->isUninitializedObject($mainEvent));\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($q);\n    }\n\n    #[Group('DDC-368')]\n    public function testBulkUpdateIssueDDC368(): void\n    {\n        $this->_em->createQuery('UPDATE ' . CompanyEmployee::class . ' AS p SET p.salary = 1')\n                  ->execute();\n\n        $query  = $this->_em->createQuery('SELECT count(p.id) FROM ' . CompanyEmployee::class . ' p WHERE p.salary = 1');\n        $result = $query->getResult();\n\n        self::assertGreaterThan(0, $result);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    #[Group('DDC-1341')]\n    #[DoesNotPerformAssertions]\n    public function testBulkUpdateNonScalarParameterDDC1341(): void\n    {\n        $this->_em->createQuery('UPDATE ' . CompanyEmployee::class . ' AS p SET p.startDate = ?0 WHERE p.department = ?1')\n                  ->setParameter(0, new DateTime())\n                  ->setParameter(1, 'IT')\n                  ->execute();\n    }\n\n    #[Group('DDC-130')]\n    public function testDeleteJoinTableRecords(): void\n    {\n        $employee1 = new CompanyEmployee();\n        $employee1->setName('gblanco');\n        $employee1->setSalary(0);\n        $employee1->setDepartment('IT');\n\n        $employee2 = new CompanyEmployee();\n        $employee2->setName('jwage');\n        $employee2->setSalary(0);\n        $employee2->setDepartment('IT');\n\n        $employee1->addFriend($employee2);\n\n        $this->_em->persist($employee1);\n        $this->_em->persist($employee2);\n        $this->_em->flush();\n\n        $employee1Id = $employee1->getId();\n\n        $this->_em->remove($employee1);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find($employee1::class, $employee1Id));\n    }\n\n    #[Group('DDC-728')]\n    public function testQueryForInheritedSingleValuedAssociation(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        $person = new CompanyPerson();\n        $person->setName('spouse');\n\n        $manager->setSpouse($person);\n\n        $this->_em->persist($manager);\n        $this->_em->persist($person);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dqlManager = $this->_em->createQuery('SELECT m FROM ' . CompanyManager::class . ' m WHERE m.spouse = ?1')\n                                ->setParameter(1, $person->getId())\n                                ->getSingleResult();\n\n        self::assertEquals($manager->getId(), $dqlManager->getId());\n        self::assertEquals($person->getId(), $dqlManager->getSpouse()->getId());\n    }\n\n    #[Group('DDC-817')]\n    public function testFindByAssociation(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        $person = new CompanyPerson();\n        $person->setName('spouse');\n\n        $manager->setSpouse($person);\n\n        $this->_em->persist($manager);\n        $this->_em->persist($person);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $repos    = $this->_em->getRepository(CompanyManager::class);\n        $pmanager = $repos->findOneBy(['spouse' => $person->getId()]);\n\n        self::assertEquals($manager->getId(), $pmanager->getId());\n\n        $repos    = $this->_em->getRepository(CompanyPerson::class);\n        $pmanager = $repos->findOneBy(['spouse' => $person->getId()]);\n\n        self::assertEquals($manager->getId(), $pmanager->getId());\n    }\n\n    #[Group('DDC-834')]\n    public function testGetReferenceEntityWithSubclasses(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $ref = $this->_em->getReference(CompanyPerson::class, $manager->getId());\n        self::assertFalse($this->isUninitializedObject($ref), 'Cannot Request a proxy from a class that has subclasses.');\n        self::assertInstanceOf(CompanyPerson::class, $ref);\n        self::assertInstanceOf(CompanyEmployee::class, $ref, 'Direct fetch of the reference has to load the child class Employee directly.');\n        $this->_em->clear();\n\n        $ref = $this->_em->getReference(CompanyManager::class, $manager->getId());\n        self::assertTrue($this->isUninitializedObject($ref), 'A proxy can be generated only if no subclasses exists for the requested reference.');\n    }\n\n    #[Group('DDC-992')]\n    public function testGetSubClassManyToManyCollection(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        $person = new CompanyPerson();\n        $person->setName('friend');\n\n        $manager->addFriend($person);\n\n        $this->_em->persist($manager);\n        $this->_em->persist($person);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $manager = $this->_em->find(CompanyManager::class, $manager->getId());\n\n        self::assertCount(1, $manager->getFriends());\n    }\n\n    #[Group('DDC-1777')]\n    public function testExistsSubclass(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        self::assertFalse($this->_em->getUnitOfWork()->getEntityPersister($manager::class)->exists($manager));\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        self::assertTrue($this->_em->getUnitOfWork()->getEntityPersister($manager::class)->exists($manager));\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatching(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('gblanco');\n        $manager->setSalary(1234);\n        $manager->setTitle('Awesome!');\n        $manager->setDepartment('IT');\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        $repository = $this->_em->getRepository(CompanyEmployee::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('department', 'IT'),\n        ));\n        self::assertCount(1, $users);\n\n        $repository = $this->_em->getRepository(CompanyManager::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('department', 'IT'),\n        ));\n        self::assertCount(1, $users);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ClearEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\OnClearEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * ClearEventTest\n */\nclass ClearEventTest extends OrmFunctionalTestCase\n{\n    public function testEventIsCalledOnClear(): void\n    {\n        $listener = new OnClearListener();\n        $this->_em->getEventManager()->addEventListener(Events::onClear, $listener);\n\n        $this->_em->clear();\n\n        self::assertTrue($listener->called);\n    }\n}\n\nclass OnClearListener\n{\n    /** @var bool */\n    public $called = false;\n\n    public function onClear(OnClearEventArgs $args): void\n    {\n        $this->called = true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CompositeKeyRelations\\CustomerClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyRelations\\InvoiceClass;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass CompositeKeyRelationsTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('compositekeyrelations');\n\n        parent::setUp();\n    }\n\n    public function testFindEntityWithNotNullRelation(): void\n    {\n        $this->_em->getConnection()->insert('CustomerClass', [\n            'companyCode' => 'AA',\n            'code' => 'CUST1',\n            'name' => 'Customer 1',\n        ]);\n\n        $this->_em->getConnection()->insert('InvoiceClass', [\n            'companyCode' => 'AA',\n            'invoiceNumber' => 'INV1',\n            'customerCode' => 'CUST1',\n        ]);\n\n        $entity = $this->findEntity('AA', 'INV1');\n        self::assertSame('AA', $entity->companyCode);\n        self::assertSame('INV1', $entity->invoiceNumber);\n        self::assertInstanceOf(CustomerClass::class, $entity->customer);\n        self::assertSame('Customer 1', $entity->customer->name);\n    }\n\n    public function testFindEntityWithNullRelation(): void\n    {\n        $this->_em->getConnection()->insert('InvoiceClass', [\n            'companyCode' => 'BB',\n            'invoiceNumber' => 'INV1',\n        ]);\n\n        $entity = $this->findEntity('BB', 'INV1');\n        self::assertSame('BB', $entity->companyCode);\n        self::assertSame('INV1', $entity->invoiceNumber);\n        self::assertNull($entity->customer);\n    }\n\n    private function findEntity(string $companyCode, string $invoiceNumber): InvoiceClass\n    {\n        return $this->_em->find(\n            InvoiceClass::class,\n            ['companyCode' => $companyCode, 'invoiceNumber' => $invoiceNumber],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CompositePrimaryKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Exception\\MissingIdentifierField;\nuse Doctrine\\ORM\\Exception\\UnrecognizedIdentifierFields;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\Navigation\\NavCountry;\nuse Doctrine\\Tests\\Models\\Navigation\\NavPhotos;\nuse Doctrine\\Tests\\Models\\Navigation\\NavPointOfInterest;\nuse Doctrine\\Tests\\Models\\Navigation\\NavTour;\nuse Doctrine\\Tests\\Models\\Navigation\\NavUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass CompositePrimaryKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('navigation');\n\n        parent::setUp();\n    }\n\n    public function putGermanysBrandenburderTor(): void\n    {\n        $country = new NavCountry('Germany');\n        $this->_em->persist($country);\n        $poi = new NavPointOfInterest(100, 200, 'Brandenburger Tor', $country);\n        $this->_em->persist($poi);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function putTripAroundEurope(): NavTour\n    {\n        $poi = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n\n        $tour = new NavTour('Trip around Europe');\n        $tour->addPointOfInterest($poi);\n\n        $this->_em->persist($tour);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return $tour;\n    }\n\n    public function testPersistCompositePkEntity(): void\n    {\n        $this->putGermanysBrandenburderTor();\n\n        $poi = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n\n        self::assertInstanceOf(NavPointOfInterest::class, $poi);\n        self::assertEquals(100, $poi->getLat());\n        self::assertEquals(200, $poi->getLong());\n        self::assertEquals('Brandenburger Tor', $poi->getName());\n    }\n\n    #[Group('DDC-1651')]\n    public function testSetParameterCompositeKeyObject(): void\n    {\n        $this->putGermanysBrandenburderTor();\n\n        $poi   = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n        $photo = new NavPhotos($poi, 'asdf');\n        $this->_em->persist($photo);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql = 'SELECT t FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos t WHERE t.poi = ?1';\n\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('A single-valued association path expression to an entity with a composite primary key is not supported.');\n\n        $sql = $this->_em->createQuery($dql)->getSQL();\n    }\n\n    public function testIdentityFunctionWithCompositePrimaryKey(): void\n    {\n        $this->putGermanysBrandenburderTor();\n\n        $poi   = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n        $photo = new NavPhotos($poi, 'asdf');\n        $this->_em->persist($photo);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql    = \"SELECT IDENTITY(p.poi, 'long') AS long, IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos p\";\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertEquals(200, $result[0]['long']);\n        self::assertEquals(100, $result[0]['lat']);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testManyToManyCompositeRelation(): void\n    {\n        $this->putGermanysBrandenburderTor();\n        $tour = $this->putTripAroundEurope();\n\n        $tour = $this->_em->find(NavTour::class, $tour->getId());\n\n        self::assertCount(1, $tour->getPointOfInterests());\n    }\n\n    public function testCompositeDqlEagerFetching(): void\n    {\n        $this->putGermanysBrandenburderTor();\n        $this->putTripAroundEurope();\n\n        $dql   = 'SELECT t, p, c FROM Doctrine\\Tests\\Models\\Navigation\\NavTour t ' .\n               'INNER JOIN t.pois p INNER JOIN p.country c';\n        $tours = $this->_em->createQuery($dql)->getResult();\n\n        $query = $this->_em->createQuery($dql);\n        $tours = $query->getResult();\n\n        self::assertCount(1, $tours);\n\n        $pois = $tours[0]->getPointOfInterests();\n\n        self::assertCount(1, $pois);\n        self::assertEquals('Brandenburger Tor', $pois[0]->getName());\n    }\n\n    public function testCompositeCollectionMemberExpression(): void\n    {\n        self::markTestSkipped('How to test this?');\n\n        $this->putGermanysBrandenburderTor();\n        $this->putTripAroundEurope();\n\n        $dql   = 'SELECT t FROM Doctrine\\Tests\\Models\\Navigation\\NavTour t, Doctrine\\Tests\\Models\\Navigation\\NavPointOfInterest p ' .\n               'WHERE p MEMBER OF t.pois';\n        $tours = $this->_em->createQuery($dql)\n                           ->getResult();\n\n        self::assertCount(1, $tours);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testSpecifyUnknownIdentifierPrimaryKeyFails(): void\n    {\n        $this->expectException(MissingIdentifierField::class);\n        $this->expectExceptionMessage('The identifier long is missing for a query of Doctrine\\Tests\\Models\\Navigation\\NavPointOfInterest');\n\n        $poi = $this->_em->find(NavPointOfInterest::class, ['key1' => 100]);\n    }\n\n    public function testUnrecognizedIdentifierFieldsOnGetReference(): void\n    {\n        $this->expectException(UnrecognizedIdentifierFields::class);\n        $this->expectExceptionMessage('Unrecognized identifier fields: \"key1\"');\n\n        $poi = $this->_em->getReference(NavPointOfInterest::class, ['lat' => 10, 'long' => 20, 'key1' => 100]);\n    }\n\n    #[Group('DDC-1939')]\n    public function testDeleteCompositePersistentCollection(): void\n    {\n        $this->putGermanysBrandenburderTor();\n\n        $poi = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n        $poi->addVisitor(new NavUser('test1'));\n        $poi->addVisitor(new NavUser('test2'));\n\n        $this->_em->flush();\n\n        $poi->getVisitors()->clear();\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $poi = $this->_em->find(NavPointOfInterest::class, ['lat' => 100, 'long' => 200]);\n        self::assertCount(0, $poi->getVisitors());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CompositePrimaryKeyWithAssociationsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1AlternateName;\nuse Doctrine\\Tests\\Models\\GeoNames\\Country;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass CompositePrimaryKeyWithAssociationsTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('geonames');\n\n        parent::setUp();\n\n        $it = new Country('IT', 'Italy');\n\n        $this->_em->persist($it);\n        $this->_em->flush();\n\n        $admin1 = new Admin1(1, 'Rome', $it);\n\n        $this->_em->persist($admin1);\n        $this->_em->flush();\n\n        $name1 = new Admin1AlternateName(1, 'Roma', $admin1);\n        $name2 = new Admin1AlternateName(2, 'Rome', $admin1);\n\n        $admin1->names[] = $name1;\n        $admin1->names[] = $name2;\n\n        $this->_em->persist($admin1);\n        $this->_em->persist($name1);\n        $this->_em->persist($name2);\n\n        $this->_em->flush();\n\n        $this->_em->clear();\n    }\n\n    public function testFindByAbleToGetCompositeEntitiesWithMixedTypeIdentifiers(): void\n    {\n        $admin1Repo      = $this->_em->getRepository(Admin1::class);\n        $admin1NamesRepo = $this->_em->getRepository(Admin1AlternateName::class);\n\n        $admin1Rome = $admin1Repo->findOneBy(['country' => 'IT', 'id' => 1]);\n\n        $names = $admin1NamesRepo->findBy(['admin1' => $admin1Rome]);\n        self::assertCount(2, $names);\n\n        $name1 = $admin1NamesRepo->findOneBy(['admin1' => $admin1Rome, 'id' => 1]);\n        $name2 = $admin1NamesRepo->findOneBy(['admin1' => $admin1Rome, 'id' => 2]);\n\n        self::assertEquals(1, $name1->id);\n        self::assertEquals('Roma', $name1->name);\n\n        self::assertEquals(2, $name2->id);\n        self::assertEquals('Rome', $name2->name);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CustomFunctionsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Query\\AST\\AggregateExpression;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nrequire_once __DIR__ . '/../../TestInit.php';\n\nclass CustomFunctionsTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testCustomFunctionDefinedWithCallback(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Bob';\n        $user->username = 'Dylan';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        // Instead of defining the function with the class name, we use a callback\n        $this->_em->getConfiguration()->addCustomStringFunction('FOO', static fn ($funcName) => new NoOp($funcName));\n        $this->_em->getConfiguration()->addCustomNumericFunction('BAR', static fn ($funcName) => new NoOp($funcName));\n\n        $query = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'\n            . ' WHERE FOO(u.name) = \\'Bob\\''\n            . ' AND BAR(1) = 1');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertSame($user, $users[0]);\n    }\n\n    public function testCustomFunctionOverride(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Bob';\n        $user->username = 'Dylan';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->getConfiguration()->addCustomStringFunction('COUNT', 'Doctrine\\Tests\\ORM\\Functional\\CustomCount');\n\n        $query = $this->_em->createQuery('SELECT COUNT(DISTINCT u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $usersCount = $query->getSingleScalarResult();\n\n        self::assertEquals(1, $usersCount);\n    }\n}\n\nclass NoOp extends FunctionNode\n{\n    /** @var PathExpression */\n    private $field;\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n        $this->field = $parser->ArithmeticPrimary();\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->field->dispatch($sqlWalker);\n    }\n}\n\nclass CustomCount extends FunctionNode\n{\n    private AggregateExpression|null $aggregateExpression = null;\n\n    public function parse(Parser $parser): void\n    {\n        $this->aggregateExpression = $parser->AggregateExpression();\n    }\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return $this->aggregateExpression->dispatch($sqlWalker);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/CustomIdObjectTypeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObjectType;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeChild;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeParent;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass CustomIdObjectTypeTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        if (DBALType::hasType(CustomIdObjectType::NAME)) {\n            DBALType::overrideType(CustomIdObjectType::NAME, CustomIdObjectType::class);\n        } else {\n            DBALType::addType(CustomIdObjectType::NAME, CustomIdObjectType::class);\n        }\n\n        $this->useModelSet('custom_id_object_type');\n\n        parent::setUp();\n    }\n\n    public function testFindByCustomIdObject(): void\n    {\n        $parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        $result = $this->_em->find(CustomIdObjectTypeParent::class, $parent->id);\n\n        self::assertSame($parent, $result);\n    }\n\n    #[Group('DDC-3622')]\n    #[Group('1336')]\n    public function testFetchJoinCustomIdObject(): void\n    {\n        $parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));\n\n        $parent->children->add(new CustomIdObjectTypeChild(new CustomIdObject('bar'), $parent));\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        $result = $this\n            ->_em\n            ->createQuery(\n                'SELECT parent, children FROM '\n                . CustomIdObjectTypeParent::class\n                . ' parent LEFT JOIN parent.children children',\n            )\n            ->getResult();\n\n        self::assertCount(1, $result);\n        self::assertSame($parent, $result[0]);\n    }\n\n    #[Group('DDC-3622')]\n    #[Group('1336')]\n    public function testFetchJoinWhereCustomIdObject(): void\n    {\n        $parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));\n\n        $parent->children->add(new CustomIdObjectTypeChild(new CustomIdObject('bar'), $parent));\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        // note: hydration is willingly broken in this example:\n        $result = $this\n            ->_em\n            ->createQuery(\n                'SELECT parent, children FROM '\n                . CustomIdObjectTypeParent::class\n                . ' parent LEFT JOIN parent.children children '\n                . 'WHERE children.id = ?1',\n            )\n            ->setParameter(1, $parent->children->first()->id)\n            ->getResult();\n\n        self::assertCount(1, $result);\n        self::assertSame($parent, $result[0]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DatabaseDriverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLServerPlatform;\nuse Doctrine\\DBAL\\Schema\\AbstractSchemaManager;\nuse Doctrine\\DBAL\\Schema\\Name\\Identifier;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_change_key_case;\nuse function class_exists;\nuse function count;\nuse function strtolower;\n\nuse const CASE_LOWER;\n\nclass DatabaseDriverTest extends DatabaseDriverTestCase\n{\n    /** @var AbstractSchemaManager */\n    protected $schemaManager = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->schemaManager = $this->createSchemaManager();\n    }\n\n    #[Group('DDC-2059')]\n    public function testIssue2059(): void\n    {\n        $user = new Table('ddc2059_user');\n        $user->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $user->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $user->setPrimaryKey(['id']);\n        }\n\n        $project = new Table('ddc2059_project');\n        $project->addColumn('id', 'integer');\n        $project->addColumn('user_id', 'integer');\n        $project->addColumn('user', 'string');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $project->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $project->setPrimaryKey(['id']);\n        }\n\n        $project->addForeignKeyConstraint('ddc2059_user', ['user_id'], ['id']);\n\n        $metadata = $this->convertToClassMetadata([$project, $user], []);\n\n        self::assertTrue(isset($metadata['Ddc2059Project']->fieldMappings['user']));\n        self::assertTrue(isset($metadata['Ddc2059Project']->associationMappings['user2']));\n    }\n\n    public function testLoadMetadataFromDatabase(): void\n    {\n        $table = new Table('dbdriver_foo');\n        $table->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $table->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $table->setPrimaryKey(['id']);\n        }\n\n        $table->addColumn('bar', 'string', ['notnull' => false, 'length' => 200]);\n\n        $this->dropAndCreateTable($table);\n\n        $metadatas = $this->extractClassMetadata(['DbdriverFoo']);\n\n        self::assertArrayHasKey('DbdriverFoo', $metadatas);\n        $metadata = $metadatas['DbdriverFoo'];\n\n        self::assertArrayHasKey('id', $metadata->fieldMappings);\n        self::assertEquals('id', $metadata->fieldMappings['id']->fieldName);\n        self::assertEquals('id', strtolower($metadata->fieldMappings['id']->columnName));\n        self::assertEquals('integer', (string) $metadata->fieldMappings['id']->type);\n\n        self::assertArrayHasKey('bar', $metadata->fieldMappings);\n        self::assertEquals('bar', $metadata->fieldMappings['bar']->fieldName);\n        self::assertEquals('bar', strtolower($metadata->fieldMappings['bar']->columnName));\n        self::assertEquals('string', (string) $metadata->fieldMappings['bar']->type);\n        self::assertEquals(200, $metadata->fieldMappings['bar']->length);\n        self::assertTrue($metadata->fieldMappings['bar']->nullable);\n    }\n\n    public function testLoadMetadataWithForeignKeyFromDatabase(): void\n    {\n        $tableB = new Table('dbdriver_bar');\n        $tableB->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $tableB->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $tableB->setPrimaryKey(['id']);\n        }\n\n        $this->dropAndCreateTable($tableB);\n\n        $tableA = new Table('dbdriver_baz');\n        $tableA->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $tableA->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $tableA->setPrimaryKey(['id']);\n        }\n\n        $tableA->addColumn('bar_id', 'integer');\n        $tableA->addForeignKeyConstraint('dbdriver_bar', ['bar_id'], ['id']);\n\n        $this->dropAndCreateTable($tableA);\n\n        $metadatas = $this->extractClassMetadata(['DbdriverBar', 'DbdriverBaz']);\n\n        self::assertArrayHasKey('DbdriverBaz', $metadatas);\n        $bazMetadata = $metadatas['DbdriverBaz'];\n\n        self::assertArrayNotHasKey('barId', $bazMetadata->fieldMappings, \"The foreign Key field should not be inflected as 'barId' field, its an association.\");\n        self::assertArrayHasKey('id', $bazMetadata->fieldMappings);\n\n        $bazMetadata->associationMappings = array_change_key_case($bazMetadata->associationMappings, CASE_LOWER);\n\n        self::assertArrayHasKey('bar', $bazMetadata->associationMappings);\n        self::assertTrue($bazMetadata->associationMappings['bar']->isManyToOne());\n    }\n\n    public function testDetectManyToManyTables(): void\n    {\n        $metadatas = $this->extractClassMetadata(['CmsUsers', 'CmsGroups', 'CmsTags']);\n\n        self::assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers entity was not detected.');\n        self::assertArrayHasKey('CmsGroups', $metadatas, 'CmsGroups entity was not detected.');\n        self::assertArrayHasKey('CmsTags', $metadatas, 'CmsTags entity was not detected.');\n\n        self::assertEquals(3, count($metadatas['CmsUsers']->associationMappings));\n        self::assertArrayHasKey('group', $metadatas['CmsUsers']->associationMappings);\n        self::assertEquals(1, count($metadatas['CmsGroups']->associationMappings));\n        self::assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);\n        self::assertEquals(1, count($metadatas['CmsTags']->associationMappings));\n        self::assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);\n    }\n\n    public function testIgnoreManyToManyTableWithoutFurtherForeignKeyDetails(): void\n    {\n        $tableB = new Table('dbdriver_bar');\n        $tableB->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $tableB->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $tableB->setPrimaryKey(['id']);\n        }\n\n        $tableA = new Table('dbdriver_baz');\n        $tableA->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $tableA->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $tableA->setPrimaryKey(['id']);\n        }\n\n        $tableMany = new Table('dbdriver_bar_baz');\n        $tableMany->addColumn('bar_id', 'integer');\n        $tableMany->addColumn('baz_id', 'integer');\n        $tableMany->addForeignKeyConstraint('dbdriver_bar', ['bar_id'], ['id']);\n\n        $metadatas = $this->convertToClassMetadata([$tableA, $tableB], [$tableMany]);\n\n        self::assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), 'no association mappings should be detected.');\n    }\n\n    public function testLoadMetadataFromDatabaseDetail(): void\n    {\n        $table = new Table('dbdriver_foo');\n\n        $table->addColumn('id', 'integer', ['unsigned' => true]);\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $table->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $table->setPrimaryKey(['id']);\n        }\n\n        $table->addColumn('column_unsigned', 'integer', ['unsigned' => true]);\n        $table->addColumn('column_comment', 'string', ['length' => 16, 'comment' => 'test_comment']);\n        $table->addColumn('column_default', 'string', ['length' => 16, 'default' => 'test_default']);\n        $table->addColumn('column_decimal', 'decimal', ['precision' => 4, 'scale' => 3]);\n\n        $table->addColumn('column_index1', 'string', ['length' => 16]);\n        $table->addColumn('column_index2', 'string', ['length' => 16]);\n        $table->addIndex(['column_index1', 'column_index2'], 'index1');\n\n        $table->addColumn('column_unique_index1', 'string', ['length' => 16]);\n        $table->addColumn('column_unique_index2', 'string', ['length' => 16]);\n        $table->addUniqueIndex(['column_unique_index1', 'column_unique_index2'], 'unique_index1');\n\n        $this->dropAndCreateTable($table);\n\n        $metadatas = $this->extractClassMetadata(['DbdriverFoo']);\n\n        self::assertArrayHasKey('DbdriverFoo', $metadatas);\n\n        $metadata = $metadatas['DbdriverFoo'];\n\n        self::assertArrayHasKey('id', $metadata->fieldMappings);\n        self::assertEquals('id', $metadata->fieldMappings['id']->fieldName);\n        self::assertEquals('id', strtolower($metadata->fieldMappings['id']->columnName));\n        self::assertEquals('integer', (string) $metadata->fieldMappings['id']->type);\n\n        if (self::supportsUnsignedInteger($this->_em->getConnection()->getDatabasePlatform())) {\n            self::assertArrayHasKey('columnUnsigned', $metadata->fieldMappings);\n            self::assertTrue($metadata->fieldMappings['columnUnsigned']->options['unsigned']);\n        }\n\n        self::assertArrayHasKey('columnComment', $metadata->fieldMappings);\n        self::assertEquals('test_comment', $metadata->fieldMappings['columnComment']->options['comment']);\n\n        self::assertArrayHasKey('columnDefault', $metadata->fieldMappings);\n        self::assertEquals('test_default', $metadata->fieldMappings['columnDefault']->options['default']);\n\n        self::assertArrayHasKey('columnDecimal', $metadata->fieldMappings);\n        self::assertEquals(4, $metadata->fieldMappings['columnDecimal']->precision);\n        self::assertEquals(3, $metadata->fieldMappings['columnDecimal']->scale);\n\n        self::assertNotEmpty($metadata->table['indexes']['index1']['columns']);\n        self::assertEquals(\n            ['column_index1', 'column_index2'],\n            $metadata->table['indexes']['index1']['columns'],\n        );\n\n        self::assertNotEmpty($metadata->table['uniqueConstraints']['unique_index1']['columns']);\n        self::assertEquals(\n            ['column_unique_index1', 'column_unique_index2'],\n            $metadata->table['uniqueConstraints']['unique_index1']['columns'],\n        );\n    }\n\n    private static function supportsUnsignedInteger(AbstractPlatform $platform): bool\n    {\n        // FIXME: Condition here is fugly.\n        // NOTE: PostgreSQL and SQL SERVER do not support UNSIGNED integer\n\n        return ! $platform instanceof SQLServerPlatform\n            && ! $platform instanceof PostgreSQLPlatform;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DatabaseDriverTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function array_keys;\nuse function array_map;\nuse function count;\nuse function implode;\nuse function in_array;\nuse function strtolower;\n\n/**\n * Common BaseClass for DatabaseDriver Tests\n */\nabstract class DatabaseDriverTestCase extends OrmFunctionalTestCase\n{\n    /** @phpstan-return array<string, ClassMetadata> */\n    protected function convertToClassMetadata(array $entityTables, array $manyTables = []): array\n    {\n        $sm     = $this->createSchemaManager();\n        $driver = new DatabaseDriver($sm);\n        $driver->setTables($entityTables, $manyTables);\n\n        $metadatas = [];\n        foreach ($driver->getAllClassNames() as $className) {\n            $class = new ClassMetadata($className);\n            $driver->loadMetadataForClass($className, $class);\n            $metadatas[$className] = $class;\n        }\n\n        return $metadatas;\n    }\n\n    /**\n     * @param string[] $classNames\n     *\n     * @return array<class-string, ClassMetadata>\n     */\n    protected function extractClassMetadata(array $classNames): array\n    {\n        $classNames = array_map('strtolower', $classNames);\n        $metadatas  = [];\n\n        $sm     = $this->createSchemaManager();\n        $driver = new DatabaseDriver($sm);\n\n        foreach ($driver->getAllClassNames() as $className) {\n            if (! in_array(strtolower($className), $classNames, true)) {\n                continue;\n            }\n\n            $class = new ClassMetadata($className);\n            $driver->loadMetadataForClass($className, $class);\n            $metadatas[$className] = $class;\n        }\n\n        if (count($metadatas) !== count($classNames)) {\n            self::fail(\"Have not found all classes matching the names '\" . implode(', ', $classNames) . \"' only tables \" . implode(', ', array_keys($metadatas)));\n        }\n\n        return $metadatas;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DefaultTimeExpressionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\Attributes\\RequiresMethod;\n\nuse function interface_exists;\n\nclass DefaultTimeExpressionTest extends OrmFunctionalTestCase\n{\n    use VerifyDeprecations;\n\n    #[IgnoreDeprecations]\n    #[RequiresMethod(DefaultExpression::class, 'toSQL')]\n    public function testUsingTimeRelatedDefaultExpressionCausesAnOrmDeprecationAndNoDbalDeprecation(): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if (\n            $platform->getCurrentTimestampSQL() !== 'CURRENT_TIMESTAMP'\n            || $platform->getCurrentTimeSQL() !== 'CURRENT_TIME'\n            || $platform->getCurrentDateSQL() !== 'CURRENT_DATE'\n        ) {\n            $this->markTestSkipped(\n                'This test requires platforms to support exactly CURRENT_TIMESTAMP, CURRENT_TIME and CURRENT_DATE.',\n            );\n        }\n\n        if ($platform instanceof AbstractMySQLPlatform) {\n            $this->markTestSkipped(\n                'MySQL platform does not support CURRENT_TIME or CURRENT_DATE as default expression.',\n            );\n        }\n\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12252');\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/7195');\n\n        $this->createSchemaForModels(LegacyTimeEntity::class);\n        $this->_em->persist($entity = new LegacyTimeEntity());\n        $this->_em->flush();\n        $this->_em->find(LegacyTimeEntity::class, $entity->id);\n    }\n\n    public function testNoDeprecationsAreTrownWhenTheyCannotBeAddressed(): void\n    {\n        if (interface_exists(DefaultExpression::class)) {\n            $this->markTestSkipped(\n                'This test requires Doctrine DBAL 4.3 or lower.',\n            );\n        }\n\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if (\n            $platform->getCurrentTimestampSQL() !== 'CURRENT_TIMESTAMP'\n            || $platform->getCurrentTimeSQL() !== 'CURRENT_TIME'\n            || $platform->getCurrentDateSQL() !== 'CURRENT_DATE'\n        ) {\n            $this->markTestSkipped(\n                'This test requires platforms to support exactly CURRENT_TIMESTAMP, CURRENT_TIME and CURRENT_DATE.',\n            );\n        }\n\n        if ($platform instanceof AbstractMySQLPlatform) {\n            $this->markTestSkipped(\n                'MySQL platform does not support CURRENT_TIME or CURRENT_DATE as default expression.',\n            );\n        }\n\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12252');\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/7195');\n\n        $this->createSchemaForModels(LegacyTimeEntity::class);\n        $this->_em->persist($entity = new LegacyTimeEntity());\n        $this->_em->flush();\n        $this->_em->find(LegacyTimeEntity::class, $entity->id);\n    }\n\n    #[RequiresMethod(DefaultExpression::class, 'toSQL')]\n    public function testUsingDefaultExpressionInstancesCausesNoDeprecation(): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if ($platform instanceof AbstractMySQLPlatform) {\n            $this->markTestSkipped('MySQL platform does not support CURRENT_TIME or CURRENT_DATE as default expression.');\n        }\n\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12252');\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/7195');\n\n        $this->createSchemaForModels(TimeEntity::class);\n        $this->_em->persist($entity = new TimeEntity());\n        $this->_em->flush();\n        $this->_em->find(TimeEntity::class, $entity->id);\n    }\n}\n\n#[ORM\\Entity]\nclass LegacyTimeEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\Column(\n        type: Types::DATETIME_MUTABLE,\n        options: ['default' => 'CURRENT_TIMESTAMP'],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdAt;\n\n    #[ORM\\Column(\n        type: Types::DATETIME_IMMUTABLE,\n        options: ['default' => 'CURRENT_TIMESTAMP'],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTimeImmutable $createdAtImmutable;\n\n    #[ORM\\Column(\n        type: Types::TIME_MUTABLE,\n        options: ['default' => 'CURRENT_TIME'],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdTime;\n\n    #[ORM\\Column(\n        type: Types::DATE_MUTABLE,\n        options: ['default' => 'CURRENT_DATE'],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdDate;\n}\n\n#[ORM\\Entity]\nclass TimeEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\Column(\n        type: Types::DATETIME_MUTABLE,\n        options: ['default' => new CurrentTimestamp()],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdAt;\n\n    #[ORM\\Column(\n        type: Types::DATETIME_IMMUTABLE,\n        options: ['default' => new CurrentTimestamp()],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTimeImmutable $createdAtImmutable;\n\n    #[ORM\\Column(\n        type: Types::TIME_MUTABLE,\n        options: ['default' => new CurrentTime()],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdTime;\n\n    #[ORM\\Column(\n        type: Types::DATE_MUTABLE,\n        options: ['default' => new CurrentDate()],\n        insertable: false,\n        updatable: false,\n    )]\n    public DateTime $createdDate;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DefaultTimeExpressionXmlTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform;\nuse Doctrine\\DBAL\\Schema\\DefaultExpression;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping\\Driver\\XmlDriver;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\Attributes\\RequiresMethod;\n\nclass DefaultTimeExpressionXmlTest extends OrmFunctionalTestCase\n{\n    use VerifyDeprecations;\n\n    #[IgnoreDeprecations]\n    #[RequiresMethod(DefaultExpression::class, 'toSQL')]\n    public function testUsingTimeRelatedDefaultExpressionCausesAnOrmDeprecationAndNoDbalDeprecation(): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if (\n            $platform->getCurrentTimestampSQL() !== 'CURRENT_TIMESTAMP'\n            || $platform->getCurrentTimeSQL() !== 'CURRENT_TIME'\n            || $platform->getCurrentDateSQL() !== 'CURRENT_DATE'\n        ) {\n            $this->markTestSkipped('Platform does not use standard SQL for current time expressions.');\n        }\n\n        if ($platform instanceof AbstractMySQLPlatform) {\n            $this->markTestSkipped(\n                'MySQL platform does not support CURRENT_TIME or CURRENT_DATE as default expression.',\n            );\n        }\n\n        $this->_em         = $this->getEntityManager(\n            mappingDriver: new XmlDriver(__DIR__ . '/../Mapping/xml/'),\n        );\n        $this->_schemaTool = new SchemaTool($this->_em);\n\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12252');\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/7195');\n\n        $this->createSchemaForModels(XmlLegacyTimeEntity::class);\n        $this->_em->persist($entity = new XmlLegacyTimeEntity());\n        $this->_em->flush();\n        $this->_em->find(XmlLegacyTimeEntity::class, $entity->id);\n    }\n\n    #[RequiresMethod(DefaultExpression::class, 'toSQL')]\n    public function testUsingDefaultExpressionInstancesCausesNoDeprecationXmlDriver(): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if ($platform instanceof AbstractMySQLPlatform) {\n            $this->markTestSkipped(\n                'MySQL platform does not support CURRENT_TIME or CURRENT_DATE as default expression.',\n            );\n        }\n\n        $this->_em         = $this->getEntityManager(\n            mappingDriver: new XmlDriver(__DIR__ . '/../Mapping/xml/'),\n        );\n        $this->_schemaTool = new SchemaTool($this->_em);\n\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12252');\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/7195');\n\n        $this->createSchemaForModels(XmlTimeEntity::class);\n        $this->_em->persist($entity = new XmlTimeEntity());\n        $this->_em->flush();\n        $this->_em->clear();\n        $this->_em->find(XmlTimeEntity::class, $entity->id);\n    }\n}\n\nclass XmlLegacyTimeEntity\n{\n    public int $id;\n\n    public DateTime $createdAt;\n    public DateTimeImmutable $createdAtImmutable;\n    public DateTime $createdTime;\n    public DateTime $createdDate;\n}\n\nclass XmlTimeEntity\n{\n    public int $id;\n\n    public DateTime $createdAt;\n    public DateTimeImmutable $createdAtImmutable;\n    public DateTime $createdTime;\n    public DateTime $createdDate;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DefaultValuesTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests basic operations on entities with default values.\n */\nclass DefaultValuesTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DefaultValueUser::class,\n            DefaultValueAddress::class,\n        );\n    }\n\n    #[Group('non-cacheable')]\n    public function testSimpleDetachMerge(): void\n    {\n        $user       = new DefaultValueUser();\n        $user->name = 'romanb';\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userId = $user->id; // e.g. from $_REQUEST\n        $user2  = $this->_em->getReference($user::class, $userId);\n\n        $this->_em->flush();\n        self::assertTrue($this->isUninitializedObject($user2));\n\n        $a          = new DefaultValueAddress();\n        $a->country = 'de';\n        $a->zip     = '12345';\n        $a->city    = 'Berlin';\n        $a->street  = 'Sesamestreet';\n\n        $a->user = $user2;\n        $this->_em->persist($a);\n        $this->_em->flush();\n\n        self::assertTrue($this->isUninitializedObject($user2));\n        $this->_em->clear();\n\n        $a2 = $this->_em->find($a::class, $a->id);\n        self::assertInstanceOf(DefaultValueUser::class, $a2->getUser());\n        self::assertEquals($userId, $a2->getUser()->getId());\n        self::assertEquals('Poweruser', $a2->getUser()->type);\n    }\n}\n\n\n#[Table(name: 'defaultvalueuser')]\n#[Entity]\nclass DefaultValueUser\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name = '';\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $type = 'Poweruser';\n    /** @var DefaultValueAddress */\n    #[OneToOne(targetEntity: 'DefaultValueAddress', mappedBy: 'user', cascade: ['persist'])]\n    public $address;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n/**\n * CmsAddress\n */\n#[Table(name: 'defaultvalueaddresses')]\n#[Entity]\nclass DefaultValueAddress\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 50)]\n    public $country;\n\n    /** @var string */\n    #[Column(type: 'string', length: 50)]\n    public $zip;\n\n    /** @var string */\n    #[Column(type: 'string', length: 50)]\n    public $city;\n\n    /**\n     * @var string\n     * Testfield for Schema Updating Tests.\n     */\n    public $street;\n\n    /** @var DefaultValueUser */\n    #[OneToOne(targetEntity: 'DefaultValueUser')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    public $user;\n\n    public function getUser(): DefaultValueUser\n    {\n        return $this->user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/DetachedEntityTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Description of DetachedEntityTest\n */\nclass DetachedEntityTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-203')]\n    public function testDetachedEntityThrowsExceptionOnFlush(): void\n    {\n        $ph              = new CmsPhonenumber();\n        $ph->phonenumber = '12345';\n\n        $this->_em->persist($ph);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->_em->persist($ph);\n\n        // since it tries to insert the object twice (with the same PK)\n        $this->expectException(UniqueConstraintViolationException::class);\n        $this->_em->flush();\n    }\n\n    #[Group('DDC-822')]\n    public function testUseDetachedEntityAsQueryParameter(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->detach($user);\n\n        $dql   = 'SELECT u FROM ' . CmsUser::class . ' u WHERE u.id = ?1';\n        $query = $this->_em->createQuery($dql);\n        $query->setParameter(1, $user);\n\n        $newUser = $query->getSingleResult();\n\n        self::assertInstanceOf(CmsUser::class, $newUser);\n        self::assertEquals('gblanco', $newUser->username);\n    }\n\n    #[Group('DDC-920')]\n    public function testDetachManagedUnpersistedEntity(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->detach($user);\n\n        $this->_em->flush();\n\n        self::assertFalse($this->_em->contains($user));\n        self::assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EagerFetchCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function count;\nuse function iterator_to_array;\n\nclass EagerFetchCollectionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(EagerFetchOwner::class, EagerFetchChild::class);\n\n        // Ensure tables are empty\n        $this->_em->getRepository(EagerFetchChild::class)->createQueryBuilder('o')->delete()->getQuery()->execute();\n        $this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->delete()->getQuery()->execute();\n    }\n\n    public function testEagerFetchMode(): void\n    {\n        $owner  = $this->createOwnerWithChildren(2);\n        $owner2 = $this->createOwnerWithChildren(3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $owner = $this->_em->find(EagerFetchOwner::class, $owner->id);\n\n        $afterQueryCount = count($this->getQueryLog()->queries);\n        $this->assertCount(2, $owner->children);\n\n        $this->assertQueryCount($afterQueryCount, 'The $owner->children collection should already be initialized by find EagerFetchOwner before.');\n\n        $this->assertCount(3, $this->_em->find(EagerFetchOwner::class, $owner2->id)->children);\n\n        $this->_em->clear();\n\n        $beforeQueryCount = count($this->getQueryLog()->queries);\n        $owners           = $this->_em->getRepository(EagerFetchOwner::class)->findAll();\n\n        $this->assertQueryCount($beforeQueryCount + 2, 'the findAll() + 1 subselect loading both collections of the two returned $owners');\n\n        $this->assertCount(2, $owners[0]->children);\n        $this->assertCount(3, $owners[1]->children);\n\n        $this->assertQueryCount($beforeQueryCount + 2, 'both collections are already initialized and counting them does not make a difference in total query count');\n    }\n\n    public function testEagerFetchModeWithDQL(): void\n    {\n        $owner  = $this->createOwnerWithChildren(2);\n        $owner2 = $this->createOwnerWithChildren(3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT o FROM ' . EagerFetchOwner::class . ' o');\n        $query->setFetchMode(EagerFetchOwner::class, 'children', ORM\\ClassMetadata::FETCH_EAGER);\n\n        $beforeQueryCount = count($this->getQueryLog()->queries);\n        $owners           = $query->getResult();\n        $afterQueryCount  = count($this->getQueryLog()->queries);\n\n        $this->assertEquals($beforeQueryCount + 2, $afterQueryCount);\n\n        $owners[0]->children->count();\n        $owners[1]->children->count();\n\n        $anotherQueryCount = count($this->getQueryLog()->queries);\n\n        $this->assertEquals($anotherQueryCount, $afterQueryCount);\n    }\n\n    public function testSubselectFetchJoinWithNotAllowed(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Associations with fetch-mode=EAGER may not be using WITH conditions');\n\n        $query = $this->_em->createQuery('SELECT o, c FROM ' . EagerFetchOwner::class . ' o JOIN o.children c WITH c.id = 1');\n        $query->getResult();\n    }\n\n    public function testSubselectFetchJoinWithAllowedWhenOverriddenNotEager(): void\n    {\n        $query = $this->_em->createQuery('SELECT o, c FROM ' . EagerFetchOwner::class . ' o JOIN o.children c WITH c.id = 1');\n        $query->setFetchMode(EagerFetchChild::class, 'owner', ORM\\ClassMetadata::FETCH_LAZY);\n\n        $this->assertIsString($query->getSql());\n    }\n\n    public function testSubselectFetchJoinWithAllowedWhenOverriddenNotEagerPaginator(): void\n    {\n        $query = $this->_em->createQuery('SELECT o, c FROM ' . EagerFetchOwner::class . ' o JOIN o.children c WITH c.id = 1');\n        $query->setMaxResults(1);\n        $query->setFetchMode(EagerFetchChild::class, 'owner', ORM\\ClassMetadata::FETCH_LAZY);\n\n        $paginator = new Paginator($query, true);\n        $this->assertIsArray(iterator_to_array($paginator));\n    }\n\n    public function testEagerFetchWithIterable(): void\n    {\n        $this->createOwnerWithChildren(2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $iterable = $this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->getQuery()->toIterable();\n\n        // There is only a single record, but use a foreach to ensure the iterator is marked as finished and the table lock is released\n        foreach ($iterable as $owner) {\n            $this->assertCount(2, $owner->children);\n        }\n    }\n\n    protected function createOwnerWithChildren(int $children): EagerFetchOwner\n    {\n        $owner = new EagerFetchOwner();\n        $this->_em->persist($owner);\n\n        for ($i = 0; $i < $children; $i++) {\n            $child        = new EagerFetchChild();\n            $child->owner = $owner;\n\n            $owner->children->add($child);\n\n            $this->_em->persist($child);\n        }\n\n        return $owner;\n    }\n}\n\n#[ORM\\Entity]\nclass EagerFetchOwner\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    #[ORM\\OneToMany(mappedBy: 'owner', targetEntity: EagerFetchChild::class, fetch: 'EAGER')]\n    public Collection $children;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass EagerFetchChild\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var EagerFetchOwner */\n    #[ORM\\ManyToOne(targetEntity: EagerFetchOwner::class, inversedBy: 'children')]\n    public $owner;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EagerFetchOneToManyWithCompositeKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany\\RootEntity;\nuse Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany\\SecondLevel;\nuse Doctrine\\Tests\\Models\\EagerFetchedCompositeOneToMany\\SecondLevelWithoutCompositePrimaryKey;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class EagerFetchOneToManyWithCompositeKeyTest extends OrmFunctionalTestCase\n{\n    #[Group('GH11154')]\n    public function testItDoesNotThrowAnExceptionWhenTriggeringALoad(): void\n    {\n        $this->setUpEntitySchema([RootEntity::class, SecondLevel::class, SecondLevelWithoutCompositePrimaryKey::class]);\n\n        $a1 = new RootEntity(1, 'A');\n\n        $this->_em->persist($a1);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        self::assertCount(1, $this->_em->getRepository(RootEntity::class)->findAll());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EntityListenersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\PostPersistEventArgs;\nuse Doctrine\\ORM\\Event\\PostRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PreFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PreUpdateEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContractListener;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Assert;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1955')]\nclass EntityListenersTest extends OrmFunctionalTestCase\n{\n    private CompanyContractListener $listener;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->listener = $this->_em->getConfiguration()\n            ->getEntityListenerResolver()\n            ->resolve(CompanyContractListener::class);\n    }\n\n    public function testPreFlushListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(2000);\n\n        $this->listener->preFlushCalls = [];\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->preFlushCalls);\n        self::assertSame($fix, $this->listener->preFlushCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->preFlushCalls[0][0]);\n        self::assertInstanceOf(PreFlushEventArgs::class, $this->listener->preFlushCalls[0][1]);\n    }\n\n    public function testPostLoadListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(2000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->listener->postLoadCalls = [];\n\n        $dql = 'SELECT f FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract f WHERE f.id = ?1';\n        $fix = $this->_em->createQuery($dql)->setParameter(1, $fix->getId())->getSingleResult();\n\n        self::assertCount(1, $this->listener->postLoadCalls);\n        self::assertSame($fix, $this->listener->postLoadCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->postLoadCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->postLoadCalls[0][1]);\n    }\n\n    public function testPrePersistListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(2000);\n\n        $this->listener->prePersistCalls = [];\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->prePersistCalls);\n        self::assertSame($fix, $this->listener->prePersistCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->prePersistCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->prePersistCalls[0][1]);\n    }\n\n    public function testPostPersistListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(2000);\n\n        $this->listener->postPersistCalls = [];\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->postPersistCalls);\n        self::assertSame($fix, $this->listener->postPersistCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->postPersistCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->postPersistCalls[0][1]);\n    }\n\n    public function testPostPersistCalledAfterAllInsertsHaveBeenPerformedAndIdsHaveBeenAssigned(): void\n    {\n        $object1 = new CompanyFixContract();\n        $object1->setFixPrice(2000);\n\n        $object2 = new CompanyPerson();\n        $object2->setName('J. Doe');\n\n        $this->_em->persist($object1);\n        $this->_em->persist($object2);\n\n        $listener = new class ([$object1, $object2]) {\n            /** @var array<object> */\n            private $trackedObjects;\n\n            /** @var int */\n            public $invocationCount = 0;\n\n            public function __construct(array $trackedObjects)\n            {\n                $this->trackedObjects = $trackedObjects;\n            }\n\n            public function postPersist(PostPersistEventArgs $args): void\n            {\n                foreach ($this->trackedObjects as $object) {\n                    Assert::assertNotNull($object->getId());\n                }\n\n                ++$this->invocationCount;\n            }\n        };\n\n        $this->_em->getEventManager()->addEventListener(Events::postPersist, $listener);\n        $this->_em->flush();\n\n        self::assertSame(2, $listener->invocationCount);\n    }\n\n    public function testPreUpdateListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(1000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        $this->listener->preUpdateCalls = [];\n\n        $fix->setFixPrice(2000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->preUpdateCalls);\n        self::assertSame($fix, $this->listener->preUpdateCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->preUpdateCalls[0][0]);\n        self::assertInstanceOf(PreUpdateEventArgs::class, $this->listener->preUpdateCalls[0][1]);\n    }\n\n    public function testPostUpdateListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(1000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        $this->listener->postUpdateCalls = [];\n\n        $fix->setFixPrice(2000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->postUpdateCalls);\n        self::assertSame($fix, $this->listener->postUpdateCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->postUpdateCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->postUpdateCalls[0][1]);\n    }\n\n    public function testPreRemoveListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(1000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        $this->listener->preRemoveCalls = [];\n\n        $this->_em->remove($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->preRemoveCalls);\n        self::assertSame($fix, $this->listener->preRemoveCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->preRemoveCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->preRemoveCalls[0][1]);\n    }\n\n    public function testPostRemoveListeners(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(1000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        $this->listener->postRemoveCalls = [];\n\n        $this->_em->remove($fix);\n        $this->_em->flush();\n\n        self::assertCount(1, $this->listener->postRemoveCalls);\n        self::assertSame($fix, $this->listener->postRemoveCalls[0][0]);\n        self::assertInstanceOf(CompanyFixContract::class, $this->listener->postRemoveCalls[0][0]);\n        self::assertInstanceOf(LifecycleEventArgs::class, $this->listener->postRemoveCalls[0][1]);\n    }\n\n    public function testPostRemoveCalledAfterAllRemovalsHaveBeenPerformed(): void\n    {\n        $object1 = new CompanyFixContract();\n        $object1->setFixPrice(2000);\n\n        $object2 = new CompanyPerson();\n        $object2->setName('J. Doe');\n\n        $this->_em->persist($object1);\n        $this->_em->persist($object2);\n        $this->_em->flush();\n\n        $listener = new class ($this->_em->getUnitOfWork(), [$object1, $object2]) {\n            /** @var UnitOfWork */\n            private $uow;\n\n            /** @var array<object> */\n            private $trackedObjects;\n\n            /** @var int */\n            public $invocationCount = 0;\n\n            public function __construct(UnitOfWork $uow, array $trackedObjects)\n            {\n                $this->uow            = $uow;\n                $this->trackedObjects = $trackedObjects;\n            }\n\n            public function postRemove(PostRemoveEventArgs $args): void\n            {\n                foreach ($this->trackedObjects as $object) {\n                    Assert::assertFalse($this->uow->isInIdentityMap($object));\n                }\n\n                ++$this->invocationCount;\n            }\n        };\n\n        $this->_em->getEventManager()->addEventListener(Events::postRemove, $listener);\n        $this->_em->remove($object1);\n        $this->_em->remove($object2);\n        $this->_em->flush();\n\n        self::assertSame(2, $listener->invocationCount);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\LazyCriteriaCollection;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\Models\\Tweet\\Tweet;\nuse Doctrine\\Tests\\Models\\Tweet\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass EntityRepositoryCriteriaTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('generic');\n        $this->useModelSet('tweet');\n\n        parent::setUp();\n    }\n\n    public function tearDown(): void\n    {\n        if ($this->_em) {\n            $this->_em->getConfiguration()->setEntityNamespaces([]);\n        }\n\n        parent::tearDown();\n    }\n\n    public function loadFixture(): void\n    {\n        $today           = new DateTimeModel();\n        $today->datetime =\n        $today->date     =\n        $today->time     =\n            new DateTime('today');\n        $this->_em->persist($today);\n\n        $tomorrow           = new DateTimeModel();\n        $tomorrow->datetime =\n        $tomorrow->date     =\n        $tomorrow->time     =\n            new DateTime('tomorrow');\n        $this->_em->persist($tomorrow);\n\n        $yesterday           = new DateTimeModel();\n        $yesterday->datetime =\n        $yesterday->date     =\n        $yesterday->time     =\n            new DateTime('yesterday');\n        $this->_em->persist($yesterday);\n\n        $this->_em->flush();\n\n        unset($today, $tomorrow, $yesterday);\n\n        $this->_em->clear();\n    }\n\n    public function testLteDateComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(DateTimeModel::class);\n        $dates      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->lte('datetime', new DateTime('today')),\n        ));\n\n        self::assertCount(2, $dates);\n    }\n\n    private function loadNullFieldFixtures(): void\n    {\n        $today           = new DateTimeModel();\n        $today->datetime =\n        $today->date     =\n            new DateTime('today');\n\n        $this->_em->persist($today);\n\n        $tomorrow           = new DateTimeModel();\n        $tomorrow->datetime =\n        $tomorrow->date     =\n        $tomorrow->time     =\n            new DateTime('tomorrow');\n        $this->_em->persist($tomorrow);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testIsNullComparison(): void\n    {\n        $this->loadNullFieldFixtures();\n        $repository = $this->_em->getRepository(DateTimeModel::class);\n\n        $dates = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->isNull('time'),\n        ));\n\n        self::assertCount(1, $dates);\n    }\n\n    public function testEqNullComparison(): void\n    {\n        $this->loadNullFieldFixtures();\n        $repository = $this->_em->getRepository(DateTimeModel::class);\n\n        $dates = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('time', null),\n        ));\n\n        self::assertCount(1, $dates);\n    }\n\n    public function testNotEqNullComparison(): void\n    {\n        $this->loadNullFieldFixtures();\n        $repository = $this->_em->getRepository(DateTimeModel::class);\n\n        $dates = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->neq('time', null),\n        ));\n\n        self::assertCount(1, $dates);\n    }\n\n    public function testCanCountWithoutLoadingCollection(): void\n    {\n        $this->loadFixture();\n        $repository = $this->_em->getRepository(DateTimeModel::class);\n\n        $dates = $repository->matching(Criteria::create(true));\n\n        self::assertFalse($dates->isInitialized());\n        self::assertCount(3, $dates);\n        self::assertFalse($dates->isInitialized());\n\n        // Test it can work even with a constraint\n        $dates = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->lte('datetime', new DateTime('today')),\n        ));\n\n        self::assertFalse($dates->isInitialized());\n        self::assertCount(2, $dates);\n        self::assertFalse($dates->isInitialized());\n\n        // Trigger a loading, to make sure collection is initialized\n        $date = $dates[0];\n        self::assertTrue($dates->isInitialized());\n    }\n\n    public function testCanContainsWithoutLoadingCollection(): void\n    {\n        $user       = new User();\n        $user->name = 'Marco';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $tweet          = new Tweet();\n        $tweet->author  = $user;\n        $tweet->content = 'Criteria is awesome';\n        $this->_em->persist($tweet);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $criteria = Criteria::create(true);\n        $criteria->andWhere($criteria->expr()->contains('content', 'Criteria'));\n\n        $user   = $this->_em->find(User::class, $user->id);\n        $tweets = $user->tweets->matching($criteria);\n\n        self::assertInstanceOf(LazyCriteriaCollection::class, $tweets);\n        self::assertFalse($tweets->isInitialized());\n\n        $tweets->contains($tweet);\n        self::assertTrue($tweets->contains($tweet));\n\n        self::assertFalse($tweets->isInitialized());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EntityRepositoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse BadMethodCallException;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Exception\\UnrecognizedIdentifierFields;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\Persisters\\Exception\\InvalidOrientation;\nuse Doctrine\\ORM\\Persisters\\Exception\\UnrecognizedField;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\ORM\\Repository\\Exception\\InvalidFindByCall;\nuse Doctrine\\ORM\\Repository\\Exception\\InvalidMagicMethodCall;\nuse Doctrine\\ORM\\TransactionRequiredException;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753CustomRepository;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753DefaultRepository;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753EntityWithCustomRepository;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753EntityWithDefaultCustomRepository;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_values;\nuse function reset;\n\nclass EntityRepositoryTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function tearDown(): void\n    {\n        if ($this->_em) {\n            $this->_em->getConfiguration()->setEntityNamespaces([]);\n        }\n\n        parent::tearDown();\n    }\n\n    public function loadFixture(): int\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'freak';\n        $this->_em->persist($user);\n\n        $user2           = new CmsUser();\n        $user2->name     = 'Guilherme';\n        $user2->username = 'gblanco';\n        $user2->status   = 'dev';\n        $this->_em->persist($user2);\n\n        $user3           = new CmsUser();\n        $user3->name     = 'Benjamin';\n        $user3->username = 'beberlei';\n        $user3->status   = null;\n        $this->_em->persist($user3);\n\n        $user4           = new CmsUser();\n        $user4->name     = 'Alexander';\n        $user4->username = 'asm89';\n        $user4->status   = 'dev';\n        $this->_em->persist($user4);\n\n        $this->_em->flush();\n\n        $user1Id = $user->getId();\n\n        unset($user, $user2, $user3, $user4);\n\n        $this->_em->clear();\n\n        return $user1Id;\n    }\n\n    /** @phpstan-return array{int, int} */\n    public function loadAssociatedFixture(): array\n    {\n        $address          = new CmsAddress();\n        $address->city    = 'Berlin';\n        $address->country = 'Germany';\n        $address->street  = 'Foostreet';\n        $address->zip     = '12345';\n\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'freak';\n        $user->setAddress($address);\n\n        $this->_em->persist($user);\n        $this->_em->persist($address);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return [$user->id, $address->id];\n    }\n\n    /** @phpstan-return list<CmsUser> */\n    public function loadFixtureUserEmail(): array\n    {\n        $user1 = new CmsUser();\n        $user2 = new CmsUser();\n        $user3 = new CmsUser();\n\n        $email1 = new CmsEmail();\n        $email2 = new CmsEmail();\n        $email3 = new CmsEmail();\n\n        $user1->name     = 'Test 1';\n        $user1->username = 'test1';\n        $user1->status   = 'active';\n\n        $user2->name     = 'Test 2';\n        $user2->username = 'test2';\n        $user2->status   = 'active';\n\n        $user3->name     = 'Test 3';\n        $user3->username = 'test3';\n        $user3->status   = 'active';\n\n        $email1->email = 'test1@test.com';\n        $email2->email = 'test2@test.com';\n        $email3->email = 'test3@test.com';\n\n        $user1->setEmail($email1);\n        $user2->setEmail($email2);\n        $user3->setEmail($email3);\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n\n        $this->_em->persist($email1);\n        $this->_em->persist($email2);\n        $this->_em->persist($email3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return [$user1, $user2, $user3];\n    }\n\n    public function buildUser($name, $username, $status, $address): CmsUser\n    {\n        $user           = new CmsUser();\n        $user->name     = $name;\n        $user->username = $username;\n        $user->status   = $status;\n        $user->setAddress($address);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        return $user;\n    }\n\n    public function buildAddress($country, $city, $street, $zip): CmsAddress\n    {\n        $address          = new CmsAddress();\n        $address->country = $country;\n        $address->city    = $city;\n        $address->street  = $street;\n        $address->zip     = $zip;\n\n        $this->_em->persist($address);\n        $this->_em->flush();\n\n        return $address;\n    }\n\n    public function testBasicFind(): void\n    {\n        $user1Id = $this->loadFixture();\n        $repos   = $this->_em->getRepository(CmsUser::class);\n\n        $user = $repos->find($user1Id);\n        self::assertInstanceOf(CmsUser::class, $user);\n        self::assertEquals('Roman', $user->name);\n        self::assertEquals('freak', $user->status);\n    }\n\n    public function testFindByField(): void\n    {\n        $user1Id = $this->loadFixture();\n        $repos   = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repos->findBy(['status' => 'dev']);\n        self::assertCount(2, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Guilherme', $users[0]->name);\n        self::assertEquals('dev', $users[0]->status);\n    }\n\n    public function testFindByAssociationWithIntegerAsParameter(): void\n    {\n        $address1 = $this->buildAddress('Germany', 'Berlim', 'Foo st.', '123456');\n        $user1    = $this->buildUser('Benjamin', 'beberlei', 'dev', $address1);\n\n        $address2 = $this->buildAddress('Brazil', 'São Paulo', 'Bar st.', '654321');\n        $user2    = $this->buildUser('Guilherme', 'guilhermeblanco', 'freak', $address2);\n\n        $address3 = $this->buildAddress('USA', 'Nashville', 'Woo st.', '321654');\n        $user3    = $this->buildUser('Jonathan', 'jwage', 'dev', $address3);\n\n        unset($address1, $address2, $address3);\n\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(CmsAddress::class);\n        $addresses  = $repository->findBy(['user' => [$user1->getId(), $user2->getId()]]);\n\n        self::assertCount(2, $addresses);\n        self::assertInstanceOf(CmsAddress::class, $addresses[0]);\n    }\n\n    public function testFindByAssociationWithObjectAsParameter(): void\n    {\n        $address1 = $this->buildAddress('Germany', 'Berlim', 'Foo st.', '123456');\n        $user1    = $this->buildUser('Benjamin', 'beberlei', 'dev', $address1);\n\n        $address2 = $this->buildAddress('Brazil', 'São Paulo', 'Bar st.', '654321');\n        $user2    = $this->buildUser('Guilherme', 'guilhermeblanco', 'freak', $address2);\n\n        $address3 = $this->buildAddress('USA', 'Nashville', 'Woo st.', '321654');\n        $user3    = $this->buildUser('Jonathan', 'jwage', 'dev', $address3);\n\n        unset($address1, $address2, $address3);\n\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(CmsAddress::class);\n        $addresses  = $repository->findBy(['user' => [$user1, $user2]]);\n\n        self::assertCount(2, $addresses);\n        self::assertInstanceOf(CmsAddress::class, $addresses[0]);\n    }\n\n    public function testFindFieldByMagicCall(): void\n    {\n        $user1Id = $this->loadFixture();\n        $repos   = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repos->findByStatus('dev');\n        self::assertCount(2, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Guilherme', $users[0]->name);\n        self::assertEquals('dev', $users[0]->status);\n    }\n\n    public function testFindAll(): void\n    {\n        $user1Id = $this->loadFixture();\n        $repos   = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repos->findAll();\n        self::assertCount(4, $users);\n    }\n\n    public function testCount(): void\n    {\n        $this->loadFixture();\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $userCount = $repos->count([]);\n        self::assertSame(4, $userCount);\n\n        $userCount = $repos->count(['status' => 'dev']);\n        self::assertSame(2, $userCount);\n\n        $userCount = $repos->count(['status' => 'nonexistent']);\n        self::assertSame(0, $userCount);\n    }\n\n    public function testCountBy(): void\n    {\n        $this->loadFixture();\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $userCount = $repos->countByStatus('dev');\n        self::assertSame(2, $userCount);\n    }\n\n    public function testExceptionIsThrownWhenCallingFindByWithoutParameter(): void\n    {\n        $this->expectException(InvalidMagicMethodCall::class);\n        $this->_em->getRepository(CmsUser::class)\n                  ->findByStatus();\n    }\n\n    public function testExceptionIsThrownWhenUsingInvalidFieldName(): void\n    {\n        $this->expectException(InvalidMagicMethodCall::class);\n        $this->_em->getRepository(CmsUser::class)\n                  ->findByThisFieldDoesNotExist('testvalue');\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticReadLockWithoutTransactionThrowsException(): void\n    {\n        $this->expectException(TransactionRequiredException::class);\n\n        $this->_em->getRepository(CmsUser::class)\n                  ->find(1, LockMode::PESSIMISTIC_READ);\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticWriteLockWithoutTransactionThrowsException(): void\n    {\n        $this->expectException(TransactionRequiredException::class);\n\n        $this->_em->getRepository(CmsUser::class)\n                  ->find(1, LockMode::PESSIMISTIC_WRITE);\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testOptimisticLockUnversionedEntityThrowsException(): void\n    {\n        $this->expectException(OptimisticLockException::class);\n\n        $this->_em->getRepository(CmsUser::class)\n                  ->find(1, LockMode::OPTIMISTIC);\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testIdentityMappedOptimisticLockUnversionedEntityThrowsException(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'freak';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $userId = $user->id;\n\n        $this->_em->find(CmsUser::class, $userId);\n\n        $this->expectException(OptimisticLockException::class);\n\n        $this->_em->find(CmsUser::class, $userId, LockMode::OPTIMISTIC);\n    }\n\n    #[Group('DDC-819')]\n    public function testFindMagicCallByNullValue(): void\n    {\n        $this->loadFixture();\n\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repos->findByStatus(null);\n        self::assertCount(1, $users);\n    }\n\n    #[Group('DDC-819')]\n    public function testInvalidMagicCall(): void\n    {\n        $this->expectException(BadMethodCallException::class);\n\n        $repos = $this->_em->getRepository(CmsUser::class);\n        $repos->foo();\n    }\n\n    #[Group('DDC-817')]\n    public function testFindByAssociationKeyExceptionOnInverseSide(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n        $repos                = $this->_em->getRepository(CmsUser::class);\n\n        $this->expectException(InvalidFindByCall::class);\n        $this->expectExceptionMessage(\"You cannot search for the association field 'Doctrine\\Tests\\Models\\CMS\\CmsUser#address', because it is the inverse side of an association. Find methods only work on owning side associations.\");\n\n        $user = $repos->findBy(['address' => $addressId]);\n    }\n\n    #[Group('DDC-817')]\n    public function testFindOneByAssociationKey(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n        $repos                = $this->_em->getRepository(CmsAddress::class);\n        $address              = $repos->findOneBy(['user' => $userId]);\n\n        self::assertInstanceOf(CmsAddress::class, $address);\n        self::assertEquals($addressId, $address->id);\n    }\n\n    #[Group('DDC-1241')]\n    public function testFindOneByOrderBy(): void\n    {\n        $this->loadFixture();\n\n        $repos    = $this->_em->getRepository(CmsUser::class);\n        $userAsc  = $repos->findOneBy([], ['username' => 'ASC']);\n        $userDesc = $repos->findOneBy([], ['username' => 'DESC']);\n\n        self::assertNotSame($userAsc, $userDesc);\n    }\n\n    #[Group('DDC-817')]\n    public function testFindByAssociationKey(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n        $repos                = $this->_em->getRepository(CmsAddress::class);\n        $addresses            = $repos->findBy(['user' => $userId]);\n\n        self::assertContainsOnly(CmsAddress::class, $addresses);\n        self::assertCount(1, $addresses);\n        self::assertEquals($addressId, $addresses[0]->id);\n    }\n\n    #[Group('DDC-817')]\n    public function testFindAssociationByMagicCall(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n        $repos                = $this->_em->getRepository(CmsAddress::class);\n        $addresses            = $repos->findByUser($userId);\n\n        self::assertContainsOnly(CmsAddress::class, $addresses);\n        self::assertCount(1, $addresses);\n        self::assertEquals($addressId, $addresses[0]->id);\n    }\n\n    #[Group('DDC-817')]\n    public function testFindOneAssociationByMagicCall(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n        $repos                = $this->_em->getRepository(CmsAddress::class);\n        $address              = $repos->findOneByUser($userId);\n\n        self::assertInstanceOf(CmsAddress::class, $address);\n        self::assertEquals($addressId, $address->id);\n    }\n\n    #[Group('DDC-1087')]\n    public function testIsNullCriteriaDoesNotGenerateAParameter(): void\n    {\n        $repos = $this->_em->getRepository(CmsUser::class);\n        $users = $repos->findBy(['status' => null, 'username' => 'romanb']);\n\n        $params = $this->getLastLoggedQuery()['params'];\n        self::assertCount(1, $params, 'Should only execute with one parameter.');\n        self::assertEquals(['romanb'], array_values($params));\n    }\n\n    public function testIsNullCriteria(): void\n    {\n        $this->loadFixture();\n\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repos->findBy(['status' => null]);\n        self::assertCount(1, $users);\n    }\n\n    #[Group('DDC-1094')]\n    public function testFindByLimitOffset(): void\n    {\n        $this->loadFixture();\n\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $users1 = $repos->findBy([], null, 1, 0);\n        $users2 = $repos->findBy([], null, 1, 1);\n\n        self::assertCount(4, $repos->findBy([]));\n        self::assertCount(1, $users1);\n        self::assertCount(1, $users2);\n        self::assertNotSame($users1[0], $users2[0]);\n    }\n\n    #[Group('DDC-1094')]\n    public function testFindByOrderBy(): void\n    {\n        $this->loadFixture();\n\n        $repos     = $this->_em->getRepository(CmsUser::class);\n        $usersAsc  = $repos->findBy([], ['username' => 'ASC']);\n        $usersDesc = $repos->findBy([], ['username' => 'DESC']);\n\n        self::assertCount(4, $usersAsc, 'Pre-condition: only four users in fixture');\n        self::assertCount(4, $usersDesc, 'Pre-condition: only four users in fixture');\n        self::assertSame($usersAsc[0], $usersDesc[3]);\n        self::assertSame($usersAsc[3], $usersDesc[0]);\n    }\n\n    #[Group('DDC-1376')]\n    public function testFindByOrderByAssociation(): void\n    {\n        $this->loadFixtureUserEmail();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $resultAsc  = $repository->findBy([], ['email' => 'ASC']);\n        $resultDesc = $repository->findBy([], ['email' => 'DESC']);\n\n        self::assertCount(3, $resultAsc);\n        self::assertCount(3, $resultDesc);\n\n        self::assertEquals($resultAsc[0]->getEmail()->getId(), $resultDesc[2]->getEmail()->getId());\n        self::assertEquals($resultAsc[2]->getEmail()->getId(), $resultDesc[0]->getEmail()->getId());\n    }\n\n    #[Group('DDC-1426')]\n    public function testFindFieldByMagicCallOrderBy(): void\n    {\n        $this->loadFixture();\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $usersAsc  = $repos->findByStatus('dev', ['username' => 'ASC']);\n        $usersDesc = $repos->findByStatus('dev', ['username' => 'DESC']);\n\n        self::assertCount(2, $usersAsc);\n        self::assertCount(2, $usersDesc);\n\n        self::assertInstanceOf(CmsUser::class, $usersAsc[0]);\n        self::assertEquals('Alexander', $usersAsc[0]->name);\n        self::assertEquals('dev', $usersAsc[0]->status);\n\n        self::assertSame($usersAsc[0], $usersDesc[1]);\n        self::assertSame($usersAsc[1], $usersDesc[0]);\n    }\n\n    #[Group('DDC-1426')]\n    public function testFindFieldByMagicCallLimitOffset(): void\n    {\n        $this->loadFixture();\n        $repos = $this->_em->getRepository(CmsUser::class);\n\n        $users1 = $repos->findByStatus('dev', [], 1, 0);\n        $users2 = $repos->findByStatus('dev', [], 1, 1);\n\n        self::assertCount(1, $users1);\n        self::assertCount(1, $users2);\n        self::assertNotSame($users1[0], $users2[0]);\n    }\n\n    #[Group('DDC-753')]\n    public function testDefaultRepositoryClassName(): void\n    {\n        self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), EntityRepository::class);\n        $this->_em->getConfiguration()->setDefaultRepositoryClassName(DDC753DefaultRepository::class);\n        self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), DDC753DefaultRepository::class);\n\n        $repos = $this->_em->getRepository(DDC753EntityWithDefaultCustomRepository::class);\n        self::assertInstanceOf(DDC753DefaultRepository::class, $repos);\n        self::assertTrue($repos->isDefaultRepository());\n\n        $repos = $this->_em->getRepository(DDC753EntityWithCustomRepository::class);\n        self::assertInstanceOf(DDC753CustomRepository::class, $repos);\n        self::assertTrue($repos->isCustomRepository());\n\n        self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), DDC753DefaultRepository::class);\n        $this->_em->getConfiguration()->setDefaultRepositoryClassName(EntityRepository::class);\n        self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), EntityRepository::class);\n    }\n\n    #[Group('DDC-3257')]\n    public function testCanRetrieveRepositoryFromClassNameWithLeadingBackslash(): void\n    {\n        self::assertSame(\n            $this->_em->getRepository('\\\\' . CmsUser::class),\n            $this->_em->getRepository(CmsUser::class),\n        );\n    }\n\n    #[Group('DDC-1376')]\n    public function testInvalidOrderByAssociation(): void\n    {\n        $this->expectException(InvalidFindByCall::class);\n        $this->expectExceptionMessage('You cannot search for the association field \\'Doctrine\\Tests\\Models\\CMS\\CmsUser#address\\', because it is the inverse side of an association.');\n        $this->_em->getRepository(CmsUser::class)\n            ->findBy(['status' => 'test'], ['address' => 'ASC']);\n    }\n\n    #[Group('DDC-1500')]\n    public function testInvalidOrientation(): void\n    {\n        $this->expectException(InvalidOrientation::class);\n        $this->expectExceptionMessage('Invalid order by orientation specified for Doctrine\\Tests\\Models\\CMS\\CmsUser#username');\n\n        $repo = $this->_em->getRepository(CmsUser::class);\n        $repo->findBy(['status' => 'test'], ['username' => 'INVALID']);\n    }\n\n    #[Group('DDC-1713')]\n    public function testFindByAssociationArray(): void\n    {\n        $address1          = new CmsAddress();\n        $address1->country = 'Germany';\n        $address1->zip     = '12345';\n        $address1->city    = 'Berlin';\n        $user1             = new CmsUser();\n        $user1->username   = 'nifnif';\n        $user1->name       = 'Nif-Nif';\n        $user1->setAddress($address1);\n\n        $address2          = new CmsAddress();\n        $address2->country = 'France';\n        $address2->zip     = '54321';\n        $address2->city    = 'Paris';\n        $user2             = new CmsUser();\n        $user2->username   = 'noufnouf';\n        $user2->name       = 'Nouf-Nouf';\n        $user2->setAddress($address2);\n\n        $address3          = new CmsAddress();\n        $address3->country = 'Spain';\n        $address3->zip     = '98765';\n        $address3->city    = 'Madrid';\n        $user3             = new CmsUser();\n        $user3->username   = 'nafnaf';\n        $user3->name       = 'Naf-Naf';\n        $user3->setAddress($address3);\n\n        $address4          = new CmsAddress();\n        $address4->country = 'United Kingdom';\n        $address4->zip     = '32145';\n        $address4->city    = 'London';\n        $user4             = new CmsUser();\n        $user4->username   = 'wolf';\n        $user4->name       = 'Wolf';\n        $user4->setAddress($address4);\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n        $this->_em->persist($user4);\n        $this->_em->flush();\n\n        $ids = [$user1->id, $user2->id, $user3->id];\n        $this->_em->clear();\n\n        $repo      = $this->_em->getRepository(CmsAddress::class);\n        $addresses = $repo->findBy(['user' => $ids]);\n\n        self::assertCount(3, $addresses);\n        foreach ($addresses as $address) {\n            self::assertContains($address->zip, ['12345', '54321', '98765']);\n        }\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingEmptyCriteria(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true));\n\n        self::assertCount(4, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaEqComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('username', 'beberlei'),\n        ));\n\n        self::assertCount(1, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaNeqComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->neq('username', 'beberlei'),\n        ));\n\n        self::assertCount(3, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaInComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->in('username', ['beberlei', 'gblanco']),\n        ));\n\n        self::assertCount(2, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaNotInComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->notIn('username', ['beberlei', 'gblanco', 'asm89']),\n        ));\n\n        self::assertCount(1, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaLtComparison(): void\n    {\n        $firstUserId = $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->lt('id', $firstUserId + 1),\n        ));\n\n        self::assertCount(1, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaLeComparison(): void\n    {\n        $firstUserId = $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->lte('id', $firstUserId + 1),\n        ));\n\n        self::assertCount(2, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaGtComparison(): void\n    {\n        $firstUserId = $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->gt('id', $firstUserId),\n        ));\n\n        self::assertCount(3, $users);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatchingCriteriaGteComparison(): void\n    {\n        $firstUserId = $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $users      = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->gte('id', $firstUserId),\n        ));\n\n        self::assertCount(4, $users);\n    }\n\n    #[Group('DDC-2430')]\n    public function testMatchingCriteriaAssocationByObjectInMemory(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n\n        $user = $this->_em->find(CmsUser::class, $userId);\n\n        $criteria = Criteria::create(true)->where(\n            Criteria::expr()->eq('user', $user),\n        );\n\n        $repository = $this->_em->getRepository(CmsAddress::class);\n        $addresses  = $repository->matching($criteria);\n\n        self::assertCount(1, $addresses);\n\n        $addresses = new ArrayCollection($repository->findAll());\n\n        self::assertCount(1, $addresses->matching($criteria));\n    }\n\n    #[Group('DDC-2430')]\n    public function testMatchingCriteriaAssocationInWithArray(): void\n    {\n        [$userId, $addressId] = $this->loadAssociatedFixture();\n\n        $user = $this->_em->find(CmsUser::class, $userId);\n\n        $criteria = Criteria::create(true)->where(\n            Criteria::expr()->in('user', [$user]),\n        );\n\n        $repository = $this->_em->getRepository(CmsAddress::class);\n        $addresses  = $repository->matching($criteria);\n\n        self::assertCount(1, $addresses);\n\n        $addresses = new ArrayCollection($repository->findAll());\n\n        self::assertCount(1, $addresses->matching($criteria));\n    }\n\n    public function testMatchingCriteriaContainsComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repository->matching(Criteria::create(true)->where(Criteria::expr()->contains('name', 'Foobar')));\n        self::assertCount(0, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(Criteria::expr()->contains('name', 'Rom')));\n        self::assertCount(1, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(Criteria::expr()->contains('status', 'dev')));\n        self::assertCount(2, $users);\n    }\n\n    public function testMatchingCriteriaStartsWithComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->startsWith('name', 'Foo'),\n        ));\n        self::assertCount(0, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->startsWith('name', 'R'),\n        ));\n        self::assertCount(1, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->startsWith('status', 'de'),\n        ));\n        self::assertCount(2, $users);\n    }\n\n    public function testMatchingCriteriaEndsWithComparison(): void\n    {\n        $this->loadFixture();\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->endsWith('name', 'foo'),\n        ));\n        self::assertCount(0, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->endsWith('name', 'oman'),\n        ));\n        self::assertCount(1, $users);\n\n        $users = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->endsWith('status', 'ev'),\n        ));\n        self::assertCount(2, $users);\n    }\n\n    #[Group('DDC-2478')]\n    public function testMatchingCriteriaNullAssocComparison(): void\n    {\n        $fixtures       = $this->loadFixtureUserEmail();\n        $user           = $this->_em->find(CmsUser::class, $fixtures[0]->id);\n        $repository     = $this->_em->getRepository(CmsUser::class);\n        $criteriaIsNull = Criteria::create(true)->where(Criteria::expr()->isNull('email'));\n        $criteriaEqNull = Criteria::create(true)->where(Criteria::expr()->eq('email', null));\n\n        $user->setEmail(null);\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $usersIsNull = $repository->matching($criteriaIsNull);\n        $usersEqNull = $repository->matching($criteriaEqNull);\n\n        self::assertCount(1, $usersIsNull);\n        self::assertCount(1, $usersEqNull);\n\n        self::assertInstanceOf(CmsUser::class, $usersIsNull[0]);\n        self::assertInstanceOf(CmsUser::class, $usersEqNull[0]);\n\n        self::assertNull($usersIsNull[0]->getEmail());\n        self::assertNull($usersEqNull[0]->getEmail());\n    }\n\n    #[Group('DDC-2055')]\n    public function testCreateResultSetMappingBuilder(): void\n    {\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $rsm        = $repository->createResultSetMappingBuilder('u');\n\n        self::assertInstanceOf(ResultSetMappingBuilder::class, $rsm);\n        self::assertEquals(['u' => CmsUser::class], $rsm->aliasMap);\n    }\n\n    #[Group('DDC-3045')]\n    public function testFindByFieldInjectionPrevented(): void\n    {\n        $this->expectException(UnrecognizedField::class);\n        $this->expectExceptionMessage('Unrecognized field: ');\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $repository->findBy(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test']);\n    }\n\n    #[Group('DDC-3045')]\n    public function testFindOneByFieldInjectionPrevented(): void\n    {\n        $this->expectException(ORMException::class);\n        $this->expectExceptionMessage('Unrecognized field: ');\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $repository->findOneBy(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test']);\n    }\n\n    #[Group('DDC-3045')]\n    public function testMatchingInjectionPrevented(): void\n    {\n        $this->expectException(UnrecognizedField::class);\n        $this->expectExceptionMessage('Unrecognized field: ');\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $result     = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1', 'beberlei'),\n        ));\n\n        // Because repository returns a lazy collection, we call toArray to force initialization\n        $result->toArray();\n    }\n\n    #[Group('DDC-3045')]\n    public function testFindInjectionPrevented(): void\n    {\n        $this->expectException(UnrecognizedIdentifierFields::class);\n        $this->expectExceptionMessage('Unrecognized identifier fields: ');\n\n        $repository = $this->_em->getRepository(CmsUser::class);\n        $repository->find(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test', 'id' => 1]);\n    }\n\n    #[Group('DDC-3056')]\n    public function testFindByNullValueInInCondition(): void\n    {\n        $user1 = new CmsUser();\n        $user2 = new CmsUser();\n\n        $user1->username = 'ocramius';\n        $user1->name     = 'Marco';\n        $user2->status   = null;\n        $user2->username = 'deeky666';\n        $user2->name     = 'Steve';\n        $user2->status   = 'dbal maintainer';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        $users = $this->_em->getRepository(CmsUser::class)->findBy(['status' => [null]]);\n\n        self::assertCount(1, $users);\n        self::assertSame($user1, reset($users));\n    }\n\n    #[Group('DDC-3056')]\n    public function testFindByNullValueInMultipleInCriteriaValues(): void\n    {\n        $user1 = new CmsUser();\n        $user2 = new CmsUser();\n\n        $user1->username = 'ocramius';\n        $user1->name     = 'Marco';\n        $user2->status   = null;\n        $user2->username = 'deeky666';\n        $user2->name     = 'Steve';\n        $user2->status   = 'dbal maintainer';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        $users = $this\n            ->_em\n            ->getRepository(CmsUser::class)\n            ->findBy(['status' => ['foo', null]]);\n\n        self::assertCount(1, $users);\n        self::assertSame($user1, reset($users));\n    }\n\n    #[Group('DDC-3056')]\n    public function testFindMultipleByNullValueInMultipleInCriteriaValues(): void\n    {\n        $user1 = new CmsUser();\n        $user2 = new CmsUser();\n\n        $user1->username = 'ocramius';\n        $user1->name     = 'Marco';\n        $user2->status   = null;\n        $user2->username = 'deeky666';\n        $user2->name     = 'Steve';\n        $user2->status   = 'dbal maintainer';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        $users = $this\n            ->_em\n            ->getRepository(CmsUser::class)\n            ->findBy(['status' => ['dbal maintainer', null]]);\n\n        self::assertCount(2, $users);\n\n        foreach ($users as $user) {\n            self::assertContains($user, [$user1, $user2]);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/EnumTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\DBAL\\Types\\EnumType;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Query\\Expr\\Func;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Models\\DataTransferObjects\\DtoWithArrayOfEnums;\nuse Doctrine\\Tests\\Models\\DataTransferObjects\\DtoWithEnum;\nuse Doctrine\\Tests\\Models\\Enums\\BookCategory;\nuse Doctrine\\Tests\\Models\\Enums\\BookGenre;\nuse Doctrine\\Tests\\Models\\Enums\\BookWithGenre;\nuse Doctrine\\Tests\\Models\\Enums\\Card;\nuse Doctrine\\Tests\\Models\\Enums\\CardNativeEnum;\nuse Doctrine\\Tests\\Models\\Enums\\CardWithDefault;\nuse Doctrine\\Tests\\Models\\Enums\\CardWithNullable;\nuse Doctrine\\Tests\\Models\\Enums\\Library;\nuse Doctrine\\Tests\\Models\\Enums\\Product;\nuse Doctrine\\Tests\\Models\\Enums\\Quantity;\nuse Doctrine\\Tests\\Models\\Enums\\Scale;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\nuse Doctrine\\Tests\\Models\\Enums\\TypedCard;\nuse Doctrine\\Tests\\Models\\Enums\\TypedCardNativeEnum;\nuse Doctrine\\Tests\\Models\\Enums\\Unit;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nuse function class_exists;\nuse function sprintf;\nuse function uniqid;\n\nclass EnumTest extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $mappingDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../Models/Enums']);\n\n        $this->_em         = $this->getEntityManager(null, $mappingDriver);\n        $this->_schemaTool = new SchemaTool($this->_em);\n\n        if ($this->isSecondLevelCacheEnabled) {\n            $this->markTestSkipped();\n        }\n    }\n\n    /** @param class-string $cardClass */\n    #[DataProvider('provideCardClasses')]\n    public function testEnumMapping(string $cardClass): void\n    {\n        $this->setUpEntitySchema([$cardClass]);\n\n        $card       = new $cardClass();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fetchedCard = $this->_em->find($cardClass, $card->id);\n\n        $this->assertInstanceOf(Suit::class, $fetchedCard->suit);\n        $this->assertEquals(Suit::Clubs, $fetchedCard->suit);\n    }\n\n    public function testEnumHydrationObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card1       = new Card();\n        $card1->suit = Suit::Clubs;\n        $card2       = new Card();\n        $card2->suit = Suit::Hearts;\n\n        $this->_em->persist($card1);\n        $this->_em->persist($card2);\n        $this->_em->flush();\n\n        unset($card1, $card2);\n        $this->_em->clear();\n\n        /** @var list<Card> $foundCards */\n        $foundCards = $this->_em->createQueryBuilder()\n            ->select('c')\n            ->from(Card::class, 'c')\n            ->where('c.suit = :suit')\n            ->setParameter('suit', Suit::Clubs)\n            ->getQuery()\n            ->getResult();\n\n        self::assertNotEmpty($foundCards);\n        foreach ($foundCards as $card) {\n            self::assertSame(Suit::Clubs, $card->suit);\n        }\n    }\n\n    public function testEnumArrayHydrationObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram, Unit::Meter];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Scale::class, 's')\n            ->select('s')\n            ->getQuery()\n            ->getResult();\n\n        self::assertInstanceOf(Scale::class, $result[0]);\n        self::assertEqualsCanonicalizing([Unit::Gram, Unit::Meter], $result[0]->supportedUnits);\n    }\n\n    public function testEnumHydration(): void\n    {\n        $this->setUpEntitySchema([Card::class, CardWithNullable::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Card::class, 'c')\n            ->select('c.id, c.suit')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertInstanceOf(Suit::class, $result[0]['suit']);\n        $this->assertEquals(Suit::Clubs, $result[0]['suit']);\n    }\n\n    public function testEnumHydrationArrayHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class, CardWithNullable::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Card::class, 'c')\n            ->select('c')\n            ->getQuery()\n            ->getResult(AbstractQuery::HYDRATE_ARRAY);\n\n        $this->assertInstanceOf(Suit::class, $result[0]['suit']);\n        $this->assertEquals(Suit::Clubs, $result[0]['suit']);\n    }\n\n    public function testNullableEnumHydration(): void\n    {\n        $this->setUpEntitySchema([Card::class, CardWithNullable::class]);\n\n        $cardWithNullable       = new CardWithNullable();\n        $cardWithNullable->suit = null;\n\n        $this->_em->persist($cardWithNullable);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(CardWithNullable::class, 'c')\n            ->select('c.id, c.suit')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertNull($result[0]['suit']);\n    }\n\n    public function testEnumArrayHydration(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram, Unit::Meter];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Scale::class, 's')\n            ->select('s.id, s.supportedUnits')\n            ->getQuery()\n            ->getResult();\n\n        self::assertEqualsCanonicalizing([Unit::Gram, Unit::Meter], $result[0]['supportedUnits']);\n    }\n\n    public function testEnumInDtoHydration(): void\n    {\n        $this->setUpEntitySchema([Card::class, CardWithNullable::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(CardWithNullable::class, 'c')\n            ->select('NEW ' . DtoWithEnum::class . '(c.suit)')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertNull($result[0]->suit);\n    }\n\n    public function testNullableEnumInDtoHydration(): void\n    {\n        $this->setUpEntitySchema([Card::class, CardWithNullable::class]);\n\n        $cardWithNullable       = new CardWithNullable();\n        $cardWithNullable->suit = null;\n\n        $this->_em->persist($cardWithNullable);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(CardWithNullable::class, 'c')\n            ->select('NEW ' . DtoWithEnum::class . '(c.suit)')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertNull($result[0]->suit);\n    }\n\n    public function testEnumArrayInDtoHydration(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram, Unit::Meter];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Scale::class, 's')\n            ->select(new Func('NEW ' . DtoWithArrayOfEnums::class, ['s.supportedUnits']))\n            ->getQuery()\n            ->getResult();\n\n        self::assertEqualsCanonicalizing([Unit::Gram, Unit::Meter], $result[0]->supportedUnits);\n    }\n\n    public function testEnumSingleEntityChangeSetsSimpleObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->find(Card::class, $card->id);\n\n        $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(\n            $this->_em->getClassMetadata(Card::class),\n            $result,\n        );\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));\n\n        $result->suit = Suit::Hearts;\n\n        $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(\n            $this->_em->getClassMetadata(Card::class),\n            $result,\n        );\n\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForUpdate($result));\n    }\n\n    public function testEnumSingleEntityChangeSetsObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->find(Card::class, $card->id);\n\n        $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(\n            $this->_em->getClassMetadata(Card::class),\n            $result,\n        );\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));\n    }\n\n    public function testEnumArraySingleEntityChangeSets(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->find(Scale::class, $scale->id);\n\n        $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(\n            $this->_em->getClassMetadata(Scale::class),\n            $result,\n        );\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));\n    }\n\n    public function testEnumChangeSetsSimpleObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->find(Card::class, $card->id);\n\n        $this->_em->getUnitOfWork()->computeChangeSets();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));\n    }\n\n    public function testEnumChangeSetsObjectHydrator(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card       = new Card();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Card::class, 'c')\n            ->select('c')\n            ->getQuery()\n            ->getResult();\n\n        $this->_em->getUnitOfWork()->computeChangeSets();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result[0]));\n    }\n\n    public function testEnumArrayChangeSets(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->from(Scale::class, 's')\n            ->select('s')\n            ->getQuery()\n            ->getResult();\n\n        $this->_em->getUnitOfWork()->computeChangeSets();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result[0]));\n    }\n\n    public function testFindByEnum(): void\n    {\n        $this->setUpEntitySchema([Card::class]);\n\n        $card1       = new Card();\n        $card1->suit = Suit::Clubs;\n        $card2       = new Card();\n        $card2->suit = Suit::Hearts;\n\n        $this->_em->persist($card1);\n        $this->_em->persist($card2);\n        $this->_em->flush();\n\n        unset($card1, $card2);\n        $this->_em->clear();\n\n        /** @var list<Card> $foundCards */\n        $foundCards = $this->_em->getRepository(Card::class)->findBy(['suit' => Suit::Clubs]);\n        $this->assertNotEmpty($foundCards);\n        foreach ($foundCards as $card) {\n            $this->assertSame(Suit::Clubs, $card->suit);\n        }\n    }\n\n    /** @param class-string $cardClass */\n    #[DataProvider('provideCardClasses')]\n    public function testEnumWithNonMatchingDatabaseValueThrowsException(string $cardClass): void\n    {\n        if ($cardClass === TypedCardNativeEnum::class) {\n            self::markTestSkipped('MySQL won\\'t allow us to insert invalid values in this case.');\n        }\n\n        $this->setUpEntitySchema([$cardClass]);\n\n        $card       = new $cardClass();\n        $card->suit = Suit::Clubs;\n\n        $this->_em->persist($card);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $metadata = $this->_em->getClassMetadata($cardClass);\n        $this->_em->getConnection()->update(\n            $metadata->table['name'],\n            [$metadata->fieldMappings['suit']->columnName => 'Z'],\n            [$metadata->fieldMappings['id']->columnName => $card->id],\n        );\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(sprintf(\n            <<<'EXCEPTION'\nContext: Trying to hydrate enum property \"%s::$suit\"\nProblem: Case \"Z\" is not listed in enum \"Doctrine\\Tests\\Models\\Enums\\Suit\"\nSolution: Either add the case to the enum type or migrate the database column to use another case of the enum\nEXCEPTION\n            ,\n            $cardClass,\n        ));\n\n        $this->_em->find($cardClass, $card->id);\n    }\n\n    /** @return iterable<string, array{class-string}> */\n    public static function provideCardClasses(): iterable\n    {\n        yield Card::class => [Card::class];\n        yield TypedCard::class => [TypedCard::class];\n\n        if (class_exists(EnumType::class)) {\n            yield CardNativeEnum::class => [CardNativeEnum::class];\n            yield TypedCardNativeEnum::class => [TypedCardNativeEnum::class];\n        }\n    }\n\n    public function testItAllowsReadingAttributes(): void\n    {\n        $metadata = $this->_em->getClassMetadata(Card::class);\n        $property = $metadata->propertyAccessors['suit']->getUnderlyingReflector();\n\n        $attributes = $property->getAttributes();\n\n        $this->assertCount(1, $attributes);\n        $this->assertEquals(Column::class, $attributes[0]->getName());\n    }\n\n    public function testEnumMappingWithEmbeddable(): void\n    {\n        $this->setUpEntitySchema([Product::class]);\n\n        $product                  = new Product();\n        $product->quantity        = new Quantity();\n        $product->quantity->value = 10;\n        $product->quantity->unit  = Unit::Gram;\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fetchedProduct = $this->_em->find(Product::class, $product->id);\n\n        $this->assertInstanceOf(Unit::class, $fetchedProduct->quantity->unit);\n        $this->assertEquals(Unit::Gram, $fetchedProduct->quantity->unit);\n    }\n\n    public function testEnumArrayMapping(): void\n    {\n        $this->setUpEntitySchema([Scale::class]);\n\n        $scale                 = new Scale();\n        $scale->supportedUnits = [Unit::Gram, Unit::Meter];\n\n        $this->_em->persist($scale);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fetchedScale = $this->_em->find(Scale::class, $scale->id);\n\n        $this->assertIsArray($fetchedScale->supportedUnits);\n        $this->assertContains(Unit::Gram, $fetchedScale->supportedUnits);\n        $this->assertContains(Unit::Meter, $fetchedScale->supportedUnits);\n    }\n\n    public function testEnumWithDefault(): void\n    {\n        $this->setUpEntitySchema([CardWithDefault::class]);\n\n        $table  = $this->_em->getClassMetadata(CardWithDefault::class)->getTableName();\n        $cardId = uniqid('', true);\n\n        $this->_em->getConnection()->insert($table, ['id' => $cardId]);\n        $card = $this->_em->find(CardWithDefault::class, $cardId);\n\n        self::assertSame(Suit::Hearts, $card->suit);\n    }\n\n    #[DataProvider('provideGenreMatchingExpressions')]\n    public function testEnumCollectionMatchingOnOneToMany(Comparison $comparison): void\n    {\n        $this->setUpEntitySchema([BookWithGenre::class, Library::class, BookCategory::class]);\n\n        $library = new Library();\n\n        $fictionBook          = new BookWithGenre(BookGenre::FICTION);\n        $fictionBook->library = $library;\n\n        $nonfictionBook          = new BookWithGenre(BookGenre::NON_FICTION);\n        $nonfictionBook->library = $library;\n\n        $this->_em->persist($library);\n        $this->_em->persist($nonfictionBook);\n        $this->_em->persist($fictionBook);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $library = $this->_em->find(Library::class, $library->id);\n        self::assertFalse($library->books->isInitialized(), 'Pre-condition: lazy collection');\n\n        $result = $library->books->matching(Criteria::create(true)->where($comparison));\n\n        self::assertCount(1, $result);\n        self::assertSame($nonfictionBook->id, $result[0]->id);\n    }\n\n    #[DataProvider('provideGenreMatchingExpressions')]\n    public function testEnumCollectionMatchingOnManyToMany(Comparison $comparison): void\n    {\n        $this->setUpEntitySchema([Library::class, BookWithGenre::class, BookCategory::class]);\n\n        $category = new BookCategory();\n\n        $fictionBook = new BookWithGenre(BookGenre::FICTION);\n        $fictionBook->categories->add($category);\n\n        $nonfictionBook = new BookWithGenre(BookGenre::NON_FICTION);\n        $nonfictionBook->categories->add($category);\n\n        $this->_em->persist($category);\n        $this->_em->persist($nonfictionBook);\n        $this->_em->persist($fictionBook);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $category = $this->_em->find(BookCategory::class, $category->id);\n        self::assertFalse($category->books->isInitialized(), 'Pre-condition: lazy collection');\n\n        $result = $category->books->matching(Criteria::create(true)->where($comparison));\n\n        self::assertCount(1, $result);\n        self::assertSame($nonfictionBook->id, $result[0]->id);\n    }\n\n    public static function provideGenreMatchingExpressions(): Generator\n    {\n        yield [Criteria::expr()->eq('genre', BookGenre::NON_FICTION)];\n        yield [Criteria::expr()->in('genre', [BookGenre::NON_FICTION])];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\DDC2504\\DDC2504ChildClass;\nuse Doctrine\\Tests\\Models\\DDC2504\\DDC2504OtherClass;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_shift;\nuse function assert;\n\n/**\n * Description of ExtraLazyCollectionTest\n */\nclass ExtraLazyCollectionTest extends OrmFunctionalTestCase\n{\n    private int|null $userId = null;\n\n    private int|null $userId2 = null;\n\n    private int|null $groupId = null;\n\n    private int|null $articleId = null;\n\n    private int|null $ddc2504OtherClassId = null;\n\n    private int|null $ddc2504ChildClassId = null;\n\n    private string|null $username = null;\n\n    private string|null $groupname = null;\n\n    private string|null $topic = null;\n\n    /** @var CmsPhonenumber */\n    private $phonenumber;\n\n    /** @var array<string, mixed> */\n    private $previousCacheConfig = [];\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('tweet');\n        $this->useModelSet('cms');\n        $this->useModelSet('ddc2504');\n\n        parent::setUp();\n\n        $class                                               = $this->_em->getClassMetadata(CmsUser::class);\n        $class->associationMappings['groups']->fetch         = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['groups']->indexBy       = 'name';\n        $class->associationMappings['articles']->fetch       = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['articles']->indexBy     = 'topic';\n        $class->associationMappings['phonenumbers']->fetch   = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['phonenumbers']->indexBy = 'phonenumber';\n\n        foreach (['phonenumbers', 'articles', 'users'] as $field) {\n            if (isset($class->associationMappings[$field]->cache)) {\n                $this->previousCacheConfig[$field] = $class->associationMappings[$field]->cache;\n            }\n\n            unset($class->associationMappings[$field]->cache);\n        }\n\n        $class                                        = $this->_em->getClassMetadata(CmsGroup::class);\n        $class->associationMappings['users']->fetch   = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['users']->indexBy = 'username';\n\n        $this->loadFixture();\n    }\n\n    public function tearDown(): void\n    {\n        parent::tearDown();\n\n        $class                                             = $this->_em->getClassMetadata(CmsUser::class);\n        $class->associationMappings['groups']->fetch       = ClassMetadata::FETCH_LAZY;\n        $class->associationMappings['articles']->fetch     = ClassMetadata::FETCH_LAZY;\n        $class->associationMappings['phonenumbers']->fetch = ClassMetadata::FETCH_LAZY;\n\n        foreach (['phonenumbers', 'articles', 'users'] as $field) {\n            if (isset($this->previousCacheConfig[$field])) {\n                $class->associationMappings[$field]->cache = $this->previousCacheConfig[$field];\n                unset($this->previousCacheConfig[$field]);\n            }\n        }\n\n        unset($class->associationMappings['groups']->indexBy);\n        unset($class->associationMappings['articles']->indexBy);\n        unset($class->associationMappings['phonenumbers']->indexBy);\n\n        $class                                      = $this->_em->getClassMetadata(CmsGroup::class);\n        $class->associationMappings['users']->fetch = ClassMetadata::FETCH_LAZY;\n\n        unset($class->associationMappings['users']->indexBy);\n    }\n\n    #[Group('DDC-546')]\n    #[Group('non-cacheable')]\n    public function testCountNotInitializesCollection(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertCount(3, $user->groups);\n        self::assertFalse($user->groups->isInitialized());\n\n        foreach ($user->groups as $group) {\n        }\n\n        $this->assertQueryCount(2, 'Expecting two queries to be fired for count, then iteration.');\n    }\n\n    #[Group('DDC-546')]\n    public function testCountWhenNewEntityPresent(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $newGroup       = new CmsGroup();\n        $newGroup->name = 'Test4';\n\n        $user->addGroup($newGroup);\n        $this->_em->persist($newGroup);\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertCount(4, $user->groups);\n        self::assertFalse($user->groups->isInitialized());\n    }\n\n    #[Group('DDC-546')]\n    #[Group('non-cacheable')]\n    public function testCountWhenInitialized(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        foreach ($user->groups as $group) {\n        }\n\n        self::assertTrue($user->groups->isInitialized());\n        self::assertCount(3, $user->groups);\n        $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for count() more.');\n    }\n\n    #[Group('DDC-546')]\n    public function testCountInverseCollection(): void\n    {\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n        self::assertFalse($group->users->isInitialized(), 'Pre-Condition');\n\n        self::assertCount(4, $group->users);\n        self::assertFalse($group->users->isInitialized(), 'Extra Lazy collection should not be initialized by counting the collection.');\n    }\n\n    #[Group('DDC-546')]\n    public function testCountOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->groups->isInitialized(), 'Pre-Condition');\n\n        self::assertCount(2, $user->articles);\n    }\n\n    #[Group('DDC-2504')]\n    public function testCountOneToManyJoinedInheritance(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n\n        self::assertFalse($otherClass->childClasses->isInitialized(), 'Pre-Condition');\n        self::assertCount(2, $otherClass->childClasses);\n    }\n\n    #[Group('non-cacheable')]\n    public function testFirstWhenInitialized(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n        $user->groups->toArray();\n\n        self::assertTrue($user->groups->isInitialized());\n        self::assertInstanceOf(CmsGroup::class, $user->groups->first());\n        $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().');\n    }\n\n    public function testFirstOnEmptyCollectionWhenInitialized(): void\n    {\n        foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) {\n            $this->_em->remove($group);\n        }\n\n        $this->_em->flush();\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n        $user->groups->toArray();\n\n        self::assertTrue($user->groups->isInitialized());\n        self::assertFalse($user->groups->first());\n        $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().');\n    }\n\n    public function testFirstWhenNotInitialized(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertInstanceOf(CmsGroup::class, $user->groups->first());\n        self::assertFalse($user->groups->isInitialized());\n        $this->assertQueryCount(1, 'Should only execute one query for first().');\n    }\n\n    public function testFirstOnEmptyCollectionWhenNotInitialized(): void\n    {\n        foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) {\n            $this->_em->remove($group);\n        }\n\n        $this->_em->flush();\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertFalse($user->groups->first());\n        self::assertFalse($user->groups->isInitialized());\n        $this->assertQueryCount(1, 'Should only execute one query for first().');\n    }\n\n    #[Group('DDC-546')]\n    public function testFullSlice(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->groups->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        $someGroups = $user->groups->slice(0);\n        self::assertCount(3, $someGroups);\n    }\n\n    #[Group('DDC-546')]\n    #[Group('non-cacheable')]\n    public function testSlice(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->groups->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        $this->getQueryLog()->reset()->enable();\n\n        $someGroups = $user->groups->slice(0, 2);\n\n        self::assertContainsOnly(CmsGroup::class, $someGroups);\n        self::assertCount(2, $someGroups);\n        self::assertFalse($user->groups->isInitialized(), \"Slice should not initialize the collection if it wasn't before!\");\n\n        $otherGroup = $user->groups->slice(2, 1);\n\n        self::assertContainsOnly(CmsGroup::class, $otherGroup);\n        self::assertCount(1, $otherGroup);\n        self::assertFalse($user->groups->isInitialized());\n\n        foreach ($user->groups as $group) {\n        }\n\n        self::assertTrue($user->groups->isInitialized());\n        self::assertCount(3, $user->groups);\n\n        $this->assertQueryCount(3);\n    }\n\n    #[Group('DDC-546')]\n    #[Group('non-cacheable')]\n    public function testSliceInitializedCollection(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        foreach ($user->groups as $group) {\n        }\n\n        $someGroups = $user->groups->slice(0, 2);\n\n        $this->assertQueryCount(1);\n\n        self::assertCount(2, $someGroups);\n        self::assertTrue($user->groups->contains(array_shift($someGroups)));\n        self::assertTrue($user->groups->contains(array_shift($someGroups)));\n    }\n\n    #[Group('DDC-546')]\n    public function testSliceInverseCollection(): void\n    {\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n        self::assertFalse($group->users->isInitialized(), 'Pre-Condition');\n        $this->getQueryLog()->reset()->enable();\n\n        $someUsers  = $group->users->slice(0, 2);\n        $otherUsers = $group->users->slice(2, 2);\n\n        self::assertContainsOnly(CmsUser::class, $someUsers);\n        self::assertContainsOnly(CmsUser::class, $otherUsers);\n        self::assertCount(2, $someUsers);\n        self::assertCount(2, $otherUsers);\n\n        // +2 queries executed by slice\n        $this->assertQueryCount(2, 'Slicing two parts should only execute two additional queries.');\n    }\n\n    #[Group('DDC-546')]\n    public function testSliceOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->articles->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        $this->getQueryLog()->reset()->enable();\n\n        $someArticle  = $user->articles->slice(0, 1);\n        $otherArticle = $user->articles->slice(1, 1);\n\n        $this->assertQueryCount(2);\n    }\n\n    #[Group('DDC-546')]\n    public function testContainsOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->articles->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        // Test One to Many existence retrieved from DB\n        $article = $this->_em->find(CmsArticle::class, $this->articleId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($user->articles->contains($article));\n        self::assertFalse($user->articles->isInitialized(), 'Post-Condition: Collection is not initialized.');\n        $this->assertQueryCount(1);\n\n        // Test One to Many existence with state new\n        $article        = new CmsArticle();\n        $article->topic = 'Testnew';\n        $article->text  = 'blub';\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertFalse($user->articles->contains($article));\n        $this->assertQueryCount(0, 'Checking for contains of new entity should cause no query to be executed.');\n\n        // Test One to Many existence with state clear\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertFalse($user->articles->contains($article));\n        $this->assertQueryCount(1, 'Checking for contains of persisted entity should cause one query to be executed.');\n        self::assertFalse($user->articles->isInitialized(), 'Post-Condition: Collection is not initialized.');\n\n        // Test One to Many existence with state managed\n        $article        = new CmsArticle();\n        $article->topic = 'How to not fail anymore on tests';\n        $article->text  = 'That is simple! Just write more tests!';\n\n        $this->_em->persist($article);\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->articles->contains($article));\n        $this->assertQueryCount(0, 'Checking for contains of managed entity (but not persisted) should cause no query to be executed.');\n        self::assertFalse($user->articles->isInitialized(), 'Post-Condition: Collection is not initialized.');\n    }\n\n    #[Group('DDC-2504')]\n    public function testLazyOneToManyJoinedInheritanceIsLazilyInitialized(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n\n        self::assertFalse($otherClass->childClasses->isInitialized(), 'Collection is not initialized.');\n    }\n\n    #[Group('DDC-2504')]\n    public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWhenMatchingItemIsFound(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n\n        // Test One to Many existence retrieved from DB\n        $childClass = $this->_em->find(DDC2504ChildClass::class, $this->ddc2504ChildClassId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($otherClass->childClasses->contains($childClass));\n        self::assertFalse($otherClass->childClasses->isInitialized(), 'Collection is not initialized.');\n        $this->assertQueryCount(1, 'Search operation was performed via SQL');\n    }\n\n    #[Group('DDC-2504')]\n    public function testContainsOnOneToManyJoinedInheritanceWillNotCauseQueriesWhenNonPersistentItemIsMatched(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($otherClass->childClasses->contains(new DDC2504ChildClass()));\n        $this->assertQueryCount(0, 'Checking for contains of new entity should cause no query to be executed.');\n    }\n\n    #[Group('DDC-2504')]\n    public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWithClearStateMatchingItem(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n        $childClass = new DDC2504ChildClass();\n\n        // Test One to Many existence with state clear\n        $this->_em->persist($childClass);\n        $this->_em->flush();\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertFalse($otherClass->childClasses->contains($childClass));\n        $this->assertQueryCount(1, 'Checking for contains of persisted entity should cause one query to be executed.');\n        self::assertFalse($otherClass->childClasses->isInitialized(), 'Post-Condition: Collection is not initialized.');\n    }\n\n    #[Group('DDC-2504')]\n    public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWithNewStateNotMatchingItem(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n        $childClass = new DDC2504ChildClass();\n\n        $this->_em->persist($childClass);\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($otherClass->childClasses->contains($childClass));\n        $this->assertQueryCount(0, 'Checking for contains of managed entity (but not persisted) should cause no query to be executed.');\n        self::assertFalse($otherClass->childClasses->isInitialized(), 'Post-Condition: Collection is not initialized.');\n    }\n\n    #[Group('DDC-2504')]\n    public function testCountingOnOneToManyJoinedInheritanceWillNotInitializeCollection(): void\n    {\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n\n        self::assertCount(2, $otherClass->childClasses);\n\n        self::assertFalse($otherClass->childClasses->isInitialized());\n    }\n\n    #[Group('DDC-546')]\n    public function testContainsManyToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertFalse($user->groups->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        // Test Many to Many existence retrieved from DB\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($user->groups->contains($group));\n        $this->assertQueryCount(1, 'Checking for contains of managed entity should cause one query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n\n        // Test Many to Many existence with state new\n        $group       = new CmsGroup();\n        $group->name = 'A New group!';\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->contains($group));\n        $this->assertQueryCount(0, 'Checking for contains of new entity should cause no query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n\n        // Test Many to Many existence with state clear\n        $this->_em->persist($group);\n        $this->_em->flush();\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->contains($group));\n        $this->assertQueryCount(1, 'Checking for contains of persisted entity should cause one query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n\n        // Test Many to Many existence with state managed\n        $group       = new CmsGroup();\n        $group->name = 'My managed group';\n\n        $this->_em->persist($group);\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->groups->contains($group));\n        $this->assertQueryCount(0, 'Checking for contains of managed entity (but not persisted) should cause no query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n    }\n\n    #[Group('DDC-546')]\n    public function testContainsManyToManyInverse(): void\n    {\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n        self::assertFalse($group->users->isInitialized(), 'Pre-Condition: Collection is not initialized.');\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertTrue($group->users->contains($user));\n        $this->assertQueryCount(1, 'Checking for contains of managed entity should cause one query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n\n        $newUser       = new CmsUser();\n        $newUser->name = 'A New group!';\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertFalse($group->users->contains($newUser));\n        $this->assertQueryCount(0, 'Checking for contains of new entity should cause no query to be executed.');\n        self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.');\n    }\n\n    #[Group('DDC-1399')]\n    public function testCountAfterAddThenFlush(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $newGroup       = new CmsGroup();\n        $newGroup->name = 'Test4';\n\n        $user->addGroup($newGroup);\n        $this->_em->persist($newGroup);\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertCount(4, $user->groups);\n        self::assertFalse($user->groups->isInitialized());\n\n        $this->_em->flush();\n\n        self::assertCount(4, $user->groups);\n    }\n\n    #[Group('DDC-1462')]\n    #[Group('non-cacheable')]\n    public function testSliceOnDirtyCollection(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        assert($user instanceof CmsUser);\n\n        $newGroup       = new CmsGroup();\n        $newGroup->name = 'Test4';\n\n        $user->addGroup($newGroup);\n        $this->_em->persist($newGroup);\n\n        $this->getQueryLog()->reset()->enable();\n        $groups = $user->groups->slice(0, 10);\n\n        self::assertCount(4, $groups);\n        $this->assertQueryCount(1);\n    }\n\n    #[Group('DDC-1398')]\n    #[Group('non-cacheable')]\n    public function testGetIndexByIdentifier(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        assert($user instanceof CmsUser);\n\n        $this->getQueryLog()->reset()->enable();\n        $phonenumber = $user->phonenumbers->get($this->phonenumber);\n\n        self::assertFalse($user->phonenumbers->isInitialized());\n        $this->assertQueryCount(1);\n        self::assertSame($phonenumber, $this->_em->find(CmsPhonenumber::class, $this->phonenumber));\n\n        $article = $user->phonenumbers->get($this->phonenumber);\n        $this->assertQueryCount(1, 'Getting the same entity should not cause an extra query to be executed');\n    }\n\n    #[Group('DDC-1398')]\n    public function testGetIndexByOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        assert($user instanceof CmsUser);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $article = $user->articles->get($this->topic);\n\n        self::assertFalse($user->articles->isInitialized());\n        $this->assertQueryCount(1);\n        self::assertSame($article, $this->_em->find(CmsArticle::class, $this->articleId));\n    }\n\n    #[Group('DDC-1398')]\n    public function testGetIndexByManyToManyInverseSide(): void\n    {\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n        assert($group instanceof CmsGroup);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $user = $group->users->get($this->username);\n\n        self::assertFalse($group->users->isInitialized());\n        $this->assertQueryCount(1);\n        self::assertSame($user, $this->_em->find(CmsUser::class, $this->userId));\n    }\n\n    #[Group('DDC-1398')]\n    public function testGetIndexByManyToManyOwningSide(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        assert($user instanceof CmsUser);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $group = $user->groups->get($this->groupname);\n\n        self::assertFalse($user->groups->isInitialized());\n        $this->assertQueryCount(1);\n        self::assertSame($group, $this->_em->find(CmsGroup::class, $this->groupId));\n    }\n\n    #[Group('DDC-1398')]\n    public function testGetNonExistentIndexBy(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertNull($user->articles->get(-1));\n        self::assertNull($user->groups->get(-1));\n    }\n\n    public function testContainsKeyIndexByOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        assert($user instanceof CmsUser);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $user->articles->containsKey($this->topic);\n\n        self::assertTrue($contains);\n        self::assertFalse($user->articles->isInitialized());\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyIndexByOneToManyJoinedInheritance(): void\n    {\n        $class                                               = $this->_em->getClassMetadata(DDC2504OtherClass::class);\n        $class->associationMappings['childClasses']->indexBy = 'id';\n\n        $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $otherClass->childClasses->containsKey($this->ddc2504ChildClassId);\n\n        self::assertTrue($contains);\n        self::assertFalse($otherClass->childClasses->isInitialized());\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyIndexByManyToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $user->groups->containsKey($group->name);\n\n        self::assertTrue($contains, 'The item is not into collection');\n        self::assertFalse($user->groups->isInitialized(), 'The collection must not be initialized');\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyIndexByManyToManyNonOwning(): void\n    {\n        $user  = $this->_em->find(CmsUser::class, $this->userId2);\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $group->users->containsKey($user->username);\n\n        self::assertTrue($contains, 'The item is not into collection');\n        self::assertFalse($group->users->isInitialized(), 'The collection must not be initialized');\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyIndexByWithPkManyToMany(): void\n    {\n        $class                                         = $this->_em->getClassMetadata(CmsUser::class);\n        $class->associationMappings['groups']->indexBy = 'id';\n\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $user->groups->containsKey($this->groupId);\n\n        self::assertTrue($contains, 'The item is not into collection');\n        self::assertFalse($user->groups->isInitialized(), 'The collection must not be initialized');\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyIndexByWithPkManyToManyNonOwning(): void\n    {\n        $class                                        = $this->_em->getClassMetadata(CmsGroup::class);\n        $class->associationMappings['users']->indexBy = 'id';\n\n        $group = $this->_em->find(CmsGroup::class, $this->groupId);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $group->users->containsKey($this->userId2);\n\n        self::assertTrue($contains, 'The item is not into collection');\n        self::assertFalse($group->users->isInitialized(), 'The collection must not be initialized');\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyNonExistentIndexByOneToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $user->articles->containsKey('NonExistentTopic');\n\n        self::assertFalse($contains);\n        self::assertFalse($user->articles->isInitialized());\n        $this->assertQueryCount(1);\n    }\n\n    public function testContainsKeyNonExistentIndexByManyToMany(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $contains = $user->groups->containsKey('NonExistentTopic');\n\n        self::assertFalse($contains);\n        self::assertFalse($user->groups->isInitialized());\n        $this->assertQueryCount(1);\n    }\n\n    private function loadFixture(): void\n    {\n        $user1           = new CmsUser();\n        $user1->username = 'beberlei';\n        $user1->name     = 'Benjamin';\n        $user1->status   = 'active';\n\n        $user2           = new CmsUser();\n        $user2->username = 'jwage';\n        $user2->name     = 'Jonathan';\n        $user2->status   = 'active';\n\n        $user3           = new CmsUser();\n        $user3->username = 'romanb';\n        $user3->name     = 'Roman';\n        $user3->status   = 'active';\n\n        $user4           = new CmsUser();\n        $user4->username = 'gblanco';\n        $user4->name     = 'Guilherme';\n        $user4->status   = 'active';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n        $this->_em->persist($user4);\n\n        $group1       = new CmsGroup();\n        $group1->name = 'Test1';\n\n        $group2       = new CmsGroup();\n        $group2->name = 'Test2';\n\n        $group3       = new CmsGroup();\n        $group3->name = 'Test3';\n\n        $user1->addGroup($group1);\n        $user1->addGroup($group2);\n        $user1->addGroup($group3);\n\n        $user2->addGroup($group1);\n        $user3->addGroup($group1);\n        $user4->addGroup($group1);\n\n        $this->_em->persist($group1);\n        $this->_em->persist($group2);\n        $this->_em->persist($group3);\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Test1';\n        $article1->text  = 'Test1';\n        $article1->setAuthor($user1);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Test2';\n        $article2->text  = 'Test2';\n        $article2->setAuthor($user1);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $phonenumber1              = new CmsPhonenumber();\n        $phonenumber1->phonenumber = '12345';\n\n        $phonenumber2              = new CmsPhonenumber();\n        $phonenumber2->phonenumber = '67890';\n\n        $this->_em->persist($phonenumber1);\n        $this->_em->persist($phonenumber2);\n\n        $user1->addPhonenumber($phonenumber1);\n\n        // DDC-2504\n        $otherClass  = new DDC2504OtherClass();\n        $childClass1 = new DDC2504ChildClass();\n        $childClass2 = new DDC2504ChildClass();\n\n        $childClass1->other = $otherClass;\n        $childClass2->other = $otherClass;\n\n        $otherClass->childClasses[] = $childClass1;\n        $otherClass->childClasses[] = $childClass2;\n\n        $this->_em->persist($childClass1);\n        $this->_em->persist($childClass2);\n        $this->_em->persist($otherClass);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->articleId           = $article1->id;\n        $this->userId              = $user1->getId();\n        $this->userId2             = $user2->getId();\n        $this->groupId             = $group1->id;\n        $this->ddc2504OtherClassId = $otherClass->id;\n        $this->ddc2504ChildClassId = $childClass1->id;\n\n        $this->username    = $user1->username;\n        $this->groupname   = $group1->name;\n        $this->topic       = $article1->topic;\n        $this->phonenumber = $phonenumber1->phonenumber;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/FlushEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\OnFlushEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * FlushEventTest\n */\nclass FlushEventTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testPersistNewEntitiesOnPreFlush(): void\n    {\n        $this->_em->getEventManager()->addEventListener(Events::onFlush, new OnFlushListener());\n\n        $user           = new CmsUser();\n        $user->username = 'romanb';\n        $user->name     = 'Roman';\n        $user->status   = 'Dev';\n\n        $this->_em->persist($user);\n\n        self::assertEquals(0, $user->phonenumbers->count());\n\n        $this->_em->flush();\n\n        self::assertEquals(1, $user->phonenumbers->count());\n        self::assertTrue($this->_em->contains($user->phonenumbers->get(0)));\n        self::assertSame($user->phonenumbers->get(0)->getUser(), $user);\n\n        self::assertFalse($user->phonenumbers->isDirty());\n\n        // Can be used together with SQL Logging to check that a subsequent flush has\n        // nothing to do. This proofs the correctness of the changes that happened in onFlush.\n        //echo \"SECOND FLUSH\";\n        //$this->_em->flush();\n    }\n\n    #[Group('DDC-2173')]\n    public function testPreAndOnFlushCalledAlways(): void\n    {\n        $listener = new OnFlushCalledListener();\n        $this->_em->getEventManager()->addEventListener(Events::onFlush, $listener);\n        $this->_em->getEventManager()->addEventListener(Events::preFlush, $listener);\n        $this->_em->getEventManager()->addEventListener(Events::postFlush, $listener);\n\n        $this->_em->flush();\n\n        self::assertEquals(1, $listener->preFlush);\n        self::assertEquals(1, $listener->onFlush);\n\n        $this->_em->flush();\n\n        self::assertEquals(2, $listener->preFlush);\n        self::assertEquals(2, $listener->onFlush);\n    }\n}\n\nclass OnFlushListener\n{\n    public function onFlush(OnFlushEventArgs $args): void\n    {\n        //echo \"---preFlush\".PHP_EOL;\n\n        $em  = $args->getObjectManager();\n        $uow = $em->getUnitOfWork();\n\n        foreach ($uow->getScheduledEntityInsertions() as $entity) {\n            if ($entity instanceof CmsUser) {\n                // Adds a phonenumber to every newly persisted CmsUser ...\n\n                $phone              = new CmsPhonenumber();\n                $phone->phonenumber = 12345;\n                // Update object model\n                $entity->addPhonenumber($phone);\n                // Invoke regular persist call\n                $em->persist($phone);\n                // Explicitly calculate the changeset since onFlush is raised\n                // after changeset calculation!\n                $uow->computeChangeSet($em->getClassMetadata($phone::class), $phone);\n\n                // Take a snapshot because the UoW wont do this for us, because\n                // the UoW did not visit this collection.\n                // Alternatively we could provide an ->addVisitedCollection() method\n                // on the UoW.\n                $entity->getPhonenumbers()->takeSnapshot();\n            }\n\n            /*foreach ($uow->getEntityChangeSet($entity) as $field => $change) {\n                list ($old, $new) = $change;\n\n                var_dump($old);\n            }*/\n        }\n    }\n}\n\nclass OnFlushCalledListener\n{\n    /** @var int */\n    public $preFlush = 0;\n\n    /** @var int */\n    public $onFlush = 0;\n\n    /** @var int */\n    public $postFlush = 0;\n\n    public function preFlush($args): void\n    {\n        $this->preFlush++;\n    }\n\n    public function onFlush($args): void\n    {\n        $this->onFlush++;\n    }\n\n    public function postFlush($args): void\n    {\n        $this->postFlush++;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/GH7877Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function uniqid;\n\nclass GH7877Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH7877ApplicationGeneratedIdEntity::class,\n            GH7877EntityWithNullableAssociation::class,\n        );\n    }\n\n    public function testSelfReferenceWithApplicationGeneratedIdMayBeNotNullable(): void\n    {\n        $entity         = new GH7877ApplicationGeneratedIdEntity();\n        $entity->parent = $entity;\n\n        $this->expectNotToPerformAssertions();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n    }\n\n    public function testCrossReferenceWithApplicationGeneratedIdMayBeNotNullable(): void\n    {\n        $entity1         = new GH7877ApplicationGeneratedIdEntity();\n        $entity1->parent = $entity1;\n        $entity2         = new GH7877ApplicationGeneratedIdEntity();\n        $entity2->parent = $entity1;\n\n        $this->expectNotToPerformAssertions();\n\n        // As long as we do not have entity-level commit order computation\n        // (see https://github.com/doctrine/orm/pull/10547),\n        // this only works when the UoW processes $entity1 before $entity2,\n        // so that the foreign key constraint E2 -> E1 can be satisfied.\n\n        $this->_em->persist($entity1);\n        $this->_em->persist($entity2);\n        $this->_em->flush();\n    }\n\n    public function testNullableForeignKeysMakeInsertOrderLessRelevant(): void\n    {\n        $entity1         = new GH7877EntityWithNullableAssociation();\n        $entity1->parent = $entity1;\n        $entity2         = new GH7877EntityWithNullableAssociation();\n        $entity2->parent = $entity1;\n\n        $this->expectNotToPerformAssertions();\n\n        // In contrast to the previous test, this case demonstrates that with NULLable\n        // associations, even without entity-level commit order computation\n        // (see https://github.com/doctrine/orm/pull/10547), we can get away with an\n        // insertion order of E2 before E1. That is because the UoW will schedule an extra\n        // update that saves the day - the foreign key reference will established only after\n        // all insertions have been performed.\n\n        $this->_em->persist($entity2);\n        $this->_em->persist($entity1);\n        $this->_em->flush();\n    }\n}\n\n#[ORM\\Entity]\nclass GH7877ApplicationGeneratedIdEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'string', length: 32)]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    /** (!) Note this uses \"nullable=false\" */\n    #[ORM\\ManyToOne(targetEntity: self::class)]\n    #[ORM\\JoinColumn(name: 'parent_id', referencedColumnName: 'id', nullable: false)]\n    public self $parent;\n\n    public function __construct()\n    {\n        $this->id = uniqid();\n    }\n}\n\n#[ORM\\Entity]\nclass GH7877EntityWithNullableAssociation\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'string', length: 32)]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    #[ORM\\ManyToOne(targetEntity: self::class)]\n    #[ORM\\JoinColumn(name: 'parent_id', referencedColumnName: 'id', nullable: true)]\n    public self $parent;\n\n    public function __construct()\n    {\n        $this->id = uniqid();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/HydrationCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\Tests\\Models\\Cms\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\n#[Group('DDC-1766')]\nclass HydrationCacheTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $user           = new CmsUser();\n        $user->name     = 'Benjamin';\n        $user->username = 'beberlei';\n        $user->status   = 'active';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testHydrationCache(): void\n    {\n        $cache = new ArrayAdapter();\n\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\Cms\\CmsUser u';\n\n        $this->_em->createQuery($dql)\n                  ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache))\n                  ->getResult();\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->createQuery($dql)\n                  ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache))\n                  ->getResult();\n\n        $this->assertQueryCount(0, 'Should not execute query. Its cached!');\n\n        $this->_em->createQuery($dql)\n                  ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache))\n                  ->getArrayResult();\n\n        $this->assertQueryCount(1, 'Hydration is part of cache key.');\n\n        $this->_em->createQuery($dql)\n                  ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache))\n                  ->getArrayResult();\n\n        $this->assertQueryCount(1, 'Hydration now cached');\n\n        $this->_em->createQuery($dql)\n                  ->setHydrationCacheProfile(new QueryCacheProfile(0, 'cachekey', $cache))\n                  ->getArrayResult();\n\n        self::assertTrue($cache->hasItem('cachekey'), 'Explicit cache key');\n\n        $this->_em->createQuery($dql)\n                      ->setHydrationCacheProfile(new QueryCacheProfile(0, 'cachekey', $cache))\n                      ->getArrayResult();\n        $this->assertQueryCount(2, 'Hydration now cached');\n    }\n\n    public function testHydrationParametersSerialization(): void\n    {\n        $cache = new ArrayAdapter();\n\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\Cms\\CmsUser u WHERE u.id = ?1';\n        $query = $this->_em->createQuery($dql)\n            ->setParameter(1, 1)\n            ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache));\n\n        $query->getResult();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $query->getResult();\n\n        $this->assertQueryCount(0, 'Should not execute query. Its cached!');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/IdentityMapTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * IdentityMapTest\n *\n * Tests correct behavior and usage of the identity map. Local values and associations\n * that are already fetched always prevail, unless explicitly refreshed.\n */\nclass IdentityMapTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testBasicIdentityManagement(): void\n    {\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'romanb';\n        $user->name     = 'Roman B.';\n\n        $address          = new CmsAddress();\n        $address->country = 'de';\n        $address->zip     = 1234;\n        $address->city    = 'Berlin';\n\n        $user->setAddress($address);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user2 = $this->_em->find($user::class, $user->getId());\n        self::assertNotSame($user2, $user);\n        $user3 = $this->_em->find($user::class, $user->getId());\n        self::assertSame($user2, $user3);\n\n        $address2 = $this->_em->find($address::class, $address->getId());\n        self::assertNotSame($address2, $address);\n        $address3 = $this->_em->find($address::class, $address->getId());\n        self::assertSame($address2, $address3);\n\n        self::assertSame($user2->getAddress(), $address2); // !!!\n    }\n\n    public function testSingleValuedAssociationIdentityMapBehaviorWithRefresh(): void\n    {\n        $address          = new CmsAddress();\n        $address->country = 'de';\n        $address->zip     = '12345';\n        $address->city    = 'Berlin';\n\n        $user1           = new CmsUser();\n        $user1->status   = 'dev';\n        $user1->username = 'romanb';\n        $user1->name     = 'Roman B.';\n\n        $user2           = new CmsUser();\n        $user2->status   = 'dev';\n        $user2->username = 'gblanco';\n        $user2->name     = 'Guilherme Blanco';\n\n        $address->setUser($user1);\n\n        $this->_em->persist($address);\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        self::assertSame($user1, $address->user);\n\n        //external update to CmsAddress\n        $this->_em->getConnection()->executeStatement('update cms_addresses set user_id = ?', [$user2->getId()]);\n\n        // But we want to have this external change!\n        // Solution 1: refresh(), broken atm!\n        $this->_em->refresh($address);\n\n        // Now the association should be \"correct\", referencing $user2\n        self::assertSame($user2, $address->user);\n        self::assertSame($user2->address, $address); // check back reference also\n\n        // Attention! refreshes can result in broken bidirectional associations! this is currently expected!\n        // $user1 still points to $address!\n        self::assertSame($user1->address, $address);\n    }\n\n    public function testSingleValuedAssociationIdentityMapBehaviorWithRefreshQuery(): void\n    {\n        $address          = new CmsAddress();\n        $address->country = 'de';\n        $address->zip     = '12345';\n        $address->city    = 'Berlin';\n\n        $user1           = new CmsUser();\n        $user1->status   = 'dev';\n        $user1->username = 'romanb';\n        $user1->name     = 'Roman B.';\n\n        $user2           = new CmsUser();\n        $user2->status   = 'dev';\n        $user2->username = 'gblanco';\n        $user2->name     = 'Guilherme Blanco';\n\n        $address->setUser($user1);\n\n        $this->_em->persist($address);\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        self::assertSame($user1, $address->user);\n\n        //external update to CmsAddress\n        $this->_em->getConnection()->executeStatement('update cms_addresses set user_id = ?', [$user2->getId()]);\n\n        //select\n        $q        = $this->_em->createQuery('select a, u from Doctrine\\Tests\\Models\\CMS\\CmsAddress a join a.user u');\n        $address2 = $q->getSingleResult();\n\n        self::assertSame($address, $address2);\n\n        // Should still be $user1\n        self::assertSame($user1, $address2->user);\n        self::assertNull($user2->address);\n\n        // But we want to have this external change!\n        // Solution 2: Alternatively, a refresh query should work\n        $q = $this->_em->createQuery('select a, u from Doctrine\\Tests\\Models\\CMS\\CmsAddress a join a.user u');\n        $q->setHint(Query::HINT_REFRESH, true);\n        $address3 = $q->getSingleResult();\n\n        self::assertSame($address, $address3); // should still be the same, always from identity map\n\n        // Now the association should be \"correct\", referencing $user2\n        self::assertSame($user2, $address2->user);\n        self::assertSame($user2->address, $address2); // check back reference also\n\n        // Attention! refreshes can result in broken bidirectional associations! this is currently expected!\n        // $user1 still points to $address2!\n        self::assertSame($user1->address, $address2);\n    }\n\n    public function testCollectionValuedAssociationIdentityMapBehaviorWithRefreshQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'romanb';\n        $user->name     = 'Roman B.';\n\n        $phone1              = new CmsPhonenumber();\n        $phone1->phonenumber = 123;\n\n        $phone2              = new CmsPhonenumber();\n        $phone2->phonenumber = 234;\n\n        $phone3              = new CmsPhonenumber();\n        $phone3->phonenumber = 345;\n\n        $user->addPhonenumber($phone1);\n        $user->addPhonenumber($phone2);\n        $user->addPhonenumber($phone3);\n\n        $this->_em->persist($user); // cascaded to phone numbers\n        $this->_em->flush();\n\n        self::assertCount(3, $user->getPhonenumbers());\n        self::assertFalse($user->getPhonenumbers()->isDirty());\n\n        //external update to CmsAddress\n        $this->_em->getConnection()->executeStatement('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]);\n\n        //select\n        $q     = $this->_em->createQuery('select u, p from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p');\n        $user2 = $q->getSingleResult();\n\n        self::assertSame($user, $user2);\n\n        // Should still be the same 3 phonenumbers\n        self::assertCount(3, $user2->getPhonenumbers());\n\n        // But we want to have this external change!\n        // Solution 1: refresh().\n        //$this->_em->refresh($user2); broken atm!\n        // Solution 2: Alternatively, a refresh query should work\n        $q = $this->_em->createQuery('select u, p from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p');\n        $q->setHint(Query::HINT_REFRESH, true);\n        $user3 = $q->getSingleResult();\n\n        self::assertSame($user, $user3); // should still be the same, always from identity map\n\n        // Now the collection should be refreshed with correct count\n        self::assertCount(4, $user3->getPhonenumbers());\n    }\n\n    #[Group('non-cacheable')]\n    public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh(): void\n    {\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'romanb';\n        $user->name     = 'Roman B.';\n\n        $phone1              = new CmsPhonenumber();\n        $phone1->phonenumber = 123;\n\n        $phone2              = new CmsPhonenumber();\n        $phone2->phonenumber = 234;\n\n        $phone3              = new CmsPhonenumber();\n        $phone3->phonenumber = 345;\n\n        $user->addPhonenumber($phone1);\n        $user->addPhonenumber($phone2);\n        $user->addPhonenumber($phone3);\n\n        $this->_em->persist($user); // cascaded to phone numbers\n        $this->_em->flush();\n\n        self::assertCount(3, $user->getPhonenumbers());\n\n        //external update to CmsAddress\n        $this->_em->getConnection()->executeStatement('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]);\n\n        //select\n        $q     = $this->_em->createQuery('select u, p from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.phonenumbers p');\n        $user2 = $q->getSingleResult();\n\n        self::assertSame($user, $user2);\n\n        // Should still be the same 3 phonenumbers\n        self::assertCount(3, $user2->getPhonenumbers());\n\n        // But we want to have this external change!\n        // Solution 1: refresh().\n        $this->_em->refresh($user2);\n\n        self::assertSame($user, $user2); // should still be the same, always from identity map\n\n        // Now the collection should be refreshed with correct count\n        self::assertCount(4, $user2->getPhonenumbers());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/IndexByAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\StockExchange\\Bond;\nuse Doctrine\\Tests\\Models\\StockExchange\\Market;\nuse Doctrine\\Tests\\Models\\StockExchange\\Stock;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('DDC-250')]\nclass IndexByAssociationTest extends OrmFunctionalTestCase\n{\n    private Market|null $market = null;\n\n    private Bond|null $bond = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('stockexchange');\n\n        parent::setUp();\n\n        $this->loadFixture();\n    }\n\n    public function loadFixture(): void\n    {\n        $this->market = new Market('Some Exchange');\n        $stock1       = new Stock('AAPL', 10, $this->market);\n        $stock2       = new Stock('GOOG', 20, $this->market);\n\n        $this->bond = new Bond('MyBond');\n        $this->bond->addStock($stock1);\n        $this->bond->addStock($stock2);\n\n        $this->_em->persist($this->market);\n        $this->_em->persist($stock1);\n        $this->_em->persist($stock2);\n        $this->_em->persist($this->bond);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testManyToOneFinder(): void\n    {\n        $market = $this->_em->find(Market::class, $this->market->getId());\n        assert($market instanceof Market);\n\n        self::assertCount(2, $market->stocks);\n        self::assertTrue(isset($market->stocks['AAPL']), 'AAPL symbol has to be key in indexed association.');\n        self::assertTrue(isset($market->stocks['GOOG']), 'GOOG symbol has to be key in indexed association.');\n        self::assertEquals('AAPL', $market->stocks['AAPL']->getSymbol());\n        self::assertEquals('GOOG', $market->stocks['GOOG']->getSymbol());\n    }\n\n    public function testManyToOneDQL(): void\n    {\n        $dql    = 'SELECT m, s FROM Doctrine\\Tests\\Models\\StockExchange\\Market m JOIN m.stocks s WHERE m.id = ?1';\n        $market = $this->_em->createQuery($dql)->setParameter(1, $this->market->getId())->getSingleResult();\n\n        self::assertCount(2, $market->stocks);\n        self::assertTrue(isset($market->stocks['AAPL']), 'AAPL symbol has to be key in indexed association.');\n        self::assertTrue(isset($market->stocks['GOOG']), 'GOOG symbol has to be key in indexed association.');\n        self::assertEquals('AAPL', $market->stocks['AAPL']->getSymbol());\n        self::assertEquals('GOOG', $market->stocks['GOOG']->getSymbol());\n    }\n\n    public function testManyToMany(): void\n    {\n        $bond = $this->_em->find(Bond::class, $this->bond->getId());\n\n        self::assertCount(2, $bond->stocks);\n        self::assertTrue(isset($bond->stocks['AAPL']), 'AAPL symbol has to be key in indexed association.');\n        self::assertTrue(isset($bond->stocks['GOOG']), 'GOOG symbol has to be key in indexed association.');\n        self::assertEquals('AAPL', $bond->stocks['AAPL']->getSymbol());\n        self::assertEquals('GOOG', $bond->stocks['GOOG']->getSymbol());\n    }\n\n    public function testManytoManyDQL(): void\n    {\n        $dql  = 'SELECT b, s FROM Doctrine\\Tests\\Models\\StockExchange\\Bond b JOIN b.stocks s WHERE b.id = ?1';\n        $bond = $this->_em->createQuery($dql)->setParameter(1, $this->bond->getId())->getSingleResult();\n\n        self::assertCount(2, $bond->stocks);\n        self::assertTrue(isset($bond->stocks['AAPL']), 'AAPL symbol has to be key in indexed association.');\n        self::assertTrue(isset($bond->stocks['GOOG']), 'GOOG symbol has to be key in indexed association.');\n        self::assertEquals('AAPL', $bond->stocks['AAPL']->getSymbol());\n        self::assertEquals('GOOG', $bond->stocks['GOOG']->getSymbol());\n    }\n\n    public function testDqlOverrideIndexBy(): void\n    {\n        $dql  = 'SELECT b, s FROM Doctrine\\Tests\\Models\\StockExchange\\Bond b JOIN b.stocks s INDEX BY s.id WHERE b.id = ?1';\n        $bond = $this->_em->createQuery($dql)->setParameter(1, $this->bond->getId())->getSingleResult();\n\n        self::assertCount(2, $bond->stocks);\n        self::assertFalse(isset($bond->stocks['AAPL']), 'AAPL symbol not exists in re-indexed association.');\n        self::assertFalse(isset($bond->stocks['GOOG']), 'GOOG symbol not exists in re-indexed association.');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/InsertableUpdatableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\Upsertable\\Insertable;\nuse Doctrine\\Tests\\Models\\Upsertable\\Updatable;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass InsertableUpdatableTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(Updatable::class, Insertable::class);\n    }\n\n    public function testNotInsertableIsFetchedFromDatabase(): void\n    {\n        $insertable                    = new Insertable();\n        $insertable->insertableContent = 'abcdefg';\n\n        $this->_em->persist($insertable);\n        $this->_em->flush();\n\n        // gets inserted from default value and fetches value from database\n        self::assertEquals('1234', $insertable->nonInsertableContent);\n\n        $insertable->nonInsertableContent = '5678';\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $insertable = $this->_em->find(Insertable::class, $insertable->id);\n\n        // during UPDATE statement it is not ignored\n        self::assertEquals('5678', $insertable->nonInsertableContent);\n    }\n\n    public function testNotUpdatableIsFetched(): void\n    {\n        $updatable                      = new Updatable();\n        $updatable->updatableContent    = 'foo';\n        $updatable->nonUpdatableContent = 'foo';\n\n        $this->_em->persist($updatable);\n        $this->_em->flush();\n\n        $updatable->updatableContent    = 'bar';\n        $updatable->nonUpdatableContent = 'baz';\n\n        $this->_em->flush();\n\n        self::assertEquals('foo', $updatable->nonUpdatableContent);\n\n        $this->_em->clear();\n\n        $cleanUpdatable = $this->_em->find(Updatable::class, $updatable->id);\n\n        self::assertEquals('bar', $cleanUpdatable->updatableContent);\n        self::assertEquals('foo', $cleanUpdatable->nonUpdatableContent);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/InvalidMappingDefinitionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy.\n */\nclass InvalidMappingDefinitionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n    }\n\n    public function testManyToManyRelationWithJoinTableOnTheWrongSide(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"Mapping error on field 'owners' in Doctrine\\Tests\\ORM\\Functional\\OwnedSideEntity : 'joinTable' can only be set on many-to-many owning side.\");\n\n        $this->createSchemaForModels(\n            OwningSideEntity::class,\n            OwnedSideEntity::class,\n        );\n    }\n}\n\n#[Table(name: 'owning_side_entities1')]\n#[Entity]\nclass OwningSideEntity\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    private int $id;\n\n    #[ORM\\ManyToMany(targetEntity: OwnedSideEntity::class, inversedBy: 'owners')]\n    #[ORM\\JoinTable(name: 'owning_owned')]\n    private Collection $relations;\n\n    public function __construct()\n    {\n        $this->relations = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getRelations(): Collection\n    {\n        return $this->relations;\n    }\n\n    public function addRelation(OwnedSideEntity $ownedSide): void\n    {\n        if (! $this->relations->contains($ownedSide)) {\n            $this->relations->add($ownedSide);\n            $ownedSide->addOwner($this);\n        }\n    }\n\n    public function removeRelation(OwnedSideEntity $ownedSide): void\n    {\n        if ($this->relations->removeElement($ownedSide)) {\n            $ownedSide->removeOwner($this);\n        }\n    }\n}\n\n#[Table(name: 'owned_side_entities1')]\n#[Entity]\nclass OwnedSideEntity\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    private int $id;\n\n    #[ORM\\Column(type: 'string', length: 255)]\n    private string $data;\n\n    #[ORM\\ManyToMany(targetEntity: OwningSideEntity::class, mappedBy: 'relations')]\n    #[ORM\\JoinTable(name: 'owning_owned')]\n    private Collection $owners;\n\n    public function __construct()\n    {\n        $this->owners = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getData(): string\n    {\n        return $this->data;\n    }\n\n    public function setData(string $data): void\n    {\n        $this->data = $data;\n    }\n\n    public function getOwners(): Collection\n    {\n        return $this->owners;\n    }\n\n    public function addOwner(OwningSideEntity $owningSide): void\n    {\n        if (! $this->owners->contains($owningSide)) {\n            $this->owners->add($owningSide);\n        }\n    }\n\n    public function removeOwner(OwningSideEntity $owningSide): void\n    {\n        $this->owners->removeElement($owningSide);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedChildClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedRootClass;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass JoinedTableCompositeKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('compositekeyinheritance');\n\n        parent::setUp();\n    }\n\n    public function testInsertWithCompositeKey(): void\n    {\n        $childEntity = new JoinedChildClass();\n        $this->_em->persist($childEntity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity = $this->findEntity();\n        self::assertEquals($childEntity, $entity);\n    }\n\n    #[Group('non-cacheable')]\n    public function testUpdateWithCompositeKey(): void\n    {\n        $childEntity = new JoinedChildClass();\n        $this->_em->persist($childEntity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity            = $this->findEntity();\n        $entity->extension = 'ext-new';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $persistedEntity = $this->findEntity();\n        self::assertEquals($entity, $persistedEntity);\n    }\n\n    private function findEntity(): JoinedChildClass\n    {\n        return $this->_em->find(\n            JoinedRootClass::class,\n            ['keyPart1' => 'part-1', 'keyPart2' => 'part-2'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/LifecycleCallbackTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Event\\PostLoadEventArgs;\nuse Doctrine\\ORM\\Event\\PostPersistEventArgs;\nuse Doctrine\\ORM\\Event\\PostRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PostUpdateEventArgs;\nuse Doctrine\\ORM\\Event\\PreFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PrePersistEventArgs;\nuse Doctrine\\ORM\\Event\\PreRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PreUpdateEventArgs;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\PostLoad;\nuse Doctrine\\ORM\\Mapping\\PostPersist;\nuse Doctrine\\ORM\\Mapping\\PostRemove;\nuse Doctrine\\ORM\\Mapping\\PostUpdate;\nuse Doctrine\\ORM\\Mapping\\PreFlush;\nuse Doctrine\\ORM\\Mapping\\PrePersist;\nuse Doctrine\\ORM\\Mapping\\PreRemove;\nuse Doctrine\\ORM\\Mapping\\PreUpdate;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\nuse function current;\nuse function iterator_to_array;\nuse function sprintf;\n\nclass LifecycleCallbackTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            LifecycleCallbackEventArgEntity::class,\n            LifecycleCallbackTestEntity::class,\n            LifecycleCallbackTestUser::class,\n            LifecycleCallbackCascader::class,\n        );\n    }\n\n    public function testPreSavePostSaveCallbacksAreInvoked(): void\n    {\n        $entity        = new LifecycleCallbackTestEntity();\n        $entity->value = 'hello';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertTrue($entity->prePersistCallbackInvoked);\n        self::assertTrue($entity->postPersistCallbackInvoked);\n\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n\n        $query  = $this->_em->createQuery('select e from Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackTestEntity e');\n        $result = $query->getResult();\n        self::assertTrue($result[0]::$postLoadCallbackInvoked);\n\n        $result[0]->value = 'hello again';\n\n        $this->_em->flush();\n\n        self::assertEquals('changed from preUpdate callback!', $result[0]->value);\n    }\n\n    public function testPreFlushCallbacksAreInvoked(): void\n    {\n        $entity        = new LifecycleCallbackTestEntity();\n        $entity->value = 'hello';\n        $this->_em->persist($entity);\n\n        $this->_em->flush();\n\n        self::assertTrue($entity->prePersistCallbackInvoked);\n        self::assertTrue($entity->preFlushCallbackInvoked);\n\n        $entity->preFlushCallbackInvoked = false;\n        $this->_em->flush();\n\n        self::assertTrue($entity->preFlushCallbackInvoked);\n\n        $entity->value                   = 'bye';\n        $entity->preFlushCallbackInvoked = false;\n        $this->_em->flush();\n\n        self::assertTrue($entity->preFlushCallbackInvoked);\n    }\n\n    public function testChangesDontGetLost(): void\n    {\n        $user = new LifecycleCallbackTestUser();\n        $user->setName('Bob');\n        $user->setValue('value');\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $user->setName('Alice');\n        $this->_em->flush(); // Triggers preUpdate\n\n        $this->_em->clear();\n\n        $user2 = $this->_em->find($user::class, $user->getId());\n\n        self::assertEquals('Alice', $user2->getName());\n        self::assertEquals('Hello World', $user2->getValue());\n    }\n\n    #[Group('DDC-194')]\n    public function testGetReferenceWithPostLoadEventIsDelayedUntilProxyTrigger(): void\n    {\n        $entity        = new LifecycleCallbackTestEntity();\n        $entity->value = 'hello';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $id = $entity->getId();\n\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n\n        $reference = $this->_em->getReference(LifecycleCallbackTestEntity::class, $id);\n        self::assertArrayNotHasKey('postLoadCallbackInvoked', (array) $reference);\n        $this->assertTrue($this->isUninitializedObject($reference));\n\n        $reference->getValue(); // trigger proxy load\n        self::assertTrue($reference::$postLoadCallbackInvoked);\n    }\n\n    #[Group('DDC-958')]\n    public function testPostLoadTriggeredOnRefresh(): void\n    {\n        $entity        = new LifecycleCallbackTestEntity();\n        $entity->value = 'hello';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $id = $entity->getId();\n\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n\n        $reference = $this->_em->find(LifecycleCallbackTestEntity::class, $id);\n        self::assertTrue($reference::$postLoadCallbackInvoked);\n        $reference::$postLoadCallbackInvoked = false;\n\n        $this->_em->refresh($reference);\n        self::assertTrue($reference::$postLoadCallbackInvoked, 'postLoad should be invoked when refresh() is called.');\n    }\n\n    #[Group('DDC-113')]\n    public function testCascadedEntitiesCallsPrePersist(): void\n    {\n        $e1 = new LifecycleCallbackTestEntity();\n        $e2 = new LifecycleCallbackTestEntity();\n\n        $c = new LifecycleCallbackCascader();\n        $this->_em->persist($c);\n\n        $c->entities[] = $e1;\n        $c->entities[] = $e2;\n        $e1->cascader  = $c;\n        $e2->cascader  = $c;\n\n        //$this->_em->persist($c);\n        $this->_em->flush();\n\n        self::assertTrue($e1->prePersistCallbackInvoked);\n        self::assertTrue($e2->prePersistCallbackInvoked);\n    }\n\n    #[Group('DDC-54')]\n    #[Group('DDC-3005')]\n    public function testCascadedEntitiesLoadedInPostLoad(): void\n    {\n        $e1 = new LifecycleCallbackTestEntity();\n        $e2 = new LifecycleCallbackTestEntity();\n\n        $c = new LifecycleCallbackCascader();\n        $this->_em->persist($c);\n\n        $c->entities[] = $e1;\n        $c->entities[] = $e2;\n        $e1->cascader  = $c;\n        $e2->cascader  = $c;\n\n        $this->_em->flush();\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n\n        $dql = <<<'DQL'\nSELECT\n    e, c\nFROM\n    Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackTestEntity AS e\nLEFT JOIN\n    e.cascader AS c\nWHERE\n    e.id IN (%s, %s)\nDQL;\n\n        $entities = $this\n            ->_em\n            ->createQuery(sprintf($dql, $e1->getId(), $e2->getId()))\n            ->getResult();\n\n        self::assertTrue(current($entities)::$postLoadCallbackInvoked);\n        self::assertTrue(current($entities)->postLoadCascaderNotNull);\n        self::assertTrue(current($entities)->cascader::$postLoadCallbackInvoked);\n        self::assertEquals(current($entities)->cascader->postLoadEntitiesCount, 2);\n    }\n\n    #[Group('DDC-54')]\n    #[Group('DDC-3005')]\n    public function testCascadedEntitiesNotLoadedInPostLoadDuringIteration(): void\n    {\n        $e1 = new LifecycleCallbackTestEntity();\n        $e2 = new LifecycleCallbackTestEntity();\n\n        $c = new LifecycleCallbackCascader();\n        $this->_em->persist($c);\n\n        $c->entities[] = $e1;\n        $c->entities[] = $e2;\n        $e1->cascader  = $c;\n        $e2->cascader  = $c;\n\n        $this->_em->flush();\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n        LifecycleCallbackCascader::$postLoadCallbackInvoked   = false;\n\n        $dql = <<<'DQL'\nSELECT\n    e, c\nFROM\n    Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackTestEntity AS e\nLEFT JOIN\n    e.cascader AS c\nWHERE\n    e.id IN (%s, %s)\nDQL;\n\n        $query = $this->_em->createQuery(sprintf($dql, $e1->getId(), $e2->getId()));\n\n        $iterableResult = iterator_to_array($query->toIterable());\n\n        foreach ($iterableResult as $entity) {\n            self::assertTrue($entity::$postLoadCallbackInvoked);\n            self::assertFalse($entity->postLoadCascaderNotNull);\n\n            break;\n        }\n    }\n\n    #[Group('DDC-54')]\n    #[Group('DDC-3005')]\n    public function testCascadedEntitiesNotLoadedInPostLoadDuringIterationWithSimpleObjectHydrator(): void\n    {\n        $this->_em->persist(new LifecycleCallbackTestEntity());\n        $this->_em->persist(new LifecycleCallbackTestEntity());\n\n        $this->_em->flush();\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n\n        $query  = $this->_em->createQuery(\n            'SELECT e FROM Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackTestEntity AS e',\n        );\n        $result = iterator_to_array($query->toIterable([], Query::HYDRATE_SIMPLEOBJECT));\n\n        foreach ($result as $entity) {\n            self::assertTrue($entity::$postLoadCallbackInvoked);\n            self::assertFalse($entity->postLoadCascaderNotNull);\n\n            break;\n        }\n    }\n\n    /**\n     * https://github.com/doctrine/orm/issues/6568\n     */\n    public function testPostLoadIsInvokedOnFetchJoinedEntities(): void\n    {\n        $entA = new LifecycleCallbackCascader();\n        $this->_em->persist($entA);\n\n        $entB1 = new LifecycleCallbackTestEntity();\n        $entB2 = new LifecycleCallbackTestEntity();\n\n        $entA->entities[] = $entB1;\n        $entA->entities[] = $entB2;\n        $entB1->cascader  = $entA;\n        $entB2->cascader  = $entA;\n\n        $this->_em->flush();\n        $this->_em->clear();\n        LifecycleCallbackTestEntity::$postLoadCallbackInvoked = false; // Reset the tracking of the postLoad invocation\n        LifecycleCallbackCascader::$postLoadCallbackInvoked   = false;\n\n        $dql = <<<'DQL'\nSELECT\n    entA, entB\nFROM\n    Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackCascader AS entA\nLEFT JOIN\n    entA.entities AS entB\nWHERE\n    entA.id = :entA_id\nDQL;\n\n        $fetchedA = $this\n            ->_em\n            ->createQuery($dql)->setParameter('entA_id', $entA->getId())\n            ->getOneOrNullResult();\n\n        self::assertTrue($fetchedA::$postLoadCallbackInvoked);\n        foreach ($fetchedA->entities as $fetchJoinedEntB) {\n            self::assertTrue($fetchJoinedEntB::$postLoadCallbackInvoked);\n        }\n    }\n\n    public function testLifecycleCallbacksGetInherited(): void\n    {\n        $childMeta = $this->_em->getClassMetadata(LifecycleCallbackChildEntity::class);\n        self::assertEquals(['prePersist' => [0 => 'doStuff']], $childMeta->lifecycleCallbacks);\n    }\n\n    public function testLifecycleListenerChangeUpdateChangeSet(): void\n    {\n        $listener = new LifecycleListenerPreUpdate();\n        $this->_em->getEventManager()->addEventListener(['preUpdate'], $listener);\n\n        $user = new LifecycleCallbackTestUser();\n        $user->setName('Bob');\n        $user->setValue('value');\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql = \"SELECT u FROM Doctrine\\Tests\\ORM\\Functional\\LifecycleCallbackTestUser u WHERE u.name = 'Bob'\";\n        $bob = $this->_em->createQuery($dql)->getSingleResult();\n        $bob->setName('Alice');\n\n        $this->_em->flush(); // preUpdate reverts Alice to Bob\n        $this->_em->clear();\n\n        $this->_em->getEventManager()->removeEventListener(['preUpdate'], $listener);\n\n        $bob = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertEquals('Bob', $bob->getName());\n    }\n\n    #[Group('DDC-1955')]\n    public function testLifecycleCallbackEventArgs(): void\n    {\n        $e = new LifecycleCallbackEventArgEntity();\n\n        $e->value = 'foo';\n        $this->_em->persist($e);\n        $this->_em->flush();\n\n        $e->value = 'var';\n        $this->_em->persist($e);\n        $this->_em->flush();\n\n        $this->_em->refresh($e);\n\n        $this->_em->remove($e);\n        $this->_em->flush();\n\n        self::assertArrayHasKey('preFlushHandler', $e->calls);\n        self::assertArrayHasKey('postLoadHandler', $e->calls);\n        self::assertArrayHasKey('prePersistHandler', $e->calls);\n        self::assertArrayHasKey('postPersistHandler', $e->calls);\n        self::assertArrayHasKey('preUpdateHandler', $e->calls);\n        self::assertArrayHasKey('postUpdateHandler', $e->calls);\n        self::assertArrayHasKey('preRemoveHandler', $e->calls);\n        self::assertArrayHasKey('postRemoveHandler', $e->calls);\n\n        self::assertInstanceOf(PreFlushEventArgs::class, $e->calls['preFlushHandler']);\n        self::assertInstanceOf(PostLoadEventArgs::class, $e->calls['postLoadHandler']);\n        self::assertInstanceOf(PrePersistEventArgs::class, $e->calls['prePersistHandler']);\n        self::assertInstanceOf(PostPersistEventArgs::class, $e->calls['postPersistHandler']);\n        self::assertInstanceOf(PreUpdateEventArgs::class, $e->calls['preUpdateHandler']);\n        self::assertInstanceOf(PostUpdateEventArgs::class, $e->calls['postUpdateHandler']);\n        self::assertInstanceOf(PreRemoveEventArgs::class, $e->calls['preRemoveHandler']);\n        self::assertInstanceOf(PostRemoveEventArgs::class, $e->calls['postRemoveHandler']);\n    }\n}\n\n#[Entity]\n#[HasLifecycleCallbacks]\nclass LifecycleCallbackTestUser\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $value = null;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $name = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getValue(): string\n    {\n        return $this->value;\n    }\n\n    public function setValue(string $value): void\n    {\n        $this->value = $value;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    #[PreUpdate]\n    public function testCallback(): void\n    {\n        $this->value = 'Hello World';\n    }\n}\n\n#[Table(name: 'lc_cb_test_entity')]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass LifecycleCallbackTestEntity\n{\n    /* test stuff */\n\n    /** @var bool */\n    public $prePersistCallbackInvoked = false;\n\n    /** @var bool */\n    public $postPersistCallbackInvoked = false;\n\n    /** @var bool */\n    public static $postLoadCallbackInvoked = false;\n\n    /** @var bool */\n    public $postLoadCascaderNotNull = false;\n\n    /** @var bool */\n    public $preFlushCallbackInvoked = false;\n\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    /** @var string */\n    #[Column(type: 'string', nullable: true)]\n    public $value;\n\n    /** @var LifecycleCallbackCascader */\n    #[ManyToOne(targetEntity: 'LifecycleCallbackCascader')]\n    #[JoinColumn(name: 'cascader_id', referencedColumnName: 'id')]\n    public $cascader;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getValue(): string\n    {\n        return $this->value;\n    }\n\n    #[PrePersist]\n    public function doStuffOnPrePersist(): void\n    {\n        $this->prePersistCallbackInvoked = true;\n    }\n\n    #[PostPersist]\n    public function doStuffOnPostPersist(): void\n    {\n        $this->postPersistCallbackInvoked = true;\n    }\n\n    #[PostLoad]\n    public function doStuffOnPostLoad(): void\n    {\n        self::$postLoadCallbackInvoked = true;\n        $this->postLoadCascaderNotNull = isset($this->cascader);\n    }\n\n    #[PreUpdate]\n    public function doStuffOnPreUpdate(): void\n    {\n        $this->value = 'changed from preUpdate callback!';\n    }\n\n    #[PreFlush]\n    public function doStuffOnPreFlush(): void\n    {\n        $this->preFlushCallbackInvoked = true;\n    }\n}\n\n#[Table(name: 'lc_cb_test_cascade')]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass LifecycleCallbackCascader\n{\n    /* test stuff */\n    /** @var bool */\n    public static $postLoadCallbackInvoked = false;\n\n    /** @var int */\n    public $postLoadEntitiesCount = 0;\n\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    /** @phpstan-var Collection<int, LifecycleCallbackTestEntity> */\n    #[OneToMany(targetEntity: 'LifecycleCallbackTestEntity', mappedBy: 'cascader', cascade: ['persist'])]\n    public $entities;\n\n    public function __construct()\n    {\n        $this->entities = new ArrayCollection();\n    }\n\n    #[PostLoad]\n    public function doStuffOnPostLoad(): void\n    {\n        self::$postLoadCallbackInvoked = true;\n        $this->postLoadEntitiesCount   = count($this->entities);\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[MappedSuperclass]\n#[HasLifecycleCallbacks]\nclass LifecycleCallbackParentEntity\n{\n    #[PrePersist]\n    public function doStuff(): void\n    {\n    }\n}\n\n#[Table(name: 'lc_cb_childentity')]\n#[Entity]\nclass LifecycleCallbackChildEntity extends LifecycleCallbackParentEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n}\n\nclass LifecycleListenerPreUpdate\n{\n    public function preUpdate(PreUpdateEventArgs $eventArgs): void\n    {\n        $eventArgs->setNewValue('name', 'Bob');\n    }\n}\n\n#[Entity]\n#[HasLifecycleCallbacks]\nclass LifecycleCallbackEventArgEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $value;\n\n    /** @var array<string, BaseLifecycleEventArgs> */\n    public $calls = [];\n\n    #[PostPersist]\n    public function postPersistHandler(PostPersistEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PrePersist]\n    public function prePersistHandler(PrePersistEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PostUpdate]\n    public function postUpdateHandler(PostUpdateEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PreUpdate]\n    public function preUpdateHandler(PreUpdateEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PostRemove]\n    public function postRemoveHandler(PostRemoveEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PreRemove]\n    public function preRemoveHandler(PreRemoveEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PreFlush]\n    public function preFlushHandler(PreFlushEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n\n    #[PostLoad]\n    public function postLoadHandler(PostLoadEventArgs $event): void\n    {\n        $this->calls[__FUNCTION__] = $event;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Locking/GearmanLockTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Locking;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse GearmanClient;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\nuse function max;\nuse function serialize;\n\n#[Group('locking_functional')]\nclass GearmanLockTest extends OrmFunctionalTestCase\n{\n    private GearmanClient|null $gearman = null;\n\n    private int $maxRunTime = 0;\n\n    private int $articleId;\n\n    protected function setUp(): void\n    {\n        if (! class_exists('GearmanClient', false)) {\n            self::markTestSkipped('pecl/gearman is required for this test to run.');\n        }\n\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->tasks = [];\n\n        $this->gearman = new GearmanClient();\n        $this->gearman->addServer(\n            $_SERVER['GEARMAN_HOST'] ?? null,\n            $_SERVER['GEARMAN_PORT'] ?? 4730,\n        );\n        $this->gearman->setCompleteCallback([$this, 'gearmanTaskCompleted']);\n\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->articleId = $article->id;\n    }\n\n    public function gearmanTaskCompleted($task): void\n    {\n        $this->maxRunTime = max($this->maxRunTime, $task->data());\n    }\n\n    public function testFindWithLock(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockWorked();\n    }\n\n    public function testFindWithWriteThenReadLock(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_READ);\n\n        $this->assertLockWorked();\n    }\n\n    public function testFindWithReadThenWriteLock(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_READ);\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockWorked();\n    }\n\n    public function testFindWithOneLock(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::NONE);\n\n        $this->assertLockDoesNotBlock();\n    }\n\n    public function testDqlWithLock(): void\n    {\n        $this->asyncDqlWithLock('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a', [], LockMode::PESSIMISTIC_WRITE);\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockWorked();\n    }\n\n    public function testLock(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n        $this->asyncLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockWorked();\n    }\n\n    public function testLock2(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n        $this->asyncLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_READ);\n\n        $this->assertLockWorked();\n    }\n\n    public function testLock3(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_READ);\n        $this->asyncLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockWorked();\n    }\n\n    public function testLock4(): void\n    {\n        $this->asyncFindWithLock(CmsArticle::class, $this->articleId, LockMode::NONE);\n        $this->asyncLock(CmsArticle::class, $this->articleId, LockMode::PESSIMISTIC_WRITE);\n\n        $this->assertLockDoesNotBlock();\n    }\n\n    protected function assertLockDoesNotBlock(): void\n    {\n        $this->assertLockWorked($onlyForSeconds = 1);\n    }\n\n    protected function assertLockWorked($forTime = 2, $notLongerThan = null): void\n    {\n        if ($notLongerThan === null) {\n            $notLongerThan = $forTime + 1;\n        }\n\n        $this->gearman->runTasks();\n\n        self::assertTrue(\n            $this->maxRunTime > $forTime,\n            'Because of locking this tests should have run at least ' . $forTime . ' seconds, ' .\n            'but only did for ' . $this->maxRunTime . ' seconds.',\n        );\n        self::assertTrue(\n            $this->maxRunTime < $notLongerThan,\n            'The longest task should not run longer than ' . $notLongerThan . ' seconds, ' .\n            'but did for ' . $this->maxRunTime . ' seconds.',\n        );\n    }\n\n    protected function asyncFindWithLock($entityName, $entityId, $lockMode): void\n    {\n        $this->startJob('findWithLock', [\n            'entityName' => $entityName,\n            'entityId' => $entityId,\n            'lockMode' => $lockMode,\n        ]);\n    }\n\n    protected function asyncDqlWithLock($dql, $params, $lockMode): void\n    {\n        $this->startJob('dqlWithLock', [\n            'dql' => $dql,\n            'dqlParams' => $params,\n            'lockMode' => $lockMode,\n        ]);\n    }\n\n    protected function asyncLock($entityName, $entityId, $lockMode): void\n    {\n        $this->startJob('lock', [\n            'entityName' => $entityName,\n            'entityId' => $entityId,\n            'lockMode' => $lockMode,\n        ]);\n    }\n\n    protected function startJob($fn, $fixture): void\n    {\n        $this->gearman->addTask($fn, serialize(\n            [\n                'conn' => $this->_em->getConnection()->getParams(),\n                'fixture' => $fixture,\n            ],\n        ));\n\n        self::assertEquals(GEARMAN_SUCCESS, $this->gearman->returnCode());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Locking/LockAgentWorker.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Locking;\n\nuse Closure;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\ORM\\Functional\\Locking\\Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\TestUtil;\nuse GearmanWorker;\nuse InvalidArgumentException;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function assert;\nuse function is_array;\nuse function microtime;\nuse function sleep;\nuse function unserialize;\n\nclass LockAgentWorker\n{\n    private EntityManagerInterface|null $em = null;\n\n    public static function run(): void\n    {\n        $lockAgent = new LockAgentWorker();\n\n        $worker = new GearmanWorker();\n        $worker->addServer(\n            $_SERVER['GEARMAN_HOST'] ?? null,\n            $_SERVER['GEARMAN_PORT'] ?? 4730,\n        );\n        $worker->addFunction('findWithLock', [$lockAgent, 'findWithLock']);\n        $worker->addFunction('dqlWithLock', [$lockAgent, 'dqlWithLock']);\n        $worker->addFunction('lock', [$lockAgent, 'lock']);\n\n        while ($worker->work()) {\n            if ($worker->returnCode() !== GEARMAN_SUCCESS) {\n                echo 'return_code: ' . $worker->returnCode() . \"\\n\";\n                break;\n            }\n        }\n    }\n\n    protected function process($job, Closure $do): float\n    {\n        $fixture = $this->processWorkload($job);\n\n        $s = microtime(true);\n        $this->em->beginTransaction();\n        $do($fixture, $this->em);\n\n        sleep(1);\n        $this->em->rollback();\n        $this->em->clear();\n        $this->em->close();\n        $this->em->getConnection()->close();\n\n        return microtime(true) - $s;\n    }\n\n    public function findWithLock($job): float\n    {\n        return $this->process($job, static function ($fixture, $em): void {\n            $entity = $em->find($fixture['entityName'], $fixture['entityId'], $fixture['lockMode']);\n        });\n    }\n\n    public function dqlWithLock($job): float\n    {\n        return $this->process($job, static function ($fixture, $em): void {\n            $query = $em->createQuery($fixture['dql']);\n            assert($query instanceof Query);\n            $query->setLockMode($fixture['lockMode']);\n            $query->setParameters($fixture['dqlParams']);\n            $result = $query->getResult();\n        });\n    }\n\n    public function lock($job): float\n    {\n        return $this->process($job, static function ($fixture, $em): void {\n            $entity = $em->find($fixture['entityName'], $fixture['entityId']);\n            $em->lock($entity, $fixture['lockMode']);\n        });\n    }\n\n    /** @return mixed[] */\n    protected function processWorkload($job): array\n    {\n        echo 'Received job: ' . $job->handle() . ' for function ' . $job->functionName() . \"\\n\";\n\n        $workload = $job->workload();\n        $workload = unserialize($workload);\n\n        if (! isset($workload['conn']) || ! is_array($workload['conn'])) {\n            throw new InvalidArgumentException('Missing Database parameters');\n        }\n\n        $this->em = $this->createEntityManager($workload['conn']);\n\n        if (! isset($workload['fixture'])) {\n            throw new InvalidArgumentException('Missing Fixture parameters');\n        }\n\n        return $workload['fixture'];\n    }\n\n    protected function createEntityManager(Connection $conn): EntityManagerInterface\n    {\n        $config = new Configuration();\n        TestUtil::configureProxies($config);\n        $config->setAutoGenerateProxyClasses(true);\n\n        $attributeDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../../Models']);\n        $config->setMetadataDriverImpl($attributeDriver);\n        $config->setMetadataCache(new ArrayAdapter());\n\n        $config->setQueryCache(new ArrayAdapter());\n\n        return new EntityManager($conn, $config);\n    }\n}\n\nLockAgentWorker::run();\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Locking/LockTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Locking;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\TransactionRequiredException;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('locking')]\nclass LockTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    #[DoesNotPerformAssertions]\n    public function testLockVersionedEntity(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockVersionedEntityMismatchThrowsException(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->expectException(OptimisticLockException::class);\n        $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version + 1);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockUnversionedEntityThrowsException(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'foo';\n        $user->status   = 'active';\n        $user->username = 'foo';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->expectException(OptimisticLockException::class);\n\n        $this->_em->lock($user, LockMode::OPTIMISTIC);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockUnmanagedEntityThrowsException(): void\n    {\n        $article = new CmsArticle();\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Entity ' . CmsArticle::class);\n\n        $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version + 1);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockPessimisticReadNoTransactionThrowsException(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->expectException(TransactionRequiredException::class);\n\n        $this->_em->lock($article, LockMode::PESSIMISTIC_READ);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockPessimisticWriteNoTransactionThrowsException(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->expectException(TransactionRequiredException::class);\n\n        $this->_em->lock($article, LockMode::PESSIMISTIC_WRITE);\n    }\n\n    #[Group('locking')]\n    public function testRefreshWithLockPessimisticWriteNoTransactionThrowsException(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->expectException(TransactionRequiredException::class);\n\n        $this->_em->refresh($article, LockMode::PESSIMISTIC_WRITE);\n    }\n\n    #[Group('DDC-178')]\n    #[Group('locking')]\n    public function testLockPessimisticWrite(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('Database Driver has no Write Lock support.');\n        }\n\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->_em->beginTransaction();\n        try {\n            $this->_em->lock($article, LockMode::PESSIMISTIC_WRITE);\n            $this->_em->commit();\n        } catch (Exception) {\n            $this->_em->rollback();\n        }\n\n        $lastLoggedQuery = $this->getLastLoggedQuery()['sql'];\n        // DBAL 2 logs a commit as last query.\n        if ($lastLoggedQuery === '\"COMMIT\"') {\n            $lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];\n        }\n\n        self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery);\n    }\n\n    #[Group('locking')]\n    public function testRefreshWithLockPessimisticWrite(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('Database Driver has no Write Lock support.');\n        }\n\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->_em->beginTransaction();\n        try {\n            $this->_em->refresh($article, LockMode::PESSIMISTIC_WRITE);\n            $this->_em->commit();\n        } catch (Exception) {\n            $this->_em->rollback();\n        }\n\n        $lastLoggedQuery = $this->getLastLoggedQuery()['sql'];\n        // DBAL 2 logs a commit as last query.\n        if ($lastLoggedQuery === '\"COMMIT\"') {\n            $lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];\n        }\n\n        self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery);\n    }\n\n    #[Group('DDC-178')]\n    public function testLockPessimisticRead(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('Database Driver has no Write Lock support.');\n        }\n\n        $article        = new CmsArticle();\n        $article->text  = 'my article';\n        $article->topic = 'Hello';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $this->_em->beginTransaction();\n\n        try {\n            $this->_em->lock($article, LockMode::PESSIMISTIC_READ);\n            $this->_em->commit();\n        } catch (Exception) {\n            $this->_em->rollback();\n        }\n\n        $lastLoggedQuery = $this->getLastLoggedQuery()['sql'];\n        // DBAL 2 logs a commit as last query.\n        if ($lastLoggedQuery === '\"COMMIT\"') {\n            $lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];\n        }\n\n        self::assertThat($lastLoggedQuery, self::logicalOr(\n            self::stringContains('FOR UPDATE'),\n            self::stringContains('FOR SHARE'),\n            self::stringContains('LOCK IN SHARE MODE'),\n        ));\n    }\n\n    #[Group('DDC-1693')]\n    public function testLockOptimisticNonVersionedThrowsExceptionInDQL(): void\n    {\n        $dql = 'SELECT u FROM ' . CmsUser::class . \" u WHERE u.username = 'gblanco'\";\n\n        $this->expectException(OptimisticLockException::class);\n        $this->expectExceptionMessage('The optimistic lock on an entity failed.');\n\n        $this->_em->createQuery($dql)\n                  ->setHint(Query::HINT_LOCK_MODE, LockMode::OPTIMISTIC)\n                  ->getSQL();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Locking/OptimisticTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Locking;\n\nuse DateTime;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\n\nuse function date;\nuse function strtotime;\n\nclass OptimisticTest extends OrmFunctionalTestCase\n{\n    private Connection $_conn;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->_conn = $this->_em->getConnection();\n    }\n\n    private function createSchema(): void\n    {\n        $this->createSchemaForModels(\n            OptimisticJoinedParent::class,\n            OptimisticJoinedChild::class,\n            OptimisticStandard::class,\n            OptimisticTimestamp::class,\n        );\n    }\n\n    public function testJoinedChildInsertSetsInitialVersionValue(): OptimisticJoinedChild\n    {\n        $this->createSchema();\n        $test = new OptimisticJoinedChild();\n\n        $test->name     = 'child';\n        $test->whatever = 'whatever';\n\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertEquals(1, $test->version);\n\n        return $test;\n    }\n\n    #[Depends('testJoinedChildInsertSetsInitialVersionValue')]\n    public function testJoinedChildFailureThrowsException(OptimisticJoinedChild $child): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Locking\\OptimisticJoinedChild t WHERE t.id = :id');\n\n        $q->setParameter('id', $child->id);\n\n        $test = $q->getSingleResult();\n\n        // Manually update/increment the version so we can try and save the same\n        // $test and make sure the exception is thrown saying the record was\n        // changed or updated since you read it\n        $this->_conn->executeQuery('UPDATE optimistic_joined_parent SET version = ? WHERE id = ?', [2, $test->id]);\n\n        // Now lets change a property and try and save it again\n        $test->whatever = 'ok';\n\n        try {\n            $this->_em->flush();\n        } catch (OptimisticLockException $e) {\n            self::assertSame($test, $e->getEntity());\n        }\n    }\n\n    public function testJoinedParentInsertSetsInitialVersionValue(): OptimisticJoinedParent\n    {\n        $this->createSchema();\n        $test = new OptimisticJoinedParent();\n\n        $test->name = 'parent';\n\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertEquals(1, $test->version);\n\n        return $test;\n    }\n\n    #[Depends('testJoinedParentInsertSetsInitialVersionValue')]\n    public function testJoinedParentFailureThrowsException(OptimisticJoinedParent $parent): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Locking\\OptimisticJoinedParent t WHERE t.id = :id');\n\n        $q->setParameter('id', $parent->id);\n\n        $test = $q->getSingleResult();\n\n        // Manually update/increment the version so we can try and save the same\n        // $test and make sure the exception is thrown saying the record was\n        // changed or updated since you read it\n        $this->_conn->executeQuery('UPDATE optimistic_joined_parent SET version = ? WHERE id = ?', [2, $test->id]);\n\n        // Now lets change a property and try and save it again\n        $test->name = 'WHATT???';\n\n        try {\n            $this->_em->flush();\n        } catch (OptimisticLockException $e) {\n            self::assertSame($test, $e->getEntity());\n        }\n    }\n\n    public function testMultipleFlushesDoIncrementalUpdates(): void\n    {\n        $this->createSchema();\n\n        $test = new OptimisticStandard();\n\n        for ($i = 0; $i < 5; $i++) {\n            $test->name = 'test' . $i;\n\n            $this->_em->persist($test);\n            $this->_em->flush();\n\n            self::assertIsInt($test->getVersion());\n            self::assertEquals($i + 1, $test->getVersion());\n        }\n    }\n\n    public function testStandardInsertSetsInitialVersionValue(): OptimisticStandard\n    {\n        $this->createSchema();\n\n        $test = new OptimisticStandard();\n\n        $test->name = 'test';\n\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertIsInt($test->getVersion());\n        self::assertEquals(1, $test->getVersion());\n\n        return $test;\n    }\n\n    #[Depends('testStandardInsertSetsInitialVersionValue')]\n    public function testStandardFailureThrowsException(OptimisticStandard $entity): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Locking\\OptimisticStandard t WHERE t.id = :id');\n\n        $q->setParameter('id', $entity->id);\n\n        $test = $q->getSingleResult();\n\n        // Manually update/increment the version so we can try and save the same\n        // $test and make sure the exception is thrown saying the record was\n        // changed or updated since you read it\n        $this->_conn->executeQuery('UPDATE optimistic_standard SET version = ? WHERE id = ?', [2, $test->id]);\n\n        // Now lets change a property and try and save it again\n        $test->name = 'WHATT???';\n\n        try {\n            $this->_em->flush();\n        } catch (OptimisticLockException $e) {\n            self::assertSame($test, $e->getEntity());\n        }\n    }\n\n    #[DoesNotPerformAssertions]\n    public function testLockWorksWithProxy(): void\n    {\n        $this->createSchema();\n        $test       = new OptimisticStandard();\n        $test->name = 'test';\n\n        $this->_em->persist($test);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $proxy = $this->_em->getReference(OptimisticStandard::class, $test->id);\n\n        $this->_em->lock($proxy, LockMode::OPTIMISTIC, 1);\n    }\n\n    public function testOptimisticTimestampSetsDefaultValue(): OptimisticTimestamp\n    {\n        $this->createSchema();\n        $test = new OptimisticTimestamp();\n\n        $test->name = 'Testing';\n\n        self::assertNull($test->version, 'Pre-Condition');\n\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertInstanceOf(DateTime::class, $test->version);\n\n        return $test;\n    }\n\n    #[Depends('testOptimisticTimestampSetsDefaultValue')]\n    public function testOptimisticTimestampFailureThrowsException(OptimisticTimestamp $entity): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Locking\\OptimisticTimestamp t WHERE t.id = :id');\n\n        $q->setParameter('id', $entity->id);\n\n        $test = $q->getSingleResult();\n\n        self::assertInstanceOf(DateTime::class, $test->version);\n\n        // Manually increment the version datetime column\n        $format = $this->_em->getConnection()->getDatabasePlatform()->getDateTimeFormatString();\n\n        $this->_conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', [date($format, strtotime($test->version->format($format)) + 3600), $test->id]);\n\n        // Try and update the record and it should throw an exception\n        $caughtException = null;\n        $test->name      = 'Testing again';\n\n        try {\n            $this->_em->flush();\n        } catch (OptimisticLockException $e) {\n            $caughtException = $e;\n        }\n\n        self::assertNotNull($caughtException, 'No OptimisticLockingException was thrown');\n        self::assertSame($test, $caughtException->getEntity());\n    }\n\n    #[Depends('testOptimisticTimestampSetsDefaultValue')]\n    public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Locking\\OptimisticTimestamp t WHERE t.id = :id');\n\n        $q->setParameter('id', $entity->id);\n\n        $test = $q->getSingleResult();\n\n        self::assertInstanceOf(DateTime::class, $test->version);\n\n        // Try to lock the record with an older timestamp and it should throw an exception\n        $caughtException = null;\n\n        try {\n            $expectedVersionExpired = DateTime::createFromFormat(\n                'U',\n                (string) ($test->version->getTimestamp() - 3600),\n            );\n\n            $this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired);\n        } catch (OptimisticLockException $e) {\n            $caughtException = $e;\n        }\n\n        self::assertNotNull($caughtException, 'No OptimisticLockingException was thrown');\n        self::assertSame($test, $caughtException->getEntity());\n    }\n}\n\n#[Table(name: 'optimistic_joined_parent')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['parent' => 'OptimisticJoinedParent', 'child' => 'OptimisticJoinedChild'])]\nclass OptimisticJoinedParent\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n}\n\n\n#[Table(name: 'optimistic_joined_child')]\n#[Entity]\nclass OptimisticJoinedChild extends OptimisticJoinedParent\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $whatever;\n}\n\n#[Table(name: 'optimistic_standard')]\n#[Entity]\nclass OptimisticStandard\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    #[Version]\n    #[Column(type: 'integer')]\n    private int $version;\n\n    public function getVersion(): int\n    {\n        return $this->version;\n    }\n}\n\n#[Table(name: 'optimistic_timestamp')]\n#[Entity]\nclass OptimisticTimestamp\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var DateTime */\n    #[Version]\n    #[Column(type: 'datetime')]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function get_class;\n\n/**\n * Basic many-to-many association tests.\n * (\"Working with associations\")\n */\nclass ManyToManyBasicAssociationTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testUnsetManyToMany(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(1);\n\n        // inverse side\n        // owning side!\n        unset($user->groups[0]->users[0], $user->groups[0]);\n\n        $this->_em->flush();\n\n        // Check that the link in the association table has been deleted\n        $this->assertGblancoGroupCountIs(0);\n    }\n\n    public function testBasicManyToManyJoin(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(1);\n        $this->_em->clear();\n\n        self::assertEquals(0, $this->_em->getUnitOfWork()->size());\n\n        $query = $this->_em->createQuery('select u, g from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.groups g');\n\n        $result = $query->getResult();\n\n        self::assertEquals(2, $this->_em->getUnitOfWork()->size());\n        self::assertInstanceOf(CmsUser::class, $result[0]);\n        self::assertEquals('Guilherme', $result[0]->name);\n        self::assertEquals(1, $result[0]->getGroups()->count());\n        $groups = $result[0]->getGroups();\n        self::assertEquals('Developers_0', $groups[0]->getName());\n\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($result[0]));\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($groups[0]));\n\n        self::assertInstanceOf(PersistentCollection::class, $groups);\n        self::assertInstanceOf(PersistentCollection::class, $groups[0]->getUsers());\n\n        $groups[0]->getUsers()->clear();\n        $groups->clear();\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select u, g from Doctrine\\Tests\\Models\\CMS\\CmsUser u join u.groups g');\n        self::assertCount(0, $query->getResult());\n    }\n\n    public function testManyToManyAddRemove(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $uRep = $this->_em->getRepository($user::class);\n\n        // Get user\n        $user = $uRep->findOneById($user->getId());\n\n        self::assertNotNull($user, 'Has to return exactly one entry.');\n\n        self::assertFalse($user->getGroups()->isInitialized());\n\n        // Check groups\n        self::assertEquals(2, $user->getGroups()->count());\n\n        self::assertTrue($user->getGroups()->isInitialized());\n\n        // Remove first group\n        unset($user->groups[0]);\n        //$user->getGroups()->remove(0);\n\n        $this->_em->flush();\n\n        self::assertFalse($user->getGroups()->isDirty());\n\n        $this->_em->clear();\n\n        // Reload same user\n        $user2 = $uRep->findOneById($user->getId());\n\n        // Check groups\n        self::assertEquals(1, $user2->getGroups()->count());\n    }\n\n    public function testManyToManyInverseSideIgnored(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(0);\n\n        $group       = new CmsGroup();\n        $group->name = 'Humans';\n\n        // modify directly, addUser() would also (properly) set the owning side\n        $group->users[] = $user;\n\n        $this->_em->persist($user);\n        $this->_em->persist($group);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Association should not exist\n        $user2 = $this->_em->find($user::class, $user->getId());\n\n        self::assertNotNull($user2, 'Has to return exactly one entry.');\n        self::assertEquals(0, $user2->getGroups()->count());\n    }\n\n    public function testManyToManyCollectionClearing(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);\n\n        // Check that there are indeed 10 links in the association table\n        $this->assertGblancoGroupCountIs($groupCount);\n\n        $user->groups->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->flush();\n\n        // Deletions of entire collections happen in a single query\n        $this->removeTransactionCommandsFromQueryLog();\n        self::assertQueryCount(1);\n\n        // Check that the links in the association table have been deleted\n        $this->assertGblancoGroupCountIs(0);\n    }\n\n    public function testManyToManyCollectionClearAndAdd(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);\n\n        $groups = $user->groups->toArray();\n        $user->groups->clear();\n\n        foreach ($groups as $group) {\n            $user->groups[] = $group;\n        }\n\n        self::assertInstanceOf(PersistentCollection::class, $user->groups);\n        self::assertTrue($user->groups->isDirty());\n\n        self::assertCount($groupCount, $user->groups, 'There should be 10 groups in the collection.');\n\n        $this->_em->flush();\n\n        $this->assertGblancoGroupCountIs($groupCount);\n    }\n\n    public function assertGblancoGroupCountIs(int $expectedGroupCount): void\n    {\n        $countDql = \"SELECT count(g.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g WHERE u.username = 'gblanco'\";\n        self::assertEquals(\n            $expectedGroupCount,\n            $this->_em->createQuery($countDql)->getSingleScalarResult(),\n            \"Failed to verify that CmsUser with username 'gblanco' has a group count of 10 with a DQL count query.\",\n        );\n    }\n\n    public function testRetrieveManyToManyAndAddMore(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n\n        $group       = new CmsGroup();\n        $group->name = 'Developers_Fresh';\n        $this->_em->persist($group);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $freshUser = $this->_em->find(CmsUser::class, $user->getId());\n        assert($freshUser instanceof CmsUser);\n        $newGroup = new CmsGroup();\n        $newGroup->setName('12Monkeys');\n        $freshUser->addGroup($newGroup);\n\n        self::assertFalse($freshUser->groups->isInitialized(), 'CmsUser::groups Collection has to be uninitialized for this test.');\n\n        $this->_em->flush();\n\n        self::assertFalse($freshUser->groups->isInitialized(), 'CmsUser::groups Collection has to be uninitialized for this test.');\n        self::assertCount(3, $freshUser->getGroups());\n        self::assertCount(3, $freshUser->getGroups()->getSnapshot(), 'Snapshot of CmsUser::groups should contain 3 entries.');\n\n        $this->_em->clear();\n\n        $freshUser = $this->_em->find(CmsUser::class, $user->getId());\n        self::assertCount(3, $freshUser->getGroups());\n    }\n\n    #[Group('DDC-130')]\n    public function testRemoveUserWithManyGroups(): void\n    {\n        $user   = $this->addCmsUserGblancoWithGroups(10);\n        $userId = $user->getId();\n\n        $this->_em->remove($user);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $this->_em->flush();\n\n        // This takes three queries: One to delete all user -> group join table rows for the user,\n        // one to delete all user -> tags join table rows for the user, and a final one to delete the user itself.\n        $this->removeTransactionCommandsFromQueryLog();\n        self::assertQueryCount(3);\n\n        $newUser = $this->_em->find($user::class, $userId);\n        self::assertNull($newUser);\n    }\n\n    #[Group('DDC-130')]\n    public function testRemoveGroupWithUser(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(5);\n\n        $anotherUser           = new CmsUser();\n        $anotherUser->username = 'joe_doe';\n        $anotherUser->name     = 'Joe Doe';\n        $anotherUser->status   = 'QA Engineer';\n\n        foreach ($user->getGroups() as $group) {\n            $anotherUser->addGroup($group);\n        }\n\n        $this->_em->persist($anotherUser);\n        $this->_em->flush();\n\n        foreach ($user->getGroups() as $group) {\n            $this->_em->remove($group);\n        }\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->flush();\n\n        // This takes 5 * 2 queries – for each group to be removed, one to remove all join table rows\n        // for the CmsGroup -> CmsUser inverse side association (for both users at once),\n        // and one for the group itself.\n        $this->removeTransactionCommandsFromQueryLog();\n        self::assertQueryCount(10);\n\n        // Changes to in-memory collection have been made and flushed\n        self::assertCount(0, $user->getGroups());\n        self::assertFalse($user->getGroups()->isDirty());\n\n        $this->_em->clear();\n\n        // Changes have been made to the database\n        $newUser = $this->_em->find($user::class, $user->getId());\n        self::assertCount(0, $newUser->getGroups());\n    }\n\n    public function testDereferenceCollectionDelete(): void\n    {\n        $user         = $this->addCmsUserGblancoWithGroups(2);\n        $user->groups = null;\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->flush();\n\n        // It takes one query to remove all join table rows for the user at once\n        $this->removeTransactionCommandsFromQueryLog();\n        self::assertQueryCount(1);\n\n        $this->_em->clear();\n\n        $newUser = $this->_em->find($user::class, $user->getId());\n        self::assertCount(0, $newUser->getGroups());\n    }\n\n    #[Group('DDC-839')]\n    public function testWorkWithDqlHydratedEmptyCollection(): void\n    {\n        $user        = $this->addCmsUserGblancoWithGroups(0);\n        $group       = new CmsGroup();\n        $group->name = 'Developers0';\n        $this->_em->persist($group);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $newUser = $this->_em->createQuery('SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.groups g WHERE u.id = ?1')\n                             ->setParameter(1, $user->getId())\n                             ->getSingleResult();\n        self::assertCount(0, $newUser->groups);\n        self::assertInstanceOf(AssociationMapping::class, $newUser->groups->getMapping());\n\n        $newUser->addGroup($group);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $newUser = $this->_em->find($user::class, $user->getId());\n        self::assertCount(1, $newUser->groups);\n    }\n\n    public function addCmsUserGblancoWithGroups(int $groupCount = 1): CmsUser\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        for ($i = 0; $i < $groupCount; ++$i) {\n            $group       = new CmsGroup();\n            $group->name = 'Developers_' . $i;\n            $user->addGroup($group);\n        }\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        self::assertNotNull($user->getId(), \"User 'gblanco' should have an ID assigned after the persist()/flush() operation.\");\n\n        return $user;\n    }\n\n    #[Group('DDC-978')]\n    public function testClearAndResetCollection(): void\n    {\n        $user         = $this->addCmsUserGblancoWithGroups(2);\n        $group1       = new CmsGroup();\n        $group1->name = 'Developers_New1';\n        $group2       = new CmsGroup();\n        $group2->name = 'Developers_New2';\n\n        $this->_em->persist($group1);\n        $this->_em->persist($group2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $coll         = new ArrayCollection([$group1, $group2]);\n        $user->groups = $coll;\n        $this->_em->flush();\n        self::assertInstanceOf(\n            PersistentCollection::class,\n            $user->groups,\n            'UnitOfWork should have replaced ArrayCollection with PersistentCollection.',\n        );\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n        self::assertCount(2, $user->groups);\n        self::assertEquals('Developers_New1', $user->groups[0]->name);\n        self::assertEquals('Developers_New2', $user->groups[1]->name);\n    }\n\n    #[Group('DDC-733')]\n    public function testInitializePersistentCollection(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n        $this->_em->getUnitOfWork()->initializeObject($user->groups);\n        self::assertTrue($user->groups->isInitialized(), 'Collection should be initialized after calling UnitOfWork::initializeObject()');\n    }\n\n    #[Group('DDC-1189')]\n    #[Group('DDC-956')]\n    public function testClearBeforeLazyLoad(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(4);\n\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n        $user->groups->clear();\n        self::assertCount(0, $user->groups);\n\n        $this->_em->flush();\n\n        $user = $this->_em->find($user::class, $user->id);\n        self::assertCount(0, $user->groups);\n    }\n\n    #[Group('DDC-3952')]\n    public function testManyToManyOrderByIsNotIgnored(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(1);\n\n        $group1 = new CmsGroup();\n        $group2 = new CmsGroup();\n        $group3 = new CmsGroup();\n\n        $group1->name = 'C';\n        $group2->name = 'A';\n        $group3->name = 'B';\n\n        $user->addGroup($group1);\n        $user->addGroup($group2);\n        $user->addGroup($group3);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $criteria = Criteria::create(true)\n            ->orderBy(['name' => Order::Ascending]);\n\n        self::assertEquals(\n            ['A', 'B', 'C', 'Developers_0'],\n            $user\n                ->getGroups()\n                ->matching($criteria)\n                ->map(static fn (CmsGroup $group) => $group->getName())\n                ->toArray(),\n        );\n    }\n\n    #[Group('DDC-3952')]\n    public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $tag1 = new CmsTag();\n        $tag2 = new CmsTag();\n        $tag3 = new CmsTag();\n\n        $tag1->name = 'C';\n        $tag2->name = 'A';\n        $tag3->name = 'B';\n\n        $user->addTag($tag1);\n        $user->addTag($tag2);\n        $user->addTag($tag3);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $criteria = Criteria::create(true)\n            ->orderBy(['name' => Order::Ascending]);\n\n        self::assertEquals(\n            ['A', 'B', 'C'],\n            $user\n                ->getTags()\n                ->matching($criteria)\n                ->map(static fn (CmsTag $tag) => $tag->getName())\n                ->toArray(),\n        );\n    }\n\n    public function testMatchingWithLimit(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->setMaxResults(1);\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(1, $result);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    public function testMatchingWithOffset(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->setFirstResult(1);\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(1, $result);\n\n        $firstGroup = $result->first();\n        self::assertEquals('Developers_1', $firstGroup->name);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    public function testMatchingWithLimitAndOffset(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(5);\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->setFirstResult(1)->setMaxResults(3);\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(3, $result);\n\n        $firstGroup = $result->first();\n        self::assertEquals('Developers_1', $firstGroup->name);\n\n        $lastGroup = $result->last();\n        self::assertEquals('Developers_3', $lastGroup->name);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    public function testMatching(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find($user::class, $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq('name', (string) 'Developers_0'));\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(1, $result);\n\n        $firstGroup = $result->first();\n        self::assertEquals('Developers_0', $firstGroup->name);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    public function testMatchingWithInCondition(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find(get_class($user), $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->where(Criteria::expr()->in('name', ['Developers_1']));\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(1, $result);\n        self::assertEquals('Developers_1', $result[0]->name);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    public function testMatchingWithNotInCondition(): void\n    {\n        $user = $this->addCmsUserGblancoWithGroups(2);\n        $this->_em->clear();\n\n        $user = $this->_em->find(get_class($user), $user->id);\n\n        $groups = $user->groups;\n        self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection');\n\n        $criteria = Criteria::create(true)->where(Criteria::expr()->notIn('name', ['Developers_0']));\n        $result   = $groups->matching($criteria);\n\n        self::assertCount(1, $result);\n        self::assertEquals('Developers_1', $result[0]->name);\n\n        self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection');\n    }\n\n    private function removeTransactionCommandsFromQueryLog(): void\n    {\n        $log = $this->getQueryLog();\n\n        foreach ($log->queries as $key => $entry) {\n            if ($entry['sql'] === '\"START TRANSACTION\"' || $entry['sql'] === '\"COMMIT\"') {\n                unset($log->queries[$key]);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\n\n/**\n * Tests a bidirectional many-to-many association mapping (without inheritance).\n * Owning side is ECommerceProduct, inverse side is ECommerceCategory.\n */\nclass ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociationTestCase\n{\n    /** @var string */\n    protected $firstField = 'product_id';\n\n    /** @var string */\n    protected $secondField = 'category_id';\n\n    /** @var string */\n    protected $table = 'ecommerce_products_categories';\n\n    private ECommerceProduct $firstProduct;\n\n    private ECommerceProduct $secondProduct;\n\n    private ECommerceCategory $firstCategory;\n\n    private ECommerceCategory $secondCategory;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->firstProduct = new ECommerceProduct();\n        $this->firstProduct->setName('First Product');\n        $this->secondProduct = new ECommerceProduct();\n        $this->secondProduct->setName('Second Product');\n        $this->firstCategory = new ECommerceCategory();\n        $this->firstCategory->setName('Business');\n        $this->secondCategory = new ECommerceCategory();\n        $this->secondCategory->setName('Home');\n    }\n\n    public function testSavesAManyToManyAssociationWithCascadeSaveSet(): void\n    {\n        $this->firstProduct->addCategory($this->firstCategory);\n        $this->firstProduct->addCategory($this->secondCategory);\n        $this->_em->persist($this->firstProduct);\n        $this->_em->flush();\n\n        $this->assertForeignKeysContain($this->firstProduct->getId(), $this->firstCategory->getId());\n        $this->assertForeignKeysContain($this->firstProduct->getId(), $this->secondCategory->getId());\n    }\n\n    public function testRemovesAManyToManyAssociation(): void\n    {\n        $this->firstProduct->addCategory($this->firstCategory);\n        $this->firstProduct->addCategory($this->secondCategory);\n        $this->_em->persist($this->firstProduct);\n        $this->firstProduct->removeCategory($this->firstCategory);\n\n        $this->_em->flush();\n\n        $this->assertForeignKeysNotContain($this->firstProduct->getId(), $this->firstCategory->getId());\n        $this->assertForeignKeysContain($this->firstProduct->getId(), $this->secondCategory->getId());\n\n        $this->firstProduct->getCategories()->remove(1);\n        $this->_em->flush();\n\n        $this->assertForeignKeysNotContain($this->firstProduct->getId(), $this->secondCategory->getId());\n    }\n\n    public function testEagerLoadFromInverseSideAndLazyLoadFromOwningSide(): void\n    {\n        $this->createLoadingFixture();\n        $categories = $this->findCategories();\n        $this->assertLazyLoadFromOwningSide($categories);\n    }\n\n    public function testEagerLoadFromOwningSideAndLazyLoadFromInverseSide(): void\n    {\n        $this->createLoadingFixture();\n        $products = $this->findProducts();\n        $this->assertLazyLoadFromInverseSide($products);\n    }\n\n    private function createLoadingFixture(): void\n    {\n        $this->firstProduct->addCategory($this->firstCategory);\n        $this->firstProduct->addCategory($this->secondCategory);\n        $this->secondProduct->addCategory($this->firstCategory);\n        $this->secondProduct->addCategory($this->secondCategory);\n        $this->_em->persist($this->firstProduct);\n        $this->_em->persist($this->secondProduct);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    /** @phpstan-return list<ECommerceProduct> */\n    protected function findProducts(): array\n    {\n        $query  = $this->_em->createQuery('SELECT p, c FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p LEFT JOIN p.categories c ORDER BY p.id, c.id');\n        $result = $query->getResult();\n        self::assertCount(2, $result);\n        $cats1 = $result[0]->getCategories();\n        $cats2 = $result[1]->getCategories();\n        self::assertTrue($cats1->isInitialized());\n        self::assertTrue($cats2->isInitialized());\n        self::assertFalse($cats1[0]->getProducts()->isInitialized());\n        self::assertFalse($cats2[0]->getProducts()->isInitialized());\n\n        return $result;\n    }\n\n    /** @phpstan-return list<ECommerceCategory> */\n    protected function findCategories(): array\n    {\n        $query  = $this->_em->createQuery('SELECT c, p FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory c LEFT JOIN c.products p ORDER BY c.id, p.id');\n        $result = $query->getResult();\n        self::assertCount(2, $result);\n        self::assertInstanceOf(ECommerceCategory::class, $result[0]);\n        self::assertInstanceOf(ECommerceCategory::class, $result[1]);\n        $prods1 = $result[0]->getProducts();\n        $prods2 = $result[1]->getProducts();\n        self::assertTrue($prods1->isInitialized());\n        self::assertTrue($prods2->isInitialized());\n\n        self::assertFalse($prods1[0]->getCategories()->isInitialized());\n        self::assertFalse($prods2[0]->getCategories()->isInitialized());\n\n        return $result;\n    }\n\n    /** @phpstan-param list<ECommerceProduct> */\n    public function assertLazyLoadFromInverseSide(array $products): void\n    {\n        [$firstProduct, $secondProduct] = $products;\n\n        $firstProductCategories  = $firstProduct->getCategories();\n        $secondProductCategories = $secondProduct->getCategories();\n\n        self::assertCount(2, $firstProductCategories);\n        self::assertCount(2, $secondProductCategories);\n\n        self::assertSame($firstProductCategories[0], $secondProductCategories[0]);\n        self::assertSame($firstProductCategories[1], $secondProductCategories[1]);\n\n        $firstCategoryProducts  = $firstProductCategories[0]->getProducts();\n        $secondCategoryProducts = $firstProductCategories[1]->getProducts();\n\n        self::assertFalse($firstCategoryProducts->isInitialized());\n        self::assertFalse($secondCategoryProducts->isInitialized());\n        self::assertEquals(0, $firstCategoryProducts->unwrap()->count());\n        self::assertEquals(0, $secondCategoryProducts->unwrap()->count());\n\n        self::assertCount(2, $firstCategoryProducts); // lazy-load\n        self::assertTrue($firstCategoryProducts->isInitialized());\n        self::assertFalse($secondCategoryProducts->isInitialized());\n        self::assertCount(2, $secondCategoryProducts); // lazy-load\n        self::assertTrue($secondCategoryProducts->isInitialized());\n\n        self::assertInstanceOf(ECommerceProduct::class, $firstCategoryProducts[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $firstCategoryProducts[1]);\n        self::assertInstanceOf(ECommerceProduct::class, $secondCategoryProducts[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $secondCategoryProducts[1]);\n\n        $this->assertCollectionEquals($firstCategoryProducts, $secondCategoryProducts);\n    }\n\n    /** @phpstan-param list<ECommerceCategory> */\n    public function assertLazyLoadFromOwningSide(array $categories): void\n    {\n        [$firstCategory, $secondCategory] = $categories;\n\n        $firstCategoryProducts  = $firstCategory->getProducts();\n        $secondCategoryProducts = $secondCategory->getProducts();\n\n        self::assertCount(2, $firstCategoryProducts);\n        self::assertCount(2, $secondCategoryProducts);\n\n        self::assertSame($firstCategoryProducts[0], $secondCategoryProducts[0]);\n        self::assertSame($firstCategoryProducts[1], $secondCategoryProducts[1]);\n\n        $firstProductCategories  = $firstCategoryProducts[0]->getCategories();\n        $secondProductCategories = $firstCategoryProducts[1]->getCategories();\n\n        self::assertFalse($firstProductCategories->isInitialized());\n        self::assertFalse($secondProductCategories->isInitialized());\n        self::assertEquals(0, $firstProductCategories->unwrap()->count());\n        self::assertEquals(0, $secondProductCategories->unwrap()->count());\n\n        self::assertCount(2, $firstProductCategories); // lazy-load\n        self::assertTrue($firstProductCategories->isInitialized());\n        self::assertFalse($secondProductCategories->isInitialized());\n        self::assertCount(2, $secondProductCategories); // lazy-load\n        self::assertTrue($secondProductCategories->isInitialized());\n\n        self::assertInstanceOf(ECommerceCategory::class, $firstProductCategories[0]);\n        self::assertInstanceOf(ECommerceCategory::class, $firstProductCategories[1]);\n        self::assertInstanceOf(ECommerceCategory::class, $secondProductCategories[0]);\n        self::assertInstanceOf(ECommerceCategory::class, $secondProductCategories[1]);\n\n        $this->assertCollectionEquals($firstProductCategories, $secondProductCategories);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ManyToManyEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * ManyToManyEventTest\n */\nclass ManyToManyEventTest extends OrmFunctionalTestCase\n{\n    private PostUpdateListener $listener;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->listener = new PostUpdateListener();\n        $evm            = $this->_em->getEventManager();\n        $evm->addEventListener(Events::postUpdate, $this->listener);\n    }\n\n    public function testListenerShouldBeNotifiedWhenNewCollectionEntryAdded(): void\n    {\n        $user        = $this->createNewValidUser();\n        $group       = new CmsGroup();\n        $group->name = 'admins';\n\n        $this->_em->persist($user);\n        $this->_em->persist($group);\n        $this->_em->flush();\n        self::assertFalse($this->listener->wasNotified);\n\n        $user->addGroup($group);\n        $this->_em->flush();\n\n        self::assertTrue($this->listener->wasNotified);\n    }\n\n    public function testListenerShouldBeNotifiedWhenCollectionEntryRemoved(): void\n    {\n        $user        = $this->createNewValidUser();\n        $group       = new CmsGroup();\n        $group->name = 'admins';\n        $user->addGroup($group);\n\n        $this->_em->persist($user);\n        $this->_em->persist($group);\n        $this->_em->flush();\n        self::assertFalse($this->listener->wasNotified);\n\n        $user->getGroups()->removeElement($group);\n        $this->_em->flush();\n\n        self::assertTrue($this->listener->wasNotified);\n    }\n\n    private function createNewValidUser(): CmsUser\n    {\n        $user           = new CmsUser();\n        $user->username = 'fran6co';\n        $user->name     = 'Francisco Facioni';\n        $group          = new CmsGroup();\n        $group->name    = 'users';\n        $user->addGroup($group);\n\n        return $user;\n    }\n}\n\nclass PostUpdateListener\n{\n    /** @var bool */\n    public $wasNotified = false;\n\n    public function postUpdate($args): void\n    {\n        $this->wasNotified = true;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\n\n/**\n * Tests a self referential many-to-many association mapping (from a model to the same model, without inheritance).\n * For simplicity the relation duplicates entries in the association table\n * to remain symmetrical.\n */\nclass ManyToManySelfReferentialAssociationTest extends AbstractManyToManyAssociationTestCase\n{\n    /** @var string */\n    protected $firstField = 'product_id';\n\n    /** @var string */\n    protected $secondField = 'related_id';\n\n    /** @var string */\n    protected $table = 'ecommerce_products_related';\n\n    private ECommerceProduct $firstProduct;\n\n    private ECommerceProduct $secondProduct;\n\n    private ECommerceProduct $firstRelated;\n\n    private ECommerceProduct $secondRelated;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->firstProduct  = new ECommerceProduct();\n        $this->secondProduct = new ECommerceProduct();\n        $this->firstRelated  = new ECommerceProduct();\n        $this->firstRelated->setName('Business');\n        $this->secondRelated = new ECommerceProduct();\n        $this->secondRelated->setName('Home');\n    }\n\n    public function testSavesAManyToManyAssociationWithCascadeSaveSet(): void\n    {\n        $this->firstProduct->addRelated($this->firstRelated);\n        $this->firstProduct->addRelated($this->secondRelated);\n        $this->_em->persist($this->firstProduct);\n        $this->_em->flush();\n\n        $this->assertForeignKeysContain(\n            $this->firstProduct->getId(),\n            $this->firstRelated->getId(),\n        );\n        $this->assertForeignKeysContain(\n            $this->firstProduct->getId(),\n            $this->secondRelated->getId(),\n        );\n    }\n\n    public function testRemovesAManyToManyAssociation(): void\n    {\n        $this->firstProduct->addRelated($this->firstRelated);\n        $this->firstProduct->addRelated($this->secondRelated);\n        $this->_em->persist($this->firstProduct);\n        $this->firstProduct->removeRelated($this->firstRelated);\n\n        $this->_em->flush();\n\n        $this->assertForeignKeysNotContain(\n            $this->firstProduct->getId(),\n            $this->firstRelated->getId(),\n        );\n        $this->assertForeignKeysContain(\n            $this->firstProduct->getId(),\n            $this->secondRelated->getId(),\n        );\n    }\n\n    public function testEagerLoadsOwningSide(): void\n    {\n        $this->createLoadingFixture();\n        $products = $this->findProducts();\n        $this->assertLoadingOfOwningSide($products);\n    }\n\n    public function testLazyLoadsOwningSide(): void\n    {\n        $this->createLoadingFixture();\n\n        $metadata                                        = $this->_em->getClassMetadata(ECommerceProduct::class);\n        $metadata->associationMappings['related']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query    = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p');\n        $products = $query->getResult();\n        $this->assertLoadingOfOwningSide($products);\n    }\n\n    /** @phpstan-param list<ECommerceProduct> $products */\n    public function assertLoadingOfOwningSide(array $products): void\n    {\n        [$firstProduct, $secondProduct] = $products;\n        self::assertCount(2, $firstProduct->getRelated());\n        self::assertCount(2, $secondProduct->getRelated());\n\n        $categories      = $firstProduct->getRelated();\n        $firstRelatedBy  = $categories[0]->getRelated();\n        $secondRelatedBy = $categories[1]->getRelated();\n\n        self::assertCount(2, $firstRelatedBy);\n        self::assertCount(2, $secondRelatedBy);\n\n        self::assertInstanceOf(ECommerceProduct::class, $firstRelatedBy[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $firstRelatedBy[1]);\n        self::assertInstanceOf(ECommerceProduct::class, $secondRelatedBy[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $secondRelatedBy[1]);\n\n        $this->assertCollectionEquals($firstRelatedBy, $secondRelatedBy);\n    }\n\n    protected function createLoadingFixture(): void\n    {\n        $this->firstProduct->addRelated($this->firstRelated);\n        $this->firstProduct->addRelated($this->secondRelated);\n        $this->secondProduct->addRelated($this->firstRelated);\n        $this->secondProduct->addRelated($this->secondRelated);\n        $this->_em->persist($this->firstProduct);\n        $this->_em->persist($this->secondProduct);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    /** @phpstan-return list<ECommerceProduct> */\n    protected function findProducts(): array\n    {\n        $query = $this->_em->createQuery('SELECT p, r FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p LEFT JOIN p.related r ORDER BY p.id, r.id');\n\n        return $query->getResult();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\n\n/**\n * Tests a unidirectional many-to-many association mapping (without inheritance).\n * Inverse side is not present.\n */\nclass ManyToManyUnidirectionalAssociationTest extends AbstractManyToManyAssociationTestCase\n{\n    /** @var string */\n    protected $firstField = 'cart_id';\n\n    /** @var string */\n    protected $secondField = 'product_id';\n\n    /** @var string */\n    protected $table = 'ecommerce_carts_products';\n\n    private ECommerceProduct $firstProduct;\n\n    private ECommerceProduct $secondProduct;\n\n    private ECommerceCart $firstCart;\n\n    private ECommerceCart $secondCart;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->firstProduct = new ECommerceProduct();\n        $this->firstProduct->setName('Doctrine 1.x Manual');\n        $this->secondProduct = new ECommerceProduct();\n        $this->secondProduct->setName('Doctrine 2.x Manual');\n        $this->firstCart  = new ECommerceCart();\n        $this->secondCart = new ECommerceCart();\n    }\n\n    public function testSavesAManyToManyAssociationWithCascadeSaveSet(): void\n    {\n        $this->firstCart->addProduct($this->firstProduct);\n        $this->firstCart->addProduct($this->secondProduct);\n        $this->_em->persist($this->firstCart);\n        $this->_em->flush();\n\n        $this->assertForeignKeysContain($this->firstCart->getId(), $this->firstProduct->getId());\n        $this->assertForeignKeysContain($this->firstCart->getId(), $this->secondProduct->getId());\n    }\n\n    public function testRemovesAManyToManyAssociation(): void\n    {\n        $this->firstCart->addProduct($this->firstProduct);\n        $this->firstCart->addProduct($this->secondProduct);\n        $this->_em->persist($this->firstCart);\n        $this->firstCart->removeProduct($this->firstProduct);\n\n        $this->_em->flush();\n\n        $this->assertForeignKeysNotContain($this->firstCart->getId(), $this->firstProduct->getId());\n        $this->assertForeignKeysContain($this->firstCart->getId(), $this->secondProduct->getId());\n    }\n\n    public function testEagerLoad(): void\n    {\n        $this->createFixture();\n\n        $query      = $this->_em->createQuery('SELECT c, p FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart c LEFT JOIN c.products p ORDER BY c.id, p.id');\n        $result     = $query->getResult();\n        $firstCart  = $result[0];\n        $products   = $firstCart->getProducts();\n        $secondCart = $result[1];\n\n        self::assertInstanceOf(ECommerceProduct::class, $products[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $products[1]);\n        $this->assertCollectionEquals($products, $secondCart->getProducts());\n        //$this->assertEquals(\"Doctrine 1.x Manual\", $products[0]->getName());\n        //$this->assertEquals(\"Doctrine 2.x Manual\", $products[1]->getName());\n    }\n\n    public function testLazyLoadsCollection(): void\n    {\n        $this->createFixture();\n        $metadata                                         = $this->_em->getClassMetadata(ECommerceCart::class);\n        $metadata->associationMappings['products']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query      = $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart c');\n        $result     = $query->getResult();\n        $firstCart  = $result[0];\n        $products   = $firstCart->getProducts();\n        $secondCart = $result[1];\n\n        self::assertInstanceOf(ECommerceProduct::class, $products[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $products[1]);\n        $this->assertCollectionEquals($products, $secondCart->getProducts());\n    }\n\n    private function createFixture(): void\n    {\n        $this->firstCart->addProduct($this->firstProduct);\n        $this->firstCart->addProduct($this->secondProduct);\n        $this->secondCart->addProduct($this->firstProduct);\n        $this->secondCart->addProduct($this->secondProduct);\n        $this->_em->persist($this->firstCart);\n        $this->_em->persist($this->secondCart);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/MappedSuperclassTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\DirectoryTree\\Directory;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\File;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * MappedSuperclassTest\n */\nclass MappedSuperclassTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('directorytree');\n\n        parent::setUp();\n    }\n\n    public function testCRUD(): void\n    {\n        $root = new Directory();\n        $root->setName('Root');\n        $root->setPath('/root');\n\n        $directory = new Directory($root);\n        $directory->setName('TestA');\n        $directory->setPath('/root/dir');\n\n        $file = new File($directory);\n        $file->setName('test-b.html');\n\n        $this->_em->persist($root);\n        $this->_em->persist($directory);\n        $this->_em->persist($file);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $cleanFile = $this->_em->find($file::class, $file->getId());\n\n        self::assertInstanceOf(Directory::class, $cleanFile->getParent());\n        self::assertTrue($this->isUninitializedObject($cleanFile->getParent()));\n        self::assertEquals($directory->getId(), $cleanFile->getParent()->getId());\n        self::assertInstanceOf(Directory::class, $cleanFile->getParent()->getParent());\n        self::assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/NativeQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Internal\\Hydration\\HydrationException;\nuse Doctrine\\ORM\\Internal\\SQLResultCasing;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\Tests\\DbalTypes\\UpperCaseStringType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUserDTO;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeUpperCase;\nuse Doctrine\\Tests\\Models\\DDC3899\\DDC3899FixContract;\nuse Doctrine\\Tests\\Models\\DDC3899\\DDC3899User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass NativeQueryTest extends OrmFunctionalTestCase\n{\n    use SQLResultCasing;\n\n    private AbstractPlatform|null $platform = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->platform = $this->_em->getConnection()->getDatabasePlatform();\n    }\n\n    public function testBasicNativeQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name');\n\n        $query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Roman', $users[0]->name);\n    }\n\n    public function testNativeQueryWithArrayParameter(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'William Shatner';\n        $user->username = 'wshatner';\n        $user->status   = 'dev';\n        $this->_em->persist($user);\n        $user           = new CmsUser();\n        $user->name     = 'Leonard Nimoy';\n        $user->username = 'lnimoy';\n        $user->status   = 'dev';\n        $this->_em->persist($user);\n        $user           = new CmsUser();\n        $user->name     = 'DeForest Kelly';\n        $user->username = 'dkelly';\n        $user->status   = 'dev';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name');\n\n        $query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username IN (?) ORDER BY username', $rsm);\n        $query->setParameter(1, ['wshatner', 'lnimoy'], ArrayParameterType::STRING);\n\n        $users = $query->getResult();\n\n        self::assertCount(2, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Leonard Nimoy', $users[0]->name);\n        self::assertEquals('William Shatner', $users[1]->name);\n    }\n\n    public function testBasicNativeQueryWithMetaResult(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $addr          = new CmsAddress();\n        $addr->country = 'germany';\n        $addr->zip     = 10827;\n        $addr->city    = 'Berlin';\n\n        $user->setAddress($addr);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsAddress::class, 'a');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'country'), 'country');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'zip'), 'zip');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'city'), 'city');\n        $rsm->addMetaResult('a', $this->getSQLResultCasing($this->platform, 'user_id'), 'user_id', false, 'integer');\n\n        $query = $this->_em->createNativeQuery('SELECT a.id, a.country, a.zip, a.city, a.user_id FROM cms_addresses a WHERE a.id = ?', $rsm);\n        $query->setParameter(1, $addr->id);\n\n        $addresses = $query->getResult();\n\n        self::assertCount(1, $addresses);\n        self::assertInstanceOf(CmsAddress::class, $addresses[0]);\n        self::assertEquals($addr->country, $addresses[0]->country);\n        self::assertEquals($addr->zip, $addresses[0]->zip);\n        self::assertEquals($addr->city, $addresses[0]->city);\n        self::assertEquals($addr->street, $addresses[0]->street);\n        self::assertInstanceOf(CmsUser::class, $addresses[0]->user);\n    }\n\n    public function testJoinedOneToManyNativeQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $phone              = new CmsPhonenumber();\n        $phone->phonenumber = 424242;\n\n        $user->addPhonenumber($phone);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'status'), 'status');\n        $rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $rsm->addFieldResult('p', $this->getSQLResultCasing($this->platform, 'phonenumber'), 'phonenumber');\n\n        $query = $this->_em->createNativeQuery('SELECT id, name, status, phonenumber FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Roman', $users[0]->name);\n        self::assertInstanceOf(PersistentCollection::class, $users[0]->getPhonenumbers());\n        self::assertTrue($users[0]->getPhonenumbers()->isInitialized());\n        self::assertCount(1, $users[0]->getPhonenumbers());\n        $phones = $users[0]->getPhonenumbers();\n        self::assertEquals(424242, $phones[0]->phonenumber);\n        self::assertSame($phones[0]->getUser(), $users[0]);\n    }\n\n    public function testMappingAsDto(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $phone              = new CmsPhonenumber();\n        $phone->phonenumber = 424242;\n\n        $user->addPhonenumber($phone);\n\n        $email        = new CmsEmail();\n        $email->email = 'fabio.bat.silva@gmail.com';\n\n        $user->setEmail($email);\n\n        $addr          = new CmsAddress();\n        $addr->country = 'germany';\n        $addr->zip     = 10827;\n        $addr->city    = 'Berlin';\n\n        $user->setAddress($addr);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addScalarResult('name', 1, 'string');\n        $rsm->addScalarResult('email', 2, 'string');\n        $rsm->addScalarResult('city', 3, 'string');\n        $rsm->newObjectMappings['name']  = [\n            'className' => CmsUserDTO::class,\n            'objIndex'  => 0,\n            'argIndex'  => 0,\n        ];\n        $rsm->newObjectMappings['email'] = [\n            'className' => CmsUserDTO::class,\n            'objIndex'  => 0,\n            'argIndex'  => 1,\n        ];\n        $rsm->newObjectMappings['city']  = [\n            'className' => CmsUserDTO::class,\n            'objIndex'  => 0,\n            'argIndex'  => 2,\n        ];\n        $query                           = $this->_em->createNativeQuery(\n            <<<'SQL'\n    SELECT u.name, e.email, a.city\n      FROM cms_users u\nINNER JOIN cms_phonenumbers p ON u.id = p.user_id\nINNER JOIN cms_emails e ON e.id = u.email_id\nINNER JOIN cms_addresses a ON u.id = a.user_id\n     WHERE username = ?\nSQL\n            ,\n            $rsm,\n        );\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n        $user = $users[0];\n        self::assertInstanceOf(CmsUserDTO::class, $user);\n        self::assertEquals('Roman', $user->name);\n        self::assertEquals('fabio.bat.silva@gmail.com', $user->email);\n        self::assertEquals('Berlin', $user->address);\n    }\n\n    public function testJoinedOneToOneNativeQuery(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $addr          = new CmsAddress();\n        $addr->country = 'germany';\n        $addr->zip     = 10827;\n        $addr->city    = 'Berlin';\n\n        $user->setAddress($addr);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'status'), 'status');\n        $rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'a_id'), 'id');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'country'), 'country');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'zip'), 'zip');\n        $rsm->addFieldResult('a', $this->getSQLResultCasing($this->platform, 'city'), 'city');\n\n        $query = $this->_em->createNativeQuery('SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Roman', $users[0]->name);\n        self::assertInstanceOf(PersistentCollection::class, $users[0]->getPhonenumbers());\n        self::assertFalse($users[0]->getPhonenumbers()->isInitialized());\n        self::assertInstanceOf(CmsAddress::class, $users[0]->getAddress());\n        self::assertEquals($users[0]->getAddress()->getUser(), $users[0]);\n        self::assertEquals('germany', $users[0]->getAddress()->getCountry());\n        self::assertEquals(10827, $users[0]->getAddress()->getZipCode());\n        self::assertEquals('Berlin', $users[0]->getAddress()->getCity());\n    }\n\n    public function testFluentInterface(): void\n    {\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter(1, 'foo'));\n        $parameters->add(new Parameter(2, 'bar'));\n\n        $rsm = new ResultSetMapping();\n\n        $q  = $this->_em->createNativeQuery('SELECT id, name, status, phonenumber FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', $rsm);\n        $q2 = $q->setSQL('foo')\n          ->setResultSetMapping($rsm)\n          ->expireResultCache(true)\n          ->setHint('foo', 'bar')\n          ->setParameter(1, 'foo')\n          ->setParameters($parameters)\n          ->setResultCacheLifetime(3500);\n\n        self::assertSame($q, $q2);\n    }\n\n    public function testJoinedOneToManyNativeQueryWithRSMBuilder(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $phone              = new CmsPhonenumber();\n        $phone->phonenumber = 424242;\n\n        $user->addPhonenumber($phone);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n        $rsm->addJoinedEntityFromClassMetadata(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $query = $this->_em->createNativeQuery('SELECT u.*, p.* FROM cms_users u LEFT JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Roman', $users[0]->name);\n        self::assertInstanceOf(PersistentCollection::class, $users[0]->getPhonenumbers());\n        self::assertTrue($users[0]->getPhonenumbers()->isInitialized());\n        self::assertCount(1, $users[0]->getPhonenumbers());\n        $phones = $users[0]->getPhonenumbers();\n        self::assertEquals(424242, $phones[0]->phonenumber);\n        self::assertSame($phones[0]->getUser(), $users[0]);\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsPhonenumber::class, 'p');\n        $query = $this->_em->createNativeQuery('SELECT p.* FROM cms_phonenumbers p WHERE p.phonenumber = ?', $rsm);\n        $query->setParameter(1, $phone->phonenumber);\n        $phone = $query->getSingleResult();\n\n        self::assertNotNull($phone->getUser());\n        self::assertEquals($user->name, $phone->getUser()->getName());\n    }\n\n    public function testJoinedOneToOneNativeQueryWithRSMBuilder(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $addr          = new CmsAddress();\n        $addr->country = 'germany';\n        $addr->zip     = 10827;\n        $addr->city    = 'Berlin';\n\n        $user->setAddress($addr);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n        $rsm->addJoinedEntityFromClassMetadata(CmsAddress::class, 'a', 'u', 'address', ['id' => 'a_id']);\n\n        $query = $this->_em->createNativeQuery('SELECT u.*, a.*, a.id AS a_id FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $users = $query->getResult();\n\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertEquals('Roman', $users[0]->name);\n        self::assertInstanceOf(PersistentCollection::class, $users[0]->getPhonenumbers());\n        self::assertFalse($users[0]->getPhonenumbers()->isInitialized());\n        self::assertInstanceOf(CmsAddress::class, $users[0]->getAddress());\n        self::assertEquals($users[0]->getAddress()->getUser(), $users[0]);\n        self::assertEquals('germany', $users[0]->getAddress()->getCountry());\n        self::assertEquals(10827, $users[0]->getAddress()->getZipCode());\n        self::assertEquals('Berlin', $users[0]->getAddress()->getCity());\n\n        $this->_em->clear();\n\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsAddress::class, 'a');\n        $query = $this->_em->createNativeQuery('SELECT a.* FROM cms_addresses a WHERE a.id = ?', $rsm);\n        $query->setParameter(1, $addr->getId());\n        $address = $query->getSingleResult();\n\n        self::assertNotNull($address->getUser());\n        self::assertEquals($user->name, $address->getUser()->getName());\n    }\n\n    #[Group('rsm-sti')]\n    public function testConcreteClassInSingleTableInheritanceSchemaWithRSMBuilderIsFine(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CompanyFixContract::class, 'c');\n\n        self::assertSame(CompanyFixContract::class, $rsm->getClassName('c'));\n    }\n\n    #[Group('rsm-sti')]\n    public function testAbstractClassInSingleTableInheritanceSchemaWithRSMBuilderThrowsException(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('ResultSetMapping builder does not currently support your inheritance scheme.');\n\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CompanyContract::class, 'c');\n    }\n\n    public function testRSMBuilderThrowsExceptionOnColumnConflict(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n        $rsm->addJoinedEntityFromClassMetadata(CmsAddress::class, 'a', 'u', 'address');\n    }\n\n    #[Group('PR-39')]\n    public function testUnknownParentAliasThrowsException(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n        $rsm->addJoinedEntityFromClassMetadata(CmsAddress::class, 'a', 'un', 'address', ['id' => 'a_id']);\n\n        $query = $this->_em->createNativeQuery('SELECT u.*, a.*, a.id AS a_id FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm);\n        $query->setParameter(1, 'romanb');\n\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage(\"The parent object of entity result with alias 'a' was not found. The parent alias is 'un'.\");\n\n        $users = $query->getResult();\n    }\n\n    #[Group('DDC-2055')]\n    public function testGenerateSelectClauseNoRenameSingleEntity(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n\n        $selectClause = $rsm->generateSelectClause();\n\n        $this->assertSQLEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause);\n    }\n\n    #[Group('DDC-2055')]\n    public function testGenerateSelectClauseCustomRenames(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u', [\n            'id' => 'id1',\n            'username' => 'username2',\n        ]);\n\n        $selectClause = $rsm->generateSelectClause();\n\n        $this->assertSQLEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause);\n    }\n\n    #[Group('DDC-2055')]\n    public function testGenerateSelectClauseRenameTableAlias(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n\n        $selectClause = $rsm->generateSelectClause(['u' => 'u1']);\n\n        $this->assertSQLEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause);\n    }\n\n    #[Group('DDC-2055')]\n    public function testGenerateSelectClauseIncrement(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n\n        $selectClause = $rsm->generateSelectClause();\n\n        $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause);\n    }\n\n    #[Group('DDC-2055')]\n    public function testGenerateSelectClauseToString(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u');\n\n        $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string) $rsm);\n    }\n\n    #[Group('DDC-3899')]\n    public function testGenerateSelectClauseWithDiscriminatorColumn(): void\n    {\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addEntityResult(DDC3899User::class, 'u');\n        $rsm->addJoinedEntityResult(DDC3899FixContract::class, 'c', 'u', 'contracts');\n        $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');\n        $rsm->setDiscriminatorColumn('c', $this->getSQLResultCasing($this->platform, 'discr'));\n\n        $selectClause = $rsm->generateSelectClause(['u' => 'u1', 'c' => 'c1']);\n\n        $this->assertSQLEquals('u1.id as id, c1.discr as discr', $selectClause);\n    }\n\n    public function testGenerateSelectClauseWithCustomTypeUsingEntityFromClassMetadata(): void\n    {\n        if (DBALType::hasType('upper_case_string')) {\n            DBALType::overrideType('upper_case_string', UpperCaseStringType::class);\n        } else {\n            DBALType::addType('upper_case_string', UpperCaseStringType::class);\n        }\n\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n\n        $rsm->addRootEntityFromClassMetadata(CustomTypeUpperCase::class, 'ct');\n\n        $selectClause = $rsm->generateSelectClause(['ct' => 'ct1']);\n\n        $this->assertSQLEquals('ct1.id as id0, lower(ct1.lowercasestring) as lowercasestring1, lower(ct1.named_lower_case_string) as named_lower_case_string2', $selectClause);\n    }\n\n    public function testGenerateSelectClauseWithCustomTypeUsingAddFieldResult(): void\n    {\n        if (DBALType::hasType('upper_case_string')) {\n            DBALType::overrideType('upper_case_string', UpperCaseStringType::class);\n        } else {\n            DBALType::addType('upper_case_string', UpperCaseStringType::class);\n        }\n\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addEntityResult(CustomTypeUpperCase::class, 'ct');\n        $rsm->addFieldResult('ct', $this->getSQLResultCasing($this->platform, 'lowercasestring'), 'lowerCaseString');\n\n        $selectClause = $rsm->generateSelectClause(['ct' => 'ct1']);\n\n        $this->assertSQLEquals('lower(ct1.lowercasestring) as lowercasestring', $selectClause);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/NewOperatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Exception\\DuplicateFieldException;\nuse Doctrine\\ORM\\Exception\\NoMatchingPropertyException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddressDTO;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddressDTONamedArgs;\nuse Doctrine\\Tests\\Models\\CMS\\CmsDumbDTO;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUserDTO;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUserDTONamedArgs;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUserDTOVariadicArg;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\nuse function sprintf;\n\n#[Group('DDC-1574')]\nclass NewOperatorTest extends OrmFunctionalTestCase\n{\n    /** @var list<CmsUser> */\n    private $fixtures;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->loadFixtures();\n    }\n\n    /** @phpstan-return list<array{int}> */\n    public static function provideDataForHydrationMode(): array\n    {\n        return [\n            [Query::HYDRATE_ARRAY],\n            [Query::HYDRATE_OBJECT],\n        ];\n    }\n\n    private function loadFixtures(): void\n    {\n        $u1 = new CmsUser();\n        $u2 = new CmsUser();\n        $u3 = new CmsUser();\n\n        $u1->setEmail(new CmsEmail());\n        $u1->setAddress(new CmsAddress());\n        $u1->addPhonenumber(new CmsPhonenumber());\n\n        $u2->setEmail(new CmsEmail());\n        $u2->setAddress(new CmsAddress());\n        $u2->addPhonenumber(new CmsPhonenumber());\n        $u2->addPhonenumber(new CmsPhonenumber());\n\n        $u3->setEmail(new CmsEmail());\n        $u3->setAddress(new CmsAddress());\n        $u3->addPhonenumber(new CmsPhonenumber());\n        $u3->addPhonenumber(new CmsPhonenumber());\n        $u3->addPhonenumber(new CmsPhonenumber());\n\n        $u1->name                         = 'Test 1';\n        $u1->username                     = '1test';\n        $u1->status                       = 'developer';\n        $u1->email->email                 = 'email@test1.com';\n        $u1->address->zip                 = '111111111';\n        $u1->address->city                = 'Some City 1';\n        $u1->address->country             = 'Some Country 2';\n        $u1->phonenumbers[0]->phonenumber = '(11) 1111-1111';\n\n        $u2->name                         = 'Test 2';\n        $u2->username                     = '2test';\n        $u2->status                       = 'developer';\n        $u2->email->email                 = 'email@test2.com';\n        $u2->address->zip                 = '222222222';\n        $u2->address->city                = 'Some City 2';\n        $u2->address->country             = 'Some Country 2';\n        $u2->phonenumbers[0]->phonenumber = '(22) 1111-1111';\n        $u2->phonenumbers[1]->phonenumber = '(22) 2222-2222';\n\n        $u3->name                         = 'Test 3';\n        $u3->username                     = '3test';\n        $u3->status                       = 'developer';\n        $u3->email->email                 = 'email@test3.com';\n        $u3->address->zip                 = '33333333';\n        $u3->address->city                = 'Some City 3';\n        $u3->address->country             = 'Some Country 3';\n        $u3->phonenumbers[0]->phonenumber = '(33) 1111-1111';\n        $u3->phonenumbers[1]->phonenumber = '(33) 2222-2222';\n        $u3->phonenumbers[2]->phonenumber = '(33) 3333-3333';\n\n        $this->_em->persist($u1);\n        $this->_em->persist($u2);\n        $this->_em->persist($u3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->fixtures = [$u1, $u2, $u3];\n    }\n\n    #[DataProvider('provideDataForHydrationMode')]\n    public function testShouldSupportsBasicUsage($hydrationMode): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    e.email,\n                    a.city\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult($hydrationMode);\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]->address);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]->address);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]->address);\n    }\n\n    #[DataProvider('provideDataForHydrationMode')]\n    public function testShouldIgnoreAliasesForSingleObject($hydrationMode): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    e.email,\n                    a.city\n                ) as cmsUser\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult($hydrationMode);\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]->address);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]->address);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]->address);\n    }\n\n    public function testShouldAssumeFromEntityNamespaceWhenNotGiven(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(u.name, e.email, a.city)\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n    }\n\n    public function testShouldSupportLiteralExpression(): void\n    {\n        $dql = \"\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    'fabio.bat.silva@gmail.com',\n                    FALSE,\n                    123\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            JOIN\n                u.phonenumbers p\n            GROUP BY\n                u, e, a\n            ORDER BY\n                u.name\";\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals('fabio.bat.silva@gmail.com', $result[0]->email);\n        self::assertEquals('fabio.bat.silva@gmail.com', $result[1]->email);\n        self::assertEquals('fabio.bat.silva@gmail.com', $result[2]->email);\n\n        // Note that the type hints on the DTO model convert false -> ''\n        self::assertEquals('', $result[0]->address);\n        self::assertEquals('', $result[1]->address);\n        self::assertEquals('', $result[2]->address);\n\n        self::assertEquals(123, $result[0]->phonenumbers);\n        self::assertEquals(123, $result[1]->phonenumbers);\n        self::assertEquals(123, $result[2]->phonenumbers);\n    }\n\n    public function testShouldSupportCaseExpression(): void\n    {\n        $dql = \"\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    CASE WHEN (e.email = 'email@test1.com') THEN 'TEST1' ELSE 'OTHER_TEST' END\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            JOIN\n                u.phonenumbers p\n            GROUP BY\n                u, e, a\n            ORDER BY\n                u.name\";\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals('TEST1', $result[0]->email);\n        self::assertEquals('OTHER_TEST', $result[1]->email);\n        self::assertEquals('OTHER_TEST', $result[2]->email);\n    }\n\n    public function testShouldSupportSimpleArithmeticExpression(): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    e.email,\n                    a.city,\n                    a.id + u.id\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            JOIN\n                u.phonenumbers p\n            GROUP BY\n                u, e, a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]->address);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]->address);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]->address);\n\n        self::assertEquals(\n            $this->fixtures[0]->address->id + $this->fixtures[0]->id,\n            $result[0]->phonenumbers,\n        );\n\n        self::assertEquals(\n            $this->fixtures[1]->address->id + $this->fixtures[1]->id,\n            $result[1]->phonenumbers,\n        );\n\n        self::assertEquals(\n            $this->fixtures[2]->address->id + $this->fixtures[2]->id,\n            $result[2]->phonenumbers,\n        );\n    }\n\n    public function testShouldSupportAggregateFunctions(): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    e.email,\n                    a.city,\n                    COUNT(p)\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            JOIN\n                u.phonenumbers p\n            GROUP BY\n                u, e, a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]->address);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]->address);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]->address);\n\n        self::assertEquals(\n            count($this->fixtures[0]->phonenumbers),\n            $result[0]->phonenumbers,\n        );\n\n        self::assertEquals(\n            count($this->fixtures[1]->phonenumbers),\n            $result[1]->phonenumbers,\n        );\n\n        self::assertEquals(\n            count($this->fixtures[2]->phonenumbers),\n            $result[2]->phonenumbers,\n        );\n    }\n\n    public function testShouldSupportArithmeticExpression(): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    e.email,\n                    a.city,\n                    COUNT(p) + u.id\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            JOIN\n                u.phonenumbers p\n            GROUP BY\n                u, e, a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]->address);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]->address);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]->address);\n\n        self::assertEquals(\n            count($this->fixtures[0]->phonenumbers) + $this->fixtures[0]->id,\n            $result[0]->phonenumbers,\n        );\n\n        self::assertEquals(\n            count($this->fixtures[1]->phonenumbers) + $this->fixtures[1]->id,\n            $result[1]->phonenumbers,\n        );\n\n        self::assertEquals(\n            count($this->fixtures[2]->phonenumbers) + $this->fixtures[2]->id,\n            $result[2]->phonenumbers,\n        );\n    }\n\n    public function testShouldSupportMultipleNewOperators(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ),\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2][0]);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][1]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0][0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1][0]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2][0]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0][0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1][0]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2][0]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][1]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][1]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][1]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][1]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][1]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][1]->country);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsWithAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ) as cmsAddress\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['cmsAddress']);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]['cmsAddress']->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2]['cmsAddress']->country);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsWithAndWithoutAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][0]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][0]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][0]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][0]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][0]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][0]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][0]->country);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndSingleScalar(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ),\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ),\n                u.status\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2][0]);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][1]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0][0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1][0]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2][0]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0][0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1][0]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2][0]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][1]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][1]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][1]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][1]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][1]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][1]->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['status']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['status']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['status']);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndSingleScalarWithAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ) as cmsAddress,\n                u.status as cmsUserStatus\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['cmsAddress']);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]['cmsAddress']->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2]['cmsAddress']->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['cmsUserStatus']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['cmsUserStatus']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['cmsUserStatus']);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndSingleScalarWithAndWithoutAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ),\n                u.status\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][0]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][0]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][0]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][0]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][0]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][0]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][0]->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['status']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['status']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['status']);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndMultipleScalars(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ),\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ),\n                u.status,\n                u.username\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2][0]);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][1]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][1]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0][0]->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1][0]->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2][0]->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0][0]->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1][0]->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2][0]->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][1]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][1]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][1]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][1]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][1]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][1]->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['status']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['status']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertEquals($this->fixtures[0]->username, $result[0]['username']);\n        self::assertEquals($this->fixtures[1]->username, $result[1]['username']);\n        self::assertEquals($this->fixtures[2]->username, $result[2]['username']);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndMultipleScalarsWithAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ) as cmsAddress,\n                u.status as cmsUserStatus,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['cmsAddress']);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['cmsAddress']);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1]['cmsAddress']->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2]['cmsAddress']->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1]['cmsAddress']->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2]['cmsAddress']->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['cmsUserStatus']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['cmsUserStatus']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['cmsUserStatus']);\n\n        self::assertEquals($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertEquals($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertEquals($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testShouldSupportMultipleNewOperatorsAndMultipleScalarsWithAndWithoutAliases(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email\n                ) as cmsUser,\n                new CmsAddressDTO(\n                    a.country,\n                    a.city\n                ),\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['cmsUser']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['cmsUser']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1][0]);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2][0]);\n\n        self::assertEquals($this->fixtures[0]->name, $result[0]['cmsUser']->name);\n        self::assertEquals($this->fixtures[1]->name, $result[1]['cmsUser']->name);\n        self::assertEquals($this->fixtures[2]->name, $result[2]['cmsUser']->name);\n\n        self::assertEquals($this->fixtures[0]->email->email, $result[0]['cmsUser']->email);\n        self::assertEquals($this->fixtures[1]->email->email, $result[1]['cmsUser']->email);\n        self::assertEquals($this->fixtures[2]->email->email, $result[2]['cmsUser']->email);\n\n        self::assertEquals($this->fixtures[0]->address->city, $result[0][0]->city);\n        self::assertEquals($this->fixtures[1]->address->city, $result[1][0]->city);\n        self::assertEquals($this->fixtures[2]->address->city, $result[2][0]->city);\n\n        self::assertEquals($this->fixtures[0]->address->country, $result[0][0]->country);\n        self::assertEquals($this->fixtures[1]->address->country, $result[1][0]->country);\n        self::assertEquals($this->fixtures[2]->address->country, $result[2][0]->country);\n\n        self::assertEquals($this->fixtures[0]->status, $result[0]['status']);\n        self::assertEquals($this->fixtures[1]->status, $result[1]['status']);\n        self::assertEquals($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertEquals($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertEquals($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertEquals($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testInvalidClassException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \\'\\InvalidClass(u.name)\\': Error: Class \"\\InvalidClass\" is not defined.');\n        $dql = 'SELECT new \\InvalidClass(u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $this->_em->createQuery($dql)->getResult();\n    }\n\n    public function testInvalidClassConstructorException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \\'\\stdClass(u.name)\\': Error: Class \"\\stdClass\" has not a valid constructor.');\n        $dql = 'SELECT new \\stdClass(u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $this->_em->createQuery($dql)->getResult();\n    }\n\n    public function testInvalidClassWithoutConstructorException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \\'Doctrine\\Tests\\ORM\\Functional\\ClassWithTooMuchArgs(u.name)\\': Error: Number of arguments does not match with \"Doctrine\\Tests\\ORM\\Functional\\ClassWithTooMuchArgs\" constructor declaration.');\n        $dql = 'SELECT new Doctrine\\Tests\\ORM\\Functional\\ClassWithTooMuchArgs(u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $this->_em->createQuery($dql)->getResult();\n    }\n\n    public function testClassCantBeInstantiatedException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \\'Doctrine\\Tests\\ORM\\Functional\\ClassWithPrivateConstructor(u.name)\\': Error: Class \"Doctrine\\Tests\\ORM\\Functional\\ClassWithPrivateConstructor\" can not be instantiated.');\n        $dql = 'SELECT new Doctrine\\Tests\\ORM\\Functional\\ClassWithPrivateConstructor(u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $this->_em->createQuery($dql)->getResult();\n    }\n\n    public function testShouldSupportNestedNewOperators(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email,\n                    new CmsAddressDTO(\n                        a.country,\n                        a.city,\n                        a.zip,\n                        \\'Abbey Road\\',\n                        new CmsDumbDTO(\n                            a.country,\n                            a.city,\n                            a.zip\n                        )\n                    )\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['user']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->address);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->country);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]['user']->address->other);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]['user']->address->other);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]['user']->address->other);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->other->val1);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->other->val1);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->other->val1);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->other->val2);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->other->val2);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->other->val2);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testShouldSupportNestedNewOperatorsWithDtoFirst(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email,\n                    new CmsAddressDTO(\n                        a.country,\n                        a.city,\n                        a.zip\n                    ),\n                    555812452\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['user']);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->address);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->country);\n\n        self::assertSame(555812452, $result[0]['user']->phonenumbers);\n        self::assertSame(555812452, $result[1]['user']->phonenumbers);\n        self::assertSame(555812452, $result[2]['user']->phonenumbers);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testOnlyObjectInObject(): void\n    {\n        $dql = '\n            SELECT\n                new CmsDumbDTO(\n                    new CmsDumbDTO(\n                        u.name,\n                        e.email\n                    ),\n                    new CmsAddressDTO(\n                        a.country,\n                        a.city,\n                        a.zip\n                    )\n                ) as user\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]->val1);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]->val1);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]->val1);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->val1->val1);\n        self::assertSame($this->fixtures[1]->name, $result[1]->val1->val1);\n        self::assertSame($this->fixtures[2]->name, $result[2]->val1->val1);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->val1->val2);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->val1->val2);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->val1->val2);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]->val2);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]->val2);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]->val2);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->val2->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->val2->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->val2->country);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->val2->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->val2->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->val2->city);\n\n        self::assertSame($this->fixtures[0]->address->zip, $result[0]->val2->zip);\n        self::assertSame($this->fixtures[1]->address->zip, $result[1]->val2->zip);\n        self::assertSame($this->fixtures[2]->address->zip, $result[2]->val2->zip);\n    }\n\n    public function testEntityInDtoWithRoot(): void\n    {\n        $dql = '\n            SELECT\n                new CmsDumbDTO(\n                    u.id,\n                    u,\n                    a,\n                    e.email\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]);\n\n        self::assertInstanceOf(CmsUser::class, $result[0]->val2);\n        self::assertInstanceOf(CmsUser::class, $result[1]->val2);\n        self::assertInstanceOf(CmsUser::class, $result[2]->val2);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->val2->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]->val2->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]->val2->name);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]->val2->username);\n        self::assertSame($this->fixtures[1]->username, $result[1]->val2->username);\n        self::assertSame($this->fixtures[2]->username, $result[2]->val2->username);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]->val2->status);\n        self::assertSame($this->fixtures[1]->status, $result[1]->val2->status);\n        self::assertSame($this->fixtures[2]->status, $result[2]->val2->status);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->val3);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->val3);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->val3);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->val3->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->val3->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->val3->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->val3->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->val3->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->val3->country);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->val4);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->val4);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->val4);\n    }\n\n    public function testEntityInDtoWithoutRoot(): void\n    {\n        $dql = '\n            SELECT\n                new CmsDumbDTO(\n                    u.id,\n                    u.name,\n                    a,\n                    e.email\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->val2);\n        self::assertSame($this->fixtures[1]->name, $result[1]->val2);\n        self::assertSame($this->fixtures[2]->name, $result[2]->val2);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->val3);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->val3);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->val3);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->val3->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->val3->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->val3->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->val3->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->val3->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->val3->country);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->val4);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->val4);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->val4);\n    }\n\n    public function testOnlyObjectInDto(): void\n    {\n        $dql = '\n            SELECT\n                new CmsDumbDTO(\n                    a,\n                    new CmsDumbDTO(\n                        u.name,\n                        e.email\n                    )\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->val1);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->val1);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->val1);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->val1->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->val1->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->val1->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->val1->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->val1->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->val1->country);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]->val2);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]->val2);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]->val2);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->val2->val1);\n        self::assertSame($this->fixtures[1]->name, $result[1]->val2->val1);\n        self::assertSame($this->fixtures[2]->name, $result[2]->val2->val1);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->val2->val2);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->val2->val2);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->val2->val2);\n    }\n\n    public function testOnlyObjectInNamedDto(): void\n    {\n        $dql = '\n            SELECT\n                new named CmsUserDTOVariadicArg(\n                    a,\n                    new CmsDumbDTO(\n                        u.name,\n                        e.email\n                    ) as dumb\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[2]);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->otherProperties['a']);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->otherProperties['a']);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->otherProperties['a']);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->otherProperties['a']->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->otherProperties['a']->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->otherProperties['a']->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->otherProperties['a']->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->otherProperties['a']->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->otherProperties['a']->country);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]->otherProperties['dumb']);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]->otherProperties['dumb']);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]->otherProperties['dumb']);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->otherProperties['dumb']->val1);\n        self::assertSame($this->fixtures[1]->name, $result[1]->otherProperties['dumb']->val1);\n        self::assertSame($this->fixtures[2]->name, $result[2]->otherProperties['dumb']->val1);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->otherProperties['dumb']->val2);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->otherProperties['dumb']->val2);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->otherProperties['dumb']->val2);\n    }\n\n    public function testOnlyObjectInNamedDtoWithAlias(): void\n    {\n        $dql = '\n            SELECT\n                new named CmsUserDTOVariadicArg(\n                    a as addr,\n                    new CmsDumbDTO(\n                        u.name,\n                        e.email\n                    ) as dumb\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[2]);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->otherProperties['addr']);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->otherProperties['addr']);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->otherProperties['addr']);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->otherProperties['addr']->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->otherProperties['addr']->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->otherProperties['addr']->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->otherProperties['addr']->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->otherProperties['addr']->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->otherProperties['addr']->country);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]->otherProperties['dumb']);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]->otherProperties['dumb']);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]->otherProperties['dumb']);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->otherProperties['dumb']->val1);\n        self::assertSame($this->fixtures[1]->name, $result[1]->otherProperties['dumb']->val1);\n        self::assertSame($this->fixtures[2]->name, $result[2]->otherProperties['dumb']->val1);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->otherProperties['dumb']->val2);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->otherProperties['dumb']->val2);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->otherProperties['dumb']->val2);\n    }\n\n    public function testOnlyObjectInNamedDtoWithSameNameAsTheProperties(): void\n    {\n        $dql = '\n            SELECT\n                new named CmsUserDTONamedArgs(\n                    addressEntity,\n                    new CmsDumbDTO(\n                        u.name,\n                        e.email\n                    ) as dumb\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            LEFT JOIN\n                u.email e\n            LEFT JOIN\n                u.address addressEntity\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]);\n\n        self::assertInstanceOf(CmsAddress::class, $result[0]->addressEntity);\n        self::assertInstanceOf(CmsAddress::class, $result[1]->addressEntity);\n        self::assertInstanceOf(CmsAddress::class, $result[2]->addressEntity);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]->addressEntity->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]->addressEntity->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]->addressEntity->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]->addressEntity->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]->addressEntity->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]->addressEntity->country);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]->dumb);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]->dumb);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]->dumb);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]->dumb->val1);\n        self::assertSame($this->fixtures[1]->name, $result[1]->dumb->val1);\n        self::assertSame($this->fixtures[2]->name, $result[2]->dumb->val1);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]->dumb->val2);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]->dumb->val2);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]->dumb->val2);\n    }\n\n    public function testNamedArguments(): void\n    {\n        $dql = <<<'SQL'\n            SELECT\n                new named CmsUserDTONamedArgs(\n                    e.email,\n                    u.name,\n                    CONCAT(a.country, ' ', a.city, ' ', a.zip) AS address\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name\n            SQL;\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame(sprintf(\n            '%s %s %s',\n            $this->fixtures[0]->address->country,\n            $this->fixtures[0]->address->city,\n            $this->fixtures[0]->address->zip,\n        ), $result[0]['user']->address);\n        self::assertSame(\n            sprintf(\n                '%s %s %s',\n                $this->fixtures[1]->address->country,\n                $this->fixtures[1]->address->city,\n                $this->fixtures[1]->address->zip,\n            ),\n            $result[1]['user']->address,\n        );\n        self::assertSame(\n            sprintf(\n                '%s %s %s',\n                $this->fixtures[2]->address->country,\n                $this->fixtures[2]->address->city,\n                $this->fixtures[2]->address->zip,\n            ),\n            $result[2]['user']->address,\n        );\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testShouldSupportNestedNewOperatorsWithNestedDtoNotLast(): void\n    {\n        $dql = '\n            SELECT\n                new CmsUserDTO(\n                    u.name,\n                    e.email,\n                    new CmsAddressDTO(\n                        a.country,\n                        a.city,\n                        a.zip,\n                        \\'Abbey Road\\',\n                        new CmsDumbDTO(\n                            a.country,\n                            a.city,\n                            new CmsDumbDTO(\n                                a.zip,\n                                456\n                            ),\n                            a.zip\n                        )\n                    ),\n                    555812452\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTO::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTO::class, $result[2]['user']);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->address);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->address);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->country);\n\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->city);\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->city);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]['user']->address->other);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]['user']->address->other);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]['user']->address->other);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->other->val1);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->other->val1);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->other->val1);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->other->val2);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->other->val2);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->other->val2);\n\n        self::assertInstanceOf(CmsDumbDTO::class, $result[0]['user']->address->other->val3);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[1]['user']->address->other->val3);\n        self::assertInstanceOf(CmsDumbDTO::class, $result[2]['user']->address->other->val3);\n\n        self::assertSame($this->fixtures[0]->address->zip, $result[0]['user']->address->other->val3->val1);\n        self::assertSame($this->fixtures[1]->address->zip, $result[1]['user']->address->other->val3->val1);\n        self::assertSame($this->fixtures[2]->address->zip, $result[2]['user']->address->other->val3->val1);\n\n        self::assertSame(456, $result[0]['user']->address->other->val3->val2);\n        self::assertSame(456, $result[1]['user']->address->other->val3->val2);\n        self::assertSame(456, $result[2]['user']->address->other->val3->val2);\n\n        self::assertSame($this->fixtures[0]->address->zip, $result[0]['user']->address->other->val4);\n        self::assertSame($this->fixtures[1]->address->zip, $result[1]['user']->address->other->val4);\n        self::assertSame($this->fixtures[2]->address->zip, $result[2]['user']->address->other->val4);\n\n        self::assertSame(555812452, $result[0]['user']->phonenumbers);\n        self::assertSame(555812452, $result[1]['user']->phonenumbers);\n        self::assertSame(555812452, $result[2]['user']->phonenumbers);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testVariadicArgument(): void\n    {\n        $dql = <<<'SQL'\n            SELECT\n                new named CmsUserDTOVariadicArg(\n                    CONCAT(a.country, ' ', a.city, ' ', a.zip) AS address,\n                    e.email,\n                    u.name\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name\n            SQL;\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[2]['user']);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame(\n            sprintf(\n                '%s %s %s',\n                $this->fixtures[0]->address->country,\n                $this->fixtures[0]->address->city,\n                $this->fixtures[0]->address->zip,\n            ),\n            $result[0]['user']->address,\n        );\n        self::assertSame(\n            sprintf(\n                '%s %s %s',\n                $this->fixtures[1]->address->country,\n                $this->fixtures[1]->address->city,\n                $this->fixtures[1]->address->zip,\n            ),\n            $result[1]['user']->address,\n        );\n        self::assertSame(\n            sprintf(\n                '%s %s %s',\n                $this->fixtures[2]->address->country,\n                $this->fixtures[2]->address->city,\n                $this->fixtures[2]->address->zip,\n            ),\n            $result[2]['user']->address,\n        );\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testShouldSupportNestedNewOperatorsAndNamedArguments(): void\n    {\n        $dql = '\n            SELECT\n                new named CmsUserDTONamedArgs(\n                    e.email,\n                    u.name as name,\n                    new CmsAddressDTO(\n                        a.country,\n                        a.city,\n                        a.zip\n                    ) as addressDto\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']);\n\n        self::assertNull($result[0]['user']->address);\n        self::assertNull($result[1]['user']->address);\n        self::assertNull($result[2]['user']->address);\n\n        self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->addressDto);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->addressDto);\n        self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->addressDto);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->addressDto->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->addressDto->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->addressDto->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->addressDto->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->addressDto->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->addressDto->country);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testShouldSupportNestedNamedArguments(): void\n    {\n        $dql = '\n            SELECT\n                new named CmsUserDTONamedArgs(\n                    e.email,\n                    u.name as name,\n                    new named CmsAddressDTONamedArgs(\n                        a.zip,\n                        a.city,\n                        a.country \n                    ) as addressDtoNamedArgs\n                ) as user,\n                u.status,\n                u.username as cmsUserUsername\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\n            JOIN\n                u.address a\n            ORDER BY\n                u.name';\n\n        $query  = $this->getEntityManager()->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']);\n        self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']);\n\n        self::assertNull($result[0]['user']->address);\n        self::assertNull($result[1]['user']->address);\n        self::assertNull($result[2]['user']->address);\n\n        self::assertNull($result[0]['user']->addressDto);\n        self::assertNull($result[1]['user']->addressDto);\n        self::assertNull($result[2]['user']->addressDto);\n\n        self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[0]['user']->addressDtoNamedArgs);\n        self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[1]['user']->addressDtoNamedArgs);\n        self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[2]['user']->addressDtoNamedArgs);\n\n        self::assertSame($this->fixtures[0]->name, $result[0]['user']->name);\n        self::assertSame($this->fixtures[1]->name, $result[1]['user']->name);\n        self::assertSame($this->fixtures[2]->name, $result[2]['user']->name);\n\n        self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email);\n        self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email);\n        self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email);\n\n        self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->addressDtoNamedArgs->city);\n        self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->addressDtoNamedArgs->city);\n        self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->addressDtoNamedArgs->city);\n\n        self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->addressDtoNamedArgs->country);\n        self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->addressDtoNamedArgs->country);\n        self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->addressDtoNamedArgs->country);\n\n        self::assertSame($this->fixtures[0]->status, $result[0]['status']);\n        self::assertSame($this->fixtures[1]->status, $result[1]['status']);\n        self::assertSame($this->fixtures[2]->status, $result[2]['status']);\n\n        self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']);\n        self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']);\n        self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']);\n    }\n\n    public function testExceptionIfTwoAliases(): void\n    {\n        $dql = '\n            SELECT\n                new named Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    u.username AS name\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n\n        $this->expectException(DuplicateFieldException::class);\n        $this->expectExceptionMessage('Name \"name\" for \"u.username AS name\" already in use.');\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n    }\n\n    public function testExceptionIfFunctionHasNoAlias(): void\n    {\n        $dql = \"\n            SELECT\n                new named Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(\n                    u.name,\n                    CASE WHEN (e.email = 'email@test1.com') THEN 'TEST1' ELSE 'OTHER_TEST' END\n                )\n            FROM\n                Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            JOIN\n                u.email e\";\n\n        $this->expectException(NoMatchingPropertyException::class);\n        $this->expectExceptionMessage('Column name \"CASE WHEN (e.email = \\'email@test1.com\\') THEN \\'TEST1\\' ELSE \\'OTHER_TEST\\' END\" does not match any property name. Consider aliasing it to the name of an existing property.');\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n    }\n}\n\nclass ClassWithTooMuchArgs\n{\n    public function __construct($foo, $bar)\n    {\n        $this->foo = $foo;\n        $this->bor = $bar;\n    }\n}\n\nclass ClassWithPrivateConstructor\n{\n    private function __construct($foo)\n    {\n        $this->foo = $foo;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests a bidirectional one-to-one association mapping (without inheritance).\n */\nclass OneToManyBidirectionalAssociationTest extends OrmFunctionalTestCase\n{\n    private ECommerceProduct $product;\n\n    private ECommerceFeature $firstFeature;\n\n    private ECommerceFeature $secondFeature;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->product = new ECommerceProduct();\n        $this->product->setName('Doctrine Cookbook');\n        $this->firstFeature = new ECommerceFeature();\n        $this->firstFeature->setDescription('Model writing tutorial');\n        $this->secondFeature = new ECommerceFeature();\n        $this->secondFeature->setDescription('Attributes examples');\n    }\n\n    public function testSavesAOneToManyAssociationWithCascadeSaveSet(): void\n    {\n        $this->product->addFeature($this->firstFeature);\n        $this->product->addFeature($this->secondFeature);\n        $this->_em->persist($this->product);\n        $this->_em->flush();\n\n        $this->assertFeatureForeignKeyIs($this->product->getId(), $this->firstFeature);\n        $this->assertFeatureForeignKeyIs($this->product->getId(), $this->secondFeature);\n    }\n\n    public function testSavesAnEmptyCollection(): void\n    {\n        $this->_em->persist($this->product);\n        $this->_em->flush();\n\n        self::assertCount(0, $this->product->getFeatures());\n    }\n\n    public function testDoesNotSaveAnInverseSideSet(): void\n    {\n        $this->product->brokenAddFeature($this->firstFeature);\n        $this->_em->persist($this->product);\n        $this->_em->flush();\n\n        $this->assertFeatureForeignKeyIs(null, $this->firstFeature);\n    }\n\n    public function testRemovesOneToOneAssociation(): void\n    {\n        $this->product->addFeature($this->firstFeature);\n        $this->product->addFeature($this->secondFeature);\n        $this->_em->persist($this->product);\n\n        $this->product->removeFeature($this->firstFeature);\n        $this->_em->flush();\n\n        $this->assertFeatureForeignKeyIs(null, $this->firstFeature);\n        $this->assertFeatureForeignKeyIs($this->product->getId(), $this->secondFeature);\n    }\n\n    public function testEagerLoadsOneToManyAssociation(): void\n    {\n        $this->createFixture();\n        $query   = $this->_em->createQuery('select p, f from Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p join p.features f');\n        $result  = $query->getResult();\n        $product = $result[0];\n\n        $features = $product->getFeatures();\n\n        self::assertInstanceOf(ECommerceFeature::class, $features[0]);\n        self::assertFalse($this->isUninitializedObject($features[0]->getProduct()));\n        self::assertSame($product, $features[0]->getProduct());\n        self::assertEquals('Model writing tutorial', $features[0]->getDescription());\n        self::assertInstanceOf(ECommerceFeature::class, $features[1]);\n        self::assertSame($product, $features[1]->getProduct());\n        self::assertFalse($this->isUninitializedObject($features[1]->getProduct()));\n        self::assertEquals('Attributes examples', $features[1]->getDescription());\n    }\n\n    public function testLazyLoadsObjectsOnTheOwningSide(): void\n    {\n        $this->createFixture();\n\n        $query    = $this->_em->createQuery('select p from Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p');\n        $result   = $query->getResult();\n        $product  = $result[0];\n        $features = $product->getFeatures();\n\n        self::assertFalse($features->isInitialized());\n        self::assertInstanceOf(ECommerceFeature::class, $features[0]);\n        self::assertTrue($features->isInitialized());\n        self::assertSame($product, $features[0]->getProduct());\n        self::assertEquals('Model writing tutorial', $features[0]->getDescription());\n        self::assertInstanceOf(ECommerceFeature::class, $features[1]);\n        self::assertSame($product, $features[1]->getProduct());\n        self::assertEquals('Attributes examples', $features[1]->getDescription());\n    }\n\n    public function testLazyLoadsObjectsOnTheInverseSide(): void\n    {\n        $this->createFixture();\n\n        $query    = $this->_em->createQuery('select f from Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature f');\n        $features = $query->getResult();\n\n        $product = $features[0]->getProduct();\n        self::assertInstanceOf(ECommerceProduct::class, $product);\n        self::assertTrue($this->isUninitializedObject($product));\n        self::assertSame('Doctrine Cookbook', $product->getName());\n        self::assertFalse($this->isUninitializedObject($product));\n    }\n\n    public function testLazyLoadsObjectsOnTheInverseSide2(): void\n    {\n        $this->createFixture();\n\n        $query    = $this->_em->createQuery('select f,p from Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature f join f.product p');\n        $features = $query->getResult();\n\n        $product = $features[0]->getProduct();\n        self::assertFalse($this->isUninitializedObject($product));\n        self::assertInstanceOf(ECommerceProduct::class, $product);\n        self::assertSame('Doctrine Cookbook', $product->getName());\n\n        self::assertFalse($product->getFeatures()->isInitialized());\n\n        // This would trigger lazy-load\n        //$this->assertEquals(2, $product->getFeatures()->count());\n        //$this->assertTrue($product->getFeatures()->contains($features[0]));\n        //$this->assertTrue($product->getFeatures()->contains($features[1]));\n    }\n\n    public function testJoinFromOwningSide(): void\n    {\n        $query    = $this->_em->createQuery('select f,p from Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature f join f.product p');\n        $features = $query->getResult();\n        self::assertCount(0, $features);\n    }\n\n    #[Group('DDC-1637')]\n    public function testMatching(): void\n    {\n        $this->createFixture();\n\n        $product  = $this->_em->find(ECommerceProduct::class, $this->product->getId());\n        $features = $product->getFeatures();\n\n        $results = $features->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('description', 'Model writing tutorial'),\n        ));\n\n        self::assertInstanceOf(Collection::class, $results);\n        self::assertCount(1, $results);\n\n        $results = $features->matching(Criteria::create(true));\n\n        self::assertInstanceOf(Collection::class, $results);\n        self::assertCount(2, $results);\n    }\n\n    #[Group('DDC-2340')]\n    public function testMatchingOnDirtyCollection(): void\n    {\n        $this->createFixture();\n\n        $product = $this->_em->find(ECommerceProduct::class, $this->product->getId());\n\n        $thirdFeature = new ECommerceFeature();\n        $thirdFeature->setDescription('Model writing tutorial');\n\n        $features = $product->getFeatures();\n        $features->add($thirdFeature);\n\n        $results = $features->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('description', 'Model writing tutorial'),\n        ));\n\n        self::assertCount(2, $results);\n    }\n\n    public function testMatchingBis(): void\n    {\n        $this->createFixture();\n\n        $product  = $this->_em->find(ECommerceProduct::class, $this->product->getId());\n        $features = $product->getFeatures();\n\n        $thirdFeature = new ECommerceFeature();\n        $thirdFeature->setDescription('Third feature');\n        $product->addFeature($thirdFeature);\n\n        $results = $features->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('description', 'Third feature'),\n        ));\n\n        self::assertInstanceOf(Collection::class, $results);\n        self::assertCount(1, $results);\n\n        $results = $features->matching(Criteria::create(true));\n\n        self::assertInstanceOf(Collection::class, $results);\n        self::assertCount(3, $results);\n    }\n\n    private function createFixture(): void\n    {\n        $this->product->addFeature($this->firstFeature);\n        $this->product->addFeature($this->secondFeature);\n        $this->_em->persist($this->product);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function assertFeatureForeignKeyIs($value, ECommerceFeature $feature): void\n    {\n        $foreignKey = $this->_em->getConnection()->executeQuery(\n            'SELECT product_id FROM ecommerce_features WHERE id=?',\n            [$feature->getId()],\n        )->fetchOne();\n        self::assertEquals($value, $foreignKey);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToManyOrphanRemovalTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests a bidirectional one-to-many association mapping with orphan removal.\n */\nclass OneToManyOrphanRemovalTest extends OrmFunctionalTestCase\n{\n    /** @var int */\n    protected $userId;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'romanb';\n        $user->name     = 'Roman B.';\n\n        $phone1              = new CmsPhonenumber();\n        $phone1->phonenumber = '123456';\n\n        $phone2              = new CmsPhonenumber();\n        $phone2->phonenumber = '234567';\n\n        $user->addPhonenumber($phone1);\n        $user->addPhonenumber($phone2);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->userId = $user->getId();\n        $this->_em->clear();\n    }\n\n    public function testOrphanRemoval(): void\n    {\n        $userProxy = $this->_em->getReference(CmsUser::class, $this->userId);\n\n        $this->_em->remove($userProxy);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsUser should be removed by EntityManager');\n\n        $query  = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsPhonenumber should be removed by orphanRemoval');\n    }\n\n    #[Group('DDC-3382')]\n    public function testOrphanRemovalRemoveFromCollection(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $phonenumber = $user->getPhonenumbers()->remove(0);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p');\n        $result = $query->getResult();\n\n        self::assertCount(1, $result, 'CmsPhonenumber should be removed by orphanRemoval');\n    }\n\n    #[Group('DDC-3382')]\n    public function testOrphanRemovalClearCollectionAndReAdd(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $phone1 = $user->getPhonenumbers()->first();\n\n        $user->getPhonenumbers()->clear();\n        $user->addPhonenumber($phone1);\n\n        $this->_em->flush();\n\n        $query  = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p');\n        $result = $query->getResult();\n\n        self::assertCount(1, $result, 'CmsPhonenumber should be removed by orphanRemoval');\n    }\n\n    #[Group('DDC-2524')]\n    public function testOrphanRemovalClearCollectionAndAddNew(): void\n    {\n        $user     = $this->_em->find(CmsUser::class, $this->userId);\n        $newPhone = new CmsPhonenumber();\n\n        $newPhone->phonenumber = '654321';\n\n        $user->getPhonenumbers()->clear();\n        $user->addPhonenumber($newPhone);\n\n        $this->_em->flush();\n\n        $query  = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p');\n        $result = $query->getResult();\n\n        self::assertCount(1, $result, 'Old CmsPhonenumbers should be removed by orphanRemoval and new one added');\n    }\n\n    #[Group('DDC-1496')]\n    public function testOrphanRemovalUnitializedCollection(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $user->phonenumbers->clear();\n        $this->_em->flush();\n\n        $query  = $this->_em->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsPhonenumber should be removed by orphanRemoval');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strstr;\n\n/**\n * Tests a bidirectional one-to-one association mapping (without inheritance).\n */\nclass OneToManySelfReferentialAssociationTest extends OrmFunctionalTestCase\n{\n    private ECommerceCategory $parent;\n\n    private ECommerceCategory $firstChild;\n\n    private ECommerceCategory $secondChild;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->parent = new ECommerceCategory();\n        $this->parent->setName('Programming languages books');\n        $this->firstChild = new ECommerceCategory();\n        $this->firstChild->setName('Java books');\n        $this->secondChild = new ECommerceCategory();\n        $this->secondChild->setName('Php books');\n    }\n\n    public function testSavesAOneToManyAssociationWithCascadeSaveSet(): void\n    {\n        $this->parent->addChild($this->firstChild);\n        $this->parent->addChild($this->secondChild);\n        $this->_em->persist($this->parent);\n\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs($this->parent->getId(), $this->firstChild);\n        $this->assertForeignKeyIs($this->parent->getId(), $this->secondChild);\n    }\n\n    public function testSavesAnEmptyCollection(): void\n    {\n        $this->_em->persist($this->parent);\n        $this->_em->flush();\n\n        self::assertCount(0, $this->parent->getChildren());\n    }\n\n    public function testDoesNotSaveAnInverseSideSet(): void\n    {\n        $this->parent->brokenAddChild($this->firstChild);\n        $this->_em->persist($this->parent);\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs(null, $this->firstChild);\n    }\n\n    public function testRemovesOneToManyAssociation(): void\n    {\n        $this->parent->addChild($this->firstChild);\n        $this->parent->addChild($this->secondChild);\n        $this->_em->persist($this->parent);\n\n        $this->parent->removeChild($this->firstChild);\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs(null, $this->firstChild);\n        $this->assertForeignKeyIs($this->parent->getId(), $this->secondChild);\n    }\n\n    public function testEagerLoadsOneToManyAssociation(): void\n    {\n        $this->createFixture();\n\n        $query  = $this->_em->createQuery('select c1, c2 from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory c1 join c1.children c2');\n        $result = $query->getResult();\n        self::assertCount(1, $result);\n        $parent   = $result[0];\n        $children = $parent->getChildren();\n\n        self::assertInstanceOf(ECommerceCategory::class, $children[0]);\n        self::assertSame($parent, $children[0]->getParent());\n        self::assertEquals(' books', strstr($children[0]->getName(), ' books'));\n        self::assertInstanceOf(ECommerceCategory::class, $children[1]);\n        self::assertSame($parent, $children[1]->getParent());\n        self::assertEquals(' books', strstr($children[1]->getName(), ' books'));\n    }\n\n    public function testLazyLoadsOneToManyAssociation(): void\n    {\n        $this->createFixture();\n        $metadata                                         = $this->_em->getClassMetadata(ECommerceCategory::class);\n        $metadata->associationMappings['children']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query    = $this->_em->createQuery('select c from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory c order by c.id asc');\n        $result   = $query->getResult();\n        $parent   = $result[0];\n        $children = $parent->getChildren();\n\n        self::assertInstanceOf(ECommerceCategory::class, $children[0]);\n        self::assertSame($parent, $children[0]->getParent());\n        self::assertEquals(' books', strstr($children[0]->getName(), ' books'));\n        self::assertInstanceOf(ECommerceCategory::class, $children[1]);\n        self::assertSame($parent, $children[1]->getParent());\n        self::assertEquals(' books', strstr($children[1]->getName(), ' books'));\n    }\n\n    private function createFixture(): void\n    {\n        $this->parent->addChild($this->firstChild);\n        $this->parent->addChild($this->secondChild);\n        $this->_em->persist($this->parent);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function assertForeignKeyIs($value, ECommerceCategory $child): void\n    {\n        $foreignKey = $this->_em->getConnection()->executeQuery('SELECT parent_id FROM ecommerce_categories WHERE id=?', [$child->getId()])->fetchOne();\n        self::assertEquals($value, $foreignKey);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToManyUnidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLeg;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLocation;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingRoute;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\n\n/**\n * Tests a bidirectional one-to-one association mapping (without inheritance).\n */\nclass OneToManyUnidirectionalAssociationTest extends OrmFunctionalTestCase\n{\n    /** @phpstan-var array<string, RoutingLocation> */\n    protected $locations = [];\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('routing');\n\n        parent::setUp();\n\n        $locations = ['Berlin', 'Bonn', 'Brasilia', 'Atlanta'];\n\n        foreach ($locations as $locationName) {\n            $location       = new RoutingLocation();\n            $location->name = $locationName;\n            $this->_em->persist($location);\n            $this->locations[$locationName] = $location;\n        }\n\n        $this->_em->flush();\n    }\n\n    public function testPersistOwningInverseCascade(): void\n    {\n        $leg                = new RoutingLeg();\n        $leg->fromLocation  = $this->locations['Berlin'];\n        $leg->toLocation    = $this->locations['Bonn'];\n        $leg->departureDate = new DateTime('now');\n        $leg->arrivalDate   = new DateTime('now +5 hours');\n\n        $route         = new RoutingRoute();\n        $route->legs[] = $leg;\n\n        $this->_em->persist($route);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $routes = $this->_em->createQuery(\n            'SELECT r, l, f, t FROM Doctrine\\Tests\\Models\\Routing\\RoutingRoute r ' .\n            'JOIN r.legs l JOIN l.fromLocation f JOIN l.toLocation t',\n        )->getSingleResult();\n\n        self::assertCount(1, $routes->legs);\n        self::assertEquals('Berlin', $routes->legs[0]->fromLocation->name);\n        self::assertEquals('Bonn', $routes->legs[0]->toLocation->name);\n    }\n\n    public function testLegsAreUniqueToRoutes(): void\n    {\n        $leg                = new RoutingLeg();\n        $leg->fromLocation  = $this->locations['Berlin'];\n        $leg->toLocation    = $this->locations['Bonn'];\n        $leg->departureDate = new DateTime('now');\n        $leg->arrivalDate   = new DateTime('now +5 hours');\n\n        $routeA         = new RoutingRoute();\n        $routeA->legs[] = $leg;\n\n        $routeB         = new RoutingRoute();\n        $routeB->legs[] = $leg;\n\n        $this->_em->persist($routeA);\n        $this->_em->persist($routeB);\n\n        // exception depending on the underlying Database Driver\n        $this->expectException(Exception::class);\n        $this->_em->flush();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Tests a bidirectional one-to-one association mapping (without inheritance).\n */\nclass OneToOneBidirectionalAssociationTest extends OrmFunctionalTestCase\n{\n    private ECommerceCustomer $customer;\n\n    private ECommerceCart $cart;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->customer = new ECommerceCustomer();\n        $this->customer->setName('John Doe');\n        $this->cart = new ECommerceCart();\n        $this->cart->setPayment('Credit card');\n    }\n\n    public function testSavesAOneToOneAssociationWithCascadeSaveSet(): void\n    {\n        $this->customer->setCart($this->cart);\n        $this->_em->persist($this->customer);\n        $this->_em->flush();\n\n        $this->assertCartForeignKeyIs($this->customer->getId());\n    }\n\n    public function testDoesNotSaveAnInverseSideSet(): void\n    {\n        $this->customer->brokenSetCart($this->cart);\n        $this->_em->persist($this->customer);\n        $this->_em->flush();\n\n        $this->assertCartForeignKeyIs(null);\n    }\n\n    public function testRemovesOneToOneAssociation(): void\n    {\n        $this->customer->setCart($this->cart);\n        $this->_em->persist($this->customer);\n        $this->customer->removeCart();\n\n        $this->_em->flush();\n\n        $this->assertCartForeignKeyIs(null);\n    }\n\n    public function testEagerLoad(): void\n    {\n        $this->createFixture();\n\n        $query    = $this->_em->createQuery('select c, ca from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer c join c.cart ca');\n        $result   = $query->getResult();\n        $customer = $result[0];\n\n        self::assertInstanceOf(ECommerceCart::class, $customer->getCart());\n        self::assertEquals('paypal', $customer->getCart()->getPayment());\n    }\n\n    public function testLazyLoadsObjectsOnTheOwningSide(): void\n    {\n        $this->createFixture();\n        $metadata                                         = $this->_em->getClassMetadata(ECommerceCart::class);\n        $metadata->associationMappings['customer']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query  = $this->_em->createQuery('select c from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart c');\n        $result = $query->getResult();\n        $cart   = $result[0];\n\n        self::assertInstanceOf(ECommerceCustomer::class, $cart->getCustomer());\n        self::assertEquals('Giorgio', $cart->getCustomer()->getName());\n    }\n\n    public function testInverseSideIsNeverLazy(): void\n    {\n        $this->createFixture();\n        $metadata                                       = $this->_em->getClassMetadata(ECommerceCustomer::class);\n        $metadata->associationMappings['mentor']->fetch = ClassMetadata::FETCH_EAGER;\n\n        $query    = $this->_em->createQuery('select c from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer c');\n        $result   = $query->getResult();\n        $customer = $result[0];\n\n        self::assertNull($customer->getMentor());\n        self::assertInstanceOf(ECommerceCart::class, $customer->getCart());\n        self::assertFalse($this->isUninitializedObject($customer->getCart()));\n        self::assertEquals('paypal', $customer->getCart()->getPayment());\n    }\n\n    public function testUpdateWithProxyObject(): void\n    {\n        $cust = new ECommerceCustomer();\n        $cust->setName('Roman');\n        $cart = new ECommerceCart();\n        $cart->setPayment('CARD');\n        $cust->setCart($cart);\n\n        $this->_em->persist($cust);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertInstanceOf(ECommerceCart::class, $cust->getCart());\n        self::assertEquals('Roman', $cust->getName());\n        self::assertSame($cust, $cart->getCustomer());\n\n        $query = $this->_em->createQuery('select ca from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart ca where ca.id =?1');\n        $query->setParameter(1, $cart->getId());\n\n        $cart2 = $query->getSingleResult();\n\n        $cart2->setPayment('CHEQUE');\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query2 = $this->_em->createQuery('select ca, c from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart ca left join ca.customer c where ca.id =?1');\n        $query2->setParameter(1, $cart->getId());\n\n        $cart3 = $query2->getSingleResult();\n\n        self::assertInstanceOf(ECommerceCustomer::class, $cart3->getCustomer());\n        self::assertEquals('Roman', $cart3->getCustomer()->getName());\n    }\n\n    protected function createFixture(): void\n    {\n        $customer = new ECommerceCustomer();\n        $customer->setName('Giorgio');\n        $cart = new ECommerceCart();\n        $cart->setPayment('paypal');\n        $customer->setCart($cart);\n\n        $this->_em->persist($customer);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function assertCartForeignKeyIs($value): void\n    {\n        $foreignKey = $this->_em->getConnection()->executeQuery('SELECT customer_id FROM ecommerce_carts WHERE id=?', [$this->cart->getId()])->fetchOne();\n        self::assertEquals($value, $foreignKey);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneEagerLoadingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-952')]\nclass OneToOneEagerLoadingTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            Train::class,\n            TrainDriver::class,\n            TrainOwner::class,\n            Waggon::class,\n            TrainOrder::class,\n        );\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadOneToOneOwningSide(): void\n    {\n        $train  = new Train(new TrainOwner('Alexander'));\n        $driver = new TrainDriver('Benjamin');\n        $waggon = new Waggon();\n\n        $train->setDriver($driver);\n        $train->addWaggon($waggon);\n\n        $this->_em->persist($train); // cascades\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $train = $this->_em->find($train::class, $train->id);\n        self::assertFalse($this->isUninitializedObject($train->driver));\n        self::assertEquals('Benjamin', $train->driver->name);\n\n        $this->assertQueryCount(1);\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadOneToOneNullOwningSide(): void\n    {\n        $train = new Train(new TrainOwner('Alexander'));\n\n        $this->_em->persist($train); // cascades\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $train = $this->_em->find($train::class, $train->id);\n        self::assertNull($train->driver);\n\n        $this->assertQueryCount(1);\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadOneToOneInverseSide(): void\n    {\n        $owner = new TrainOwner('Alexander');\n        $train = new Train($owner);\n\n        $this->_em->persist($train); // cascades\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $this->_em->find($owner::class, $owner->id);\n        self::assertFalse($this->isUninitializedObject($owner->train));\n        self::assertInstanceOf(Train::class, $owner->train);\n\n        $this->assertQueryCount(1);\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadOneToOneNullInverseSide(): void\n    {\n        $driver = new TrainDriver('Dagny Taggert');\n\n        $this->_em->persist($driver);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($driver->train);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $driver = $this->_em->find($driver::class, $driver->id);\n        self::assertNull($driver->train);\n\n        $this->assertQueryCount(1);\n    }\n\n    public function testEagerLoadManyToOne(): void\n    {\n        $train  = new Train(new TrainOwner('Alexander'));\n        $waggon = new Waggon();\n        $train->addWaggon($waggon);\n\n        $this->_em->persist($train); // cascades\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $waggon = $this->_em->find($waggon::class, $waggon->id);\n        self::assertFalse($this->isUninitializedObject($waggon->train));\n        self::assertInstanceOf(Train::class, $waggon->train);\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides(): void\n    {\n        $train  = new Train(new TrainOwner('Alexander'));\n        $driver = new TrainDriver('Benjamin');\n\n        $this->_em->persist($train);\n        $this->_em->persist($driver);\n        $this->_em->flush();\n        $trainId  = $train->id;\n        $driverId = $driver->id;\n        $this->_em->clear();\n\n        $train = $this->_em->find($train::class, $train->id);\n        self::assertNotNull($train, 'It should be possible to find the train even though it has no driver');\n        self::assertSame($trainId, $train->id);\n\n        $this->_em->clear();\n        $driver = $this->_em->find($driver::class, $driver->id);\n        self::assertNotNull($driver, 'It should be possible to find the driver even though they drive no train');\n        self::assertSame($driverId, $driver->id);\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSide(): void\n    {\n        $waggon = new Waggon();\n\n        // It should have a train\n        $train = new Train(new TrainOwner('Alexander'));\n        $train->addWaggon($waggon);\n\n        $this->_em->persist($train);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $waggon = $this->_em->find($waggon::class, $waggon->id);\n\n        // The last query is the eager loading of the owner of the train\n        $this->assertSQLEquals(\n            'SELECT t0.id AS id_1, t0.name AS name_2, t3.id AS id_4, t3.driver_id AS driver_id_5, t3.owner_id AS owner_id_6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)',\n            $this->getLastLoggedQuery()['sql'],\n        );\n\n        // The one before is the fetching of the waggon and train\n        $this->assertSQLEquals(\n            'SELECT t0.id AS id_1, t0.train_id AS train_id_2, t3.id AS id_4, t3.driver_id AS driver_id_5, t3.owner_id AS owner_id_6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?',\n            $this->getLastLoggedQuery(1)['sql'],\n        );\n    }\n\n    #[Group('non-cacheable')]\n    public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningSide(): void\n    {\n        $owner = new TrainOwner('Alexander');\n        $this->_em->persist($owner);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $owner = $this->_em->find($owner::class, $owner->id);\n        self::assertNotNull($owner, 'An owner without a train should be able to exist.');\n    }\n\n    #[Group('DDC-1946')]\n    public function testEagerLoadingDoesNotBreakRefresh(): void\n    {\n        $train = new Train(new TrainOwner('Johannes'));\n        $order = new TrainOrder($train);\n        $this->_em->persist($train);\n        $this->_em->persist($order);\n        $this->_em->flush();\n\n        $this->_em->getConnection()->executeStatement('UPDATE TrainOrder SET train_id = NULL');\n\n        self::assertSame($train, $order->train);\n        $this->_em->refresh($order);\n        self::assertNull($order->train, 'Train reference was not refreshed to NULL.');\n    }\n}\n\n#[Entity]\nclass Train\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /**\n     * Owning side\n     *\n     * @var TrainDriver\n     */\n    #[OneToOne(targetEntity: 'TrainDriver', inversedBy: 'train', fetch: 'EAGER', cascade: ['persist'])]\n    #[JoinColumn(nullable: true)]\n    public $driver;\n\n    /**\n     * Owning side\n     *\n     * @var TrainOwner\n     */\n    #[OneToOne(targetEntity: 'TrainOwner', inversedBy: 'train', fetch: 'EAGER', cascade: ['persist'])]\n    #[JoinColumn(nullable: false)]\n    public $owner;\n\n    /** @phpstan-var Collection<int, Waggon> */\n    #[OneToMany(targetEntity: 'Waggon', mappedBy: 'train', cascade: ['persist'])]\n    public $waggons;\n\n    public function __construct(TrainOwner $owner)\n    {\n        $this->waggons = new ArrayCollection();\n        $this->setOwner($owner);\n    }\n\n    public function setDriver(TrainDriver $driver): void\n    {\n        $this->driver = $driver;\n        $driver->setTrain($this);\n    }\n\n    public function setOwner(TrainOwner $owner): void\n    {\n        $this->owner = $owner;\n        $owner->setTrain($this);\n    }\n\n    public function addWaggon(Waggon $w): void\n    {\n        $w->setTrain($this);\n        $this->waggons[] = $w;\n    }\n}\n\n#[Entity]\nclass TrainDriver\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /**\n     * Inverse side\n     *\n     * @var Train\n     */\n    #[OneToOne(targetEntity: 'Train', mappedBy: 'driver', fetch: 'EAGER')]\n    public $train;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n\n    public function setTrain(Train $t): void\n    {\n        $this->train = $t;\n    }\n}\n\n#[Entity]\nclass TrainOwner\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /**\n     * Inverse side\n     *\n     * @var Train\n     */\n    #[OneToOne(targetEntity: 'Train', mappedBy: 'owner', fetch: 'EAGER')]\n    public $train;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n\n    public function setTrain(Train $t): void\n    {\n        $this->train = $t;\n    }\n}\n\n#[Entity]\nclass Waggon\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n    /** @var Train */\n    #[ManyToOne(targetEntity: 'Train', inversedBy: 'waggons', fetch: 'EAGER')]\n    #[JoinColumn(nullable: false)]\n    public $train;\n\n    public function setTrain($train): void\n    {\n        $this->train = $train;\n    }\n}\n\n#[Entity]\nclass TrainOrder\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[OneToOne(targetEntity: 'Train', fetch: 'EAGER')]\n        public Train|null $train = null,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneInverseSideLoadAfterDqlQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\OneToOneInverseSideLoad\\InverseSide;\nuse Doctrine\\Tests\\Models\\OneToOneInverseSideLoad\\OwningSide;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass OneToOneInverseSideLoadAfterDqlQueryTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(OwningSide::class, InverseSide::class);\n    }\n\n    #[Group('GH-6759')]\n    public function testInverseSideOneToOneLoadedAfterDqlQuery(): void\n    {\n        $owner   = new OwningSide();\n        $inverse = new InverseSide();\n\n        $owner->id       = 'owner';\n        $inverse->id     = 'inverse';\n        $owner->inverse  = $inverse;\n        $inverse->owning = $owner;\n\n        $this->_em->persist($owner);\n        $this->_em->persist($inverse);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fetchedInverse = $this\n            ->_em\n            ->createQueryBuilder()\n            ->select('inverse')\n            ->from(InverseSide::class, 'inverse')\n            ->andWhere('inverse.id = :id')\n            ->setParameter('id', 'inverse')\n            ->getQuery()\n            ->getSingleResult();\n        assert($fetchedInverse instanceof InverseSide);\n\n        self::assertInstanceOf(InverseSide::class, $fetchedInverse);\n        self::assertInstanceOf(OwningSide::class, $fetchedInverse->owning);\n\n        $this->assertSQLEquals(\n            'select o0_.id as id_0 from one_to_one_inverse_side_load_inverse o0_ where o0_.id = ?',\n            $this->getLastLoggedQuery(1)['sql'],\n        );\n\n        $this->assertSQLEquals(\n            'select t0.id as id_1, t0.inverse as inverse_2 from one_to_one_inverse_side_load_owning t0 WHERE t0.inverse = ?',\n            $this->getLastLoggedQuery()['sql'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad\\InverseSide;\nuse Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad\\InverseSideIdTarget;\nuse Doctrine\\Tests\\Models\\OneToOneInverseSideWithAssociativeIdLoad\\OwningSide;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(OwningSide::class, InverseSideIdTarget::class, InverseSide::class);\n    }\n\n    #[Group('GH-11108')]\n    public function testInverseSideWithAssociativeIdOneToOneLoadedAfterDqlQuery(): void\n    {\n        $owner     = new OwningSide();\n        $inverseId = new InverseSideIdTarget();\n        $inverse   = new InverseSide();\n\n        $owner->id              = 'owner';\n        $inverseId->id          = 'inverseId';\n        $inverseId->inverseSide = $inverse;\n        $inverse->associativeId = $inverseId;\n        $owner->inverse         = $inverse;\n        $inverse->owning        = $owner;\n\n        $this->_em->persist($owner);\n        $this->_em->persist($inverseId);\n        $this->_em->persist($inverse);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fetchedInverse = $this\n            ->_em\n            ->createQueryBuilder()\n            ->select('inverse')\n            ->from(InverseSide::class, 'inverse')\n            ->andWhere('inverse.associativeId = :associativeId')\n            ->setParameter('associativeId', 'inverseId')\n            ->getQuery()\n            ->getSingleResult();\n        assert($fetchedInverse instanceof InverseSide);\n\n        self::assertInstanceOf(InverseSide::class, $fetchedInverse);\n        self::assertInstanceOf(InverseSideIdTarget::class, $fetchedInverse->associativeId);\n        self::assertInstanceOf(OwningSide::class, $fetchedInverse->owning);\n\n        $this->assertSQLEquals(\n            'select o0_.associativeid as associativeid_0 from one_to_one_inverse_side_assoc_id_load_inverse o0_ where o0_.associativeid = ?',\n            $this->getLastLoggedQuery(1)['sql'],\n        );\n\n        $this->assertSQLEquals(\n            'select t0.id as id_1, t0.inverse as inverse_2 from one_to_one_inverse_side_assoc_id_load_owning t0 where t0.inverse = ?',\n            $this->getLastLoggedQuery()['sql'],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneOrphanRemovalTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Tests a bidirectional one-to-one association mapping with orphan removal.\n */\nclass OneToOneOrphanRemovalTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testOrphanRemoval(): void\n    {\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'romanb';\n        $user->name     = 'Roman B.';\n\n        $address          = new CmsAddress();\n        $address->country = 'de';\n        $address->zip     = 1234;\n        $address->city    = 'Berlin';\n\n        $user->setAddress($address);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $userId = $user->getId();\n\n        $this->_em->clear();\n\n        $userProxy = $this->_em->getReference(CmsUser::class, $userId);\n\n        $this->_em->remove($userProxy);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsUser should be removed by EntityManager');\n\n        $query  = $this->_em->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsAddress should be removed by orphanRemoval');\n    }\n\n    public function testOrphanRemovalWhenUnlink(): void\n    {\n        $user           = new CmsUser();\n        $user->status   = 'dev';\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin Eberlei';\n\n        $email        = new CmsEmail();\n        $email->email = 'beberlei@domain.com';\n\n        $user->setEmail($email);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $userId = $user->getId();\n\n        $this->_em->clear();\n\n        $user = $this->_em->find(CmsUser::class, $userId);\n\n        $user->setEmail(null);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('SELECT e FROM Doctrine\\Tests\\Models\\CMS\\CmsEmail e');\n        $result = $query->getResult();\n\n        self::assertCount(0, $result, 'CmsEmail should be removed by orphanRemoval');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests a self referential one-to-one association mapping (without inheritance).\n * Relation is defined as the mentor that a customer choose. The mentor could\n * help only one other customer, while a customer can choose only one mentor\n * for receiving support.\n * Inverse side is not present.\n */\nclass OneToOneSelfReferentialAssociationTest extends OrmFunctionalTestCase\n{\n    private ECommerceCustomer $customer;\n\n    private ECommerceCustomer $mentor;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->customer = new ECommerceCustomer();\n        $this->customer->setName('Anakin Skywalker');\n        $this->mentor = new ECommerceCustomer();\n        $this->mentor->setName('Obi-wan Kenobi');\n    }\n\n    public function testSavesAOneToOneAssociationWithCascadeSaveSet(): void\n    {\n        $this->customer->setMentor($this->mentor);\n        $this->_em->persist($this->customer);\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs($this->mentor->getId());\n    }\n\n    public function testRemovesOneToOneAssociation(): void\n    {\n        $this->customer->setMentor($this->mentor);\n        $this->_em->persist($this->customer);\n        $this->customer->removeMentor();\n\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs(null);\n    }\n\n    public function testFind(): void\n    {\n        $id = $this->createFixture();\n\n        $customer = $this->_em->find(ECommerceCustomer::class, $id);\n        self::assertFalse($this->isUninitializedObject($customer->getMentor()));\n    }\n\n    public function testEagerLoadsAssociation(): void\n    {\n        $customerId = $this->createFixture();\n\n        $query = $this->_em->createQuery('select c, m from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer c left join c.mentor m where c.id = :id');\n        $query->setParameter('id', $customerId);\n\n        $result   = $query->getResult();\n        $customer = $result[0];\n        $this->assertLoadingOfAssociation($customer);\n    }\n\n    #[Group('mine')]\n    public function testLazyLoadsAssociation(): void\n    {\n        $this->createFixture();\n\n        $metadata                                       = $this->_em->getClassMetadata(ECommerceCustomer::class);\n        $metadata->associationMappings['mentor']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query    = $this->_em->createQuery(\"select c from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer c where c.name='Luke Skywalker'\");\n        $result   = $query->getResult();\n        $customer = $result[0];\n        $this->assertLoadingOfAssociation($customer);\n    }\n\n    public function testMultiSelfReference(): void\n    {\n        $this->createSchemaForModels(MultiSelfReference::class);\n\n        $entity1 = new MultiSelfReference();\n        $this->_em->persist($entity1);\n        $entity1->setOther1($entity2 = new MultiSelfReference());\n        $entity1->setOther2($entity3 = new MultiSelfReference());\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity2 = $this->_em->find($entity1::class, $entity1->getId());\n\n        self::assertInstanceOf(MultiSelfReference::class, $entity2->getOther1());\n        self::assertInstanceOf(MultiSelfReference::class, $entity2->getOther2());\n        self::assertNull($entity2->getOther1()->getOther1());\n        self::assertNull($entity2->getOther1()->getOther2());\n        self::assertNull($entity2->getOther2()->getOther1());\n        self::assertNull($entity2->getOther2()->getOther2());\n    }\n\n    public function assertLoadingOfAssociation($customer): void\n    {\n        self::assertInstanceOf(ECommerceCustomer::class, $customer->getMentor());\n        self::assertEquals('Obi-wan Kenobi', $customer->getMentor()->getName());\n    }\n\n    public function assertForeignKeyIs($value): void\n    {\n        $foreignKey = $this->_em->getConnection()->executeQuery('SELECT mentor_id FROM ecommerce_customers WHERE id=?', [$this->customer->getId()])->fetchOne();\n        self::assertEquals($value, $foreignKey);\n    }\n\n    private function createFixture(): int\n    {\n        $customer = new ECommerceCustomer();\n        $customer->setName('Luke Skywalker');\n        $mentor = new ECommerceCustomer();\n        $mentor->setName('Obi-wan Kenobi');\n        $customer->setMentor($mentor);\n\n        $this->_em->persist($customer);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return $customer->getId();\n    }\n}\n\n#[Entity]\nclass MultiSelfReference\n{\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'MultiSelfReference', cascade: ['persist'])]\n    #[JoinColumn(name: 'other1', referencedColumnName: 'id')]\n    private MultiSelfReference|null $other1 = null;\n\n    #[OneToOne(targetEntity: 'MultiSelfReference', cascade: ['persist'])]\n    #[JoinColumn(name: 'other2', referencedColumnName: 'id')]\n    private MultiSelfReference|null $other2 = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setOther1(MultiSelfReference $other1): void\n    {\n        $this->other1 = $other1;\n    }\n\n    public function getOther1(): MultiSelfReference|null\n    {\n        return $this->other1;\n    }\n\n    public function setOther2(MultiSelfReference $other2): void\n    {\n        $this->other2 = $other2;\n    }\n\n    public function getOther2(): MultiSelfReference|null\n    {\n        return $this->other2;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneSingleTableInheritanceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance\\Cat;\nuse Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance\\LitterBox;\nuse Doctrine\\Tests\\Models\\OneToOneSingleTableInheritance\\Pet;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass OneToOneSingleTableInheritanceTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            Pet::class,\n            Cat::class,\n            LitterBox::class,\n        );\n    }\n\n    /**\n     * Tests a unidirectional one-to-one association mapping from an inheritance child class\n     */\n    #[Group('DDC-3517')]\n    #[Group('#1265')]\n    public function testFindFromOneToOneOwningSideJoinedTableInheritance(): void\n    {\n        $cat            = new Cat();\n        $cat->litterBox = new LitterBox();\n\n        $this->_em->persist($cat);\n        $this->_em->persist($cat->litterBox);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $foundCat = $this->_em->find(Pet::class, $cat->id);\n        assert($foundCat instanceof Cat);\n\n        self::assertInstanceOf(Cat::class, $foundCat);\n        self::assertSame($cat->id, $foundCat->id);\n        self::assertInstanceOf(LitterBox::class, $foundCat->litterBox);\n        self::assertSame($cat->litterBox->id, $foundCat->litterBox->id);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceShipping;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests a unidirectional one-to-one association mapping (without inheritance).\n * Inverse side is not present.\n */\nclass OneToOneUnidirectionalAssociationTest extends OrmFunctionalTestCase\n{\n    private ECommerceProduct $product;\n\n    private ECommerceShipping $shipping;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $this->product = new ECommerceProduct();\n        $this->product->setName('Doctrine 2 Manual');\n        $this->shipping = new ECommerceShipping();\n        $this->shipping->setDays('5');\n    }\n\n    public function testSavesAOneToOneAssociationWithCascadeSaveSet(): void\n    {\n        $this->product->setShipping($this->shipping);\n        $this->_em->persist($this->product);\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs($this->shipping->getId());\n    }\n\n    public function testRemovesOneToOneAssociation(): void\n    {\n        $this->product->setShipping($this->shipping);\n        $this->_em->persist($this->product);\n        $this->product->removeShipping();\n\n        $this->_em->flush();\n\n        $this->assertForeignKeyIs(null);\n    }\n\n    public function testEagerLoad(): void\n    {\n        $this->createFixture();\n\n        $query   = $this->_em->createQuery('select p, s from Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p left join p.shipping s');\n        $result  = $query->getResult();\n        $product = $result[0];\n\n        self::assertInstanceOf(ECommerceShipping::class, $product->getShipping());\n        self::assertEquals(1, $product->getShipping()->getDays());\n    }\n\n    public function testLazyLoadsObjects(): void\n    {\n        $this->createFixture();\n        $metadata                                         = $this->_em->getClassMetadata(ECommerceProduct::class);\n        $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $query   = $this->_em->createQuery('select p from Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p');\n        $result  = $query->getResult();\n        $product = $result[0];\n\n        self::assertInstanceOf(ECommerceShipping::class, $product->getShipping());\n        self::assertEquals(1, $product->getShipping()->getDays());\n    }\n\n    public function testDoesNotLazyLoadObjectsIfConfigurationDoesNotAllowIt(): void\n    {\n        $this->createFixture();\n\n        $query = $this->_em->createQuery('select p from Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p');\n        $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);\n\n        $result  = $query->getResult();\n        $product = $result[0];\n\n        self::assertNull($product->getShipping());\n    }\n\n    protected function createFixture(): void\n    {\n        $product = new ECommerceProduct();\n        $product->setName('Php manual');\n        $shipping = new ECommerceShipping();\n        $shipping->setDays('1');\n        $product->setShipping($shipping);\n\n        $this->_em->persist($product);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function assertForeignKeyIs($value): void\n    {\n        $foreignKey = $this->_em->getConnection()->executeQuery(\n            'SELECT shipping_id FROM ecommerce_products WHERE id=?',\n            [$this->product->getId()],\n        )->fetchOne();\n        self::assertEquals($value, $foreignKey);\n    }\n\n    #[Group('DDC-762')]\n    public function testNullForeignKey(): void\n    {\n        $product = new ECommerceProduct();\n        $product->setName('Doctrine 2 Manual');\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $product = $this->_em->find($product::class, $product->getId());\n\n        self::assertNull($product->getShipping());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OrderedCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLeg;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLocation;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingRoute;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingRouteBooking;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass OrderedCollectionTest extends OrmFunctionalTestCase\n{\n    /** @phpstan-var array<string, RoutingLocation> */\n    protected $locations = [];\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('routing');\n\n        parent::setUp();\n\n        $locations = ['Berlin', 'Bonn', 'Brasilia', 'Atlanta'];\n\n        foreach ($locations as $locationName) {\n            $location       = new RoutingLocation();\n            $location->name = $locationName;\n            $this->_em->persist($location);\n            $this->locations[$locationName] = $location;\n        }\n\n        $this->_em->flush();\n    }\n\n    public function createPersistedRouteWithLegs(): int\n    {\n        $route = new RoutingRoute();\n\n        $leg1                = new RoutingLeg();\n        $leg1->fromLocation  = $this->locations['Berlin'];\n        $leg1->toLocation    = $this->locations['Bonn'];\n        $leg1->departureDate = new DateTime('now');\n        $leg1->arrivalDate   = new DateTime('now +5 hours');\n\n        $leg2                = new RoutingLeg();\n        $leg2->fromLocation  = $this->locations['Bonn'];\n        $leg2->toLocation    = $this->locations['Brasilia'];\n        $leg2->departureDate = new DateTime('now +6 hours');\n        $leg2->arrivalDate   = new DateTime('now +24 hours');\n\n        $route->legs[] = $leg2;\n        $route->legs[] = $leg1;\n\n        $this->_em->persist($route);\n        $this->_em->flush();\n        $routeId = $route->id;\n        $this->_em->clear();\n\n        return $routeId;\n    }\n\n    public function testLazyManyToManyCollectionIsRetrievedWithOrderByClause(): void\n    {\n        $routeId = $this->createPersistedRouteWithLegs();\n\n        $route = $this->_em->find(RoutingRoute::class, $routeId);\n\n        self::assertCount(2, $route->legs);\n        self::assertEquals('Berlin', $route->legs[0]->fromLocation->getName());\n        self::assertEquals('Bonn', $route->legs[1]->fromLocation->getName());\n    }\n\n    public function testLazyOneToManyCollectionIsRetrievedWithOrderByClause(): void\n    {\n        $route = new RoutingRoute();\n\n        $this->_em->persist($route);\n        $this->_em->flush();\n        $routeId = $route->id;\n\n        $booking1                = new RoutingRouteBooking();\n        $booking1->passengerName = 'Guilherme';\n        $booking2                = new RoutingRouteBooking();\n        $booking2->passengerName = 'Benjamin';\n\n        $route->bookings[] = $booking1;\n        $booking1->route   = $route;\n        $route->bookings[] = $booking2;\n        $booking2->route   = $route;\n\n        $this->_em->persist($booking1);\n        $this->_em->persist($booking2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $route = $this->_em->find(RoutingRoute::class, $routeId);\n\n        self::assertCount(2, $route->bookings);\n        self::assertEquals('Benjamin', $route->bookings[0]->getPassengerName());\n        self::assertEquals('Guilherme', $route->bookings[1]->getPassengerName());\n    }\n\n    public function testOrderedResultFromDqlQuery(): void\n    {\n        $routeId = $this->createPersistedRouteWithLegs();\n\n        $route = $this->_em->createQuery('SELECT r, l FROM Doctrine\\Tests\\Models\\Routing\\RoutingRoute r JOIN r.legs l WHERE r.id = ?1')\n                           ->setParameter(1, $routeId)\n                           ->getSingleResult();\n\n        self::assertCount(2, $route->legs);\n        self::assertEquals('Berlin', $route->legs[0]->fromLocation->getName());\n        self::assertEquals('Bonn', $route->legs[1]->fromLocation->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/OrderedJoinedTableInheritanceCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Functional tests for the Single Table Inheritance mapping strategy.\n */\nclass OrderedJoinedTableInheritanceCollectionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            OJTICPet::class,\n            OJTICCat::class,\n            OJTICDog::class,\n        );\n\n        $dog       = new OJTICDog();\n        $dog->name = 'Poofy';\n\n        $dog1       = new OJTICDog();\n        $dog1->name = 'Zampa';\n        $dog2       = new OJTICDog();\n        $dog2->name = 'Aari';\n\n        $dog1->mother = $dog;\n        $dog2->mother = $dog;\n\n        $dog->children[] = $dog1;\n        $dog->children[] = $dog2;\n\n        $this->_em->persist($dog);\n        $this->_em->persist($dog1);\n        $this->_em->persist($dog2);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testOrderdOneToManyCollection(): void\n    {\n        $poofy = $this->_em->createQuery(\"SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\OJTICPet p WHERE p.name = 'Poofy'\")->getSingleResult();\n\n        self::assertEquals('Aari', $poofy->children[0]->getName());\n        self::assertEquals('Zampa', $poofy->children[1]->getName());\n\n        $this->_em->clear();\n\n        $result = $this->_em->createQuery(\n            \"SELECT p, c FROM Doctrine\\Tests\\ORM\\Functional\\OJTICPet p JOIN p.children c WHERE p.name = 'Poofy'\",\n        )\n                ->getResult();\n\n        self::assertCount(1, $result);\n        $poofy = $result[0];\n\n        self::assertEquals('Aari', $poofy->children[0]->getName());\n        self::assertEquals('Zampa', $poofy->children[1]->getName());\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['cat' => 'OJTICCat', 'dog' => 'OJTICDog'])]\nabstract class OJTICPet\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $name;\n\n    /** @var OJTICPet */\n    #[ManyToOne(targetEntity: 'OJTICPet')]\n    public $mother;\n\n    /** @phpstan-var Collection<int, OJTICPet> */\n    #[OneToMany(targetEntity: 'OJTICPet', mappedBy: 'mother')]\n    #[OrderBy(['name' => 'ASC'])]\n    public $children;\n\n    /** @phpstan-var Collection<int, OJTICPet> */\n    #[JoinTable(name: 'OTJIC_Pet_Friends')]\n    #[JoinColumn(name: 'pet_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'friend_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'OJTICPet')]\n    #[OrderBy(['name' => 'ASC'])]\n    public $friends;\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n\n#[Entity]\nclass OJTICCat extends OJTICPet\n{\n}\n\n#[Entity]\nclass OJTICDog extends OJTICPet\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PaginationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\ComparisonExpression;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalPrimary;\nuse Doctrine\\ORM\\Query\\AST\\Literal;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\AST\\WhereClause;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObjectType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeParent;\nuse Doctrine\\Tests\\Models\\Pagination\\Company;\nuse Doctrine\\Tests\\Models\\Pagination\\Department;\nuse Doctrine\\Tests\\Models\\Pagination\\Logo;\nuse Doctrine\\Tests\\Models\\Pagination\\User1;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionMethod;\nuse RuntimeException;\n\nuse function count;\nuse function iterator_to_array;\nuse function sprintf;\n\n#[Group('DDC-1613')]\nclass PaginationTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n        $this->useModelSet('pagination');\n        $this->useModelSet('company');\n        $this->useModelSet('custom_id_object_type');\n\n        if (DBALType::hasType(CustomIdObjectType::NAME)) {\n            DBALType::overrideType(CustomIdObjectType::NAME, CustomIdObjectType::class);\n        } else {\n            DBALType::addType(CustomIdObjectType::NAME, CustomIdObjectType::class);\n        }\n\n        parent::setUp();\n\n        $this->populate();\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testCountSimpleWithoutJoin($useOutputWalkers): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(9, $paginator);\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testCountWithFetchJoin($useOutputWalkers): void\n    {\n        $dql   = 'SELECT u,g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(9, $paginator);\n    }\n\n    public function testCountComplexWithOutputWalker(): void\n    {\n        $dql   = 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g LEFT JOIN g.users u GROUP BY g HAVING COUNT(u.id) > 0';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers(true);\n        self::assertCount(3, $paginator);\n    }\n\n    public function testCountComplexWithoutOutputWalker(): void\n    {\n        $dql   = 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g LEFT JOIN g.users u GROUP BY g HAVING COUNT(u.id) > 0';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers(false);\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Cannot count query that uses a HAVING clause. Use the output walkers for pagination');\n\n        self::assertCount(3, $paginator);\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testCountWithComplexScalarOrderBy($useOutputWalkers): void\n    {\n        $dql   = 'SELECT l FROM Doctrine\\Tests\\Models\\Pagination\\Logo l ORDER BY l.imageWidth * l.imageHeight DESC';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(9, $paginator);\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateSimpleWithoutJoin($useOutputWalkers, $fetchJoinCollection): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(9, $paginator->getIterator());\n\n        // Test with limit\n        $query->setMaxResults(3);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(3, $paginator->getIterator());\n\n        // Test with limit and offset\n        $query->setMaxResults(3)->setFirstResult(4);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(3, $paginator->getIterator());\n    }\n\n    private function iterateWithOrderAsc($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        // Ascending\n        $dql   = $baseDql . ' ASC';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(9, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '0', $result[0]->$checkField);\n    }\n\n    private function iterateWithOrderAscWithLimit($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        // Ascending\n        $dql   = $baseDql . ' ASC';\n        $query = $this->_em->createQuery($dql);\n\n        // With limit\n        $query->setMaxResults(3);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(3, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '0', $result[0]->$checkField);\n    }\n\n    private function iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        // Ascending\n        $dql   = $baseDql . ' ASC';\n        $query = $this->_em->createQuery($dql);\n\n        // With offset\n        $query->setMaxResults(3)->setFirstResult(3);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(3, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '3', $result[0]->$checkField);\n    }\n\n    private function iterateWithOrderDesc($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        $dql   = $baseDql . ' DESC';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(9, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '8', $result[0]->$checkField);\n    }\n\n    private function iterateWithOrderDescWithLimit($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        $dql   = $baseDql . ' DESC';\n        $query = $this->_em->createQuery($dql);\n\n        // With limit\n        $query->setMaxResults(3);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(3, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '8', $result[0]->$checkField);\n    }\n\n    private function iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $baseDql, $checkField): void\n    {\n        $dql   = $baseDql . ' DESC';\n        $query = $this->_em->createQuery($dql);\n\n        // With offset\n        $query->setMaxResults(3)->setFirstResult(3);\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        $iter = $paginator->getIterator();\n        self::assertCount(3, $iter);\n        $result = iterator_to_array($iter);\n        self::assertEquals($checkField . '5', $result[0]->$checkField);\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateSimpleWithoutJoinWithOrder($useOutputWalkers, $fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username';\n        $this->iterateWithOrderAsc($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDesc($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateSimpleWithoutJoinWithOrderAndLimit($useOutputWalkers, $fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username';\n        $this->iterateWithOrderAscWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDescWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateSimpleWithoutJoinWithOrderAndLimitAndOffset($useOutputWalkers, $fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username';\n        $this->iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrder($fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT l FROM Doctrine\\Tests\\Models\\Pagination\\Logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAsc(true, $fetchJoinCollection, $dql, 'image');\n        $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'image');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndLimit($fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT l FROM Doctrine\\Tests\\Models\\Pagination\\Logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAscWithLimit(true, $fetchJoinCollection, $dql, 'image');\n        $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'image');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndLimitAndOffset($fetchJoinCollection): void\n    {\n        // Ascending\n        $dql = 'SELECT l FROM Doctrine\\Tests\\Models\\Pagination\\Logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAscWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'image');\n        $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'image');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoin($useOutputWalkers): void\n    {\n        $dql   = 'SELECT u,g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers($useOutputWalkers);\n        self::assertCount(9, $paginator->getIterator());\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrder($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g ORDER BY u.username';\n        $this->iterateWithOrderAsc($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrderAndLimit($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g ORDER BY u.username';\n        $this->iterateWithOrderAscWithLimit($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrderAndLimitAndOffset($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g ORDER BY u.username';\n        $this->iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateWithRegularJoinWithOrderByColumnFromJoined($useOutputWalkers, $fetchJoinCollection): void\n    {\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAsc($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDesc($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimit($useOutputWalkers, $fetchJoinCollection): void\n    {\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAscWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDescWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection): void\n    {\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n        $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoined($fetchJoinCollection): void\n    {\n        // long function name is loooooooooooong\n\n        $dql = 'SELECT c FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageHeight * l.imageWidth';\n        $this->iterateWithOrderAsc(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoinedWithLimit($fetchJoinCollection): void\n    {\n        // long function name is loooooooooooong\n\n        $dql = 'SELECT c FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageHeight * l.imageWidth';\n        $this->iterateWithOrderAscWithLimit(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoinedWithLimitAndOffset($fetchJoinCollection): void\n    {\n        // long function name is loooooooooooong\n\n        $dql = 'SELECT c FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageHeight * l.imageWidth';\n        $this->iterateWithOrderAscWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrderByColumnFromJoined($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g,e FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAsc($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimit($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g,e FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAscWithLimit($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimitAndOffset($useOutputWalkers): void\n    {\n        $dql = 'SELECT u,g,e FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email';\n        $this->iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, true, $dql, 'username');\n        $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'username');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoined($fetchJoinCollection): void\n    {\n        $dql = 'SELECT c,l FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAsc(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimit($fetchJoinCollection): void\n    {\n        $dql = 'SELECT c,l FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAscWithLimit(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimitAndOffset($fetchJoinCollection): void\n    {\n        $dql = 'SELECT c,l FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight';\n        $this->iterateWithOrderAscWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    #[DataProvider('fetchJoinCollection')]\n    public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimitAndOffsetWithInheritanceType($fetchJoinCollection): void\n    {\n        $dql = 'SELECT u FROM Doctrine\\Tests\\Models\\Pagination\\User u ORDER BY u.id';\n        $this->iterateWithOrderAscWithLimit(true, $fetchJoinCollection, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'name');\n    }\n\n    public function testIterateComplexWithOutputWalker(): void\n    {\n        $dql   = 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g LEFT JOIN g.users u GROUP BY g HAVING COUNT(u.id) > 0';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers(true);\n        self::assertCount(3, $paginator->getIterator());\n    }\n\n    public function testJoinedClassTableInheritance(): void\n    {\n        $dql   = 'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyManager c ORDER BY c.startDate';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        self::assertCount(1, $paginator->getIterator());\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromBoth($useOutputWalkers): void\n    {\n        $dql     = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $dqlAsc  = $dql . ' ASC, d.name';\n        $dqlDesc = $dql . ' DESC, d.name';\n        $this->iterateWithOrderAsc($useOutputWalkers, true, $dqlAsc, 'name');\n        $this->iterateWithOrderDesc($useOutputWalkers, true, $dqlDesc, 'name');\n    }\n\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromBothWithLimitWithOutputWalker(): void\n    {\n        $dql     = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $dqlAsc  = $dql . ' ASC, d.name';\n        $dqlDesc = $dql . ' DESC, d.name';\n        $this->iterateWithOrderAscWithLimit(true, true, $dqlAsc, 'name');\n        $this->iterateWithOrderDescWithLimit(true, true, $dqlDesc, 'name');\n    }\n\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromBothWithLimitWithoutOutputWalker(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers.');\n\n        $dql     = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $dqlAsc  = $dql . ' ASC, d.name';\n        $dqlDesc = $dql . ' DESC, d.name';\n        $this->iterateWithOrderAscWithLimit(false, true, $dqlAsc, 'name');\n        $this->iterateWithOrderDescWithLimit(false, true, $dqlDesc, 'name');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRoot($useOutputWalkers): void\n    {\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $this->iterateWithOrderAsc($useOutputWalkers, true, $dql, 'name');\n        $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'name');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLimit($useOutputWalkers): void\n    {\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $this->iterateWithOrderAscWithLimit($useOutputWalkers, true, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'name');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLimitAndOffset($useOutputWalkers): void\n    {\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY c.name';\n        $this->iterateWithOrderAscWithLimitAndOffset($useOutputWalkers, true, $dql, 'name');\n        $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'name');\n    }\n\n    #[DataProvider('useOutputWalkers')]\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromJoined($useOutputWalkers): void\n    {\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY d.name';\n        $this->iterateWithOrderAsc($useOutputWalkers, true, $dql, 'name');\n        $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'name');\n    }\n\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromJoinedWithLimitWithOutputWalker(): void\n    {\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY d.name';\n        $this->iterateWithOrderAscWithLimit(true, true, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit(true, true, $dql, 'name');\n    }\n\n    public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromJoinedWithLimitWithoutOutputWalker(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers.');\n\n        $dql = 'SELECT c, d FROM Doctrine\\Tests\\Models\\Pagination\\Company c JOIN c.departments d ORDER BY d.name';\n\n        $this->iterateWithOrderAscWithLimit(false, true, $dql, 'name');\n        $this->iterateWithOrderDescWithLimit(false, true, $dql, 'name');\n    }\n\n    public function testCountWithCountSubqueryInWhereClauseWithOutputWalker(): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE ((SELECT COUNT(s.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser s) = 9) ORDER BY u.id desc';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(true);\n        self::assertCount(9, $paginator);\n    }\n\n    public function testIterateWithCountSubqueryInWhereClause(): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE ((SELECT COUNT(s.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser s) = 9) ORDER BY u.id desc';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(true);\n\n        $users = iterator_to_array($paginator->getIterator());\n        self::assertCount(9, $users);\n        foreach ($users as $i => $user) {\n            self::assertEquals('username' . (8 - $i), $user->username);\n        }\n    }\n\n    public function testDetectOutputWalker(): void\n    {\n        // This query works using the output walkers but causes an exception using the TreeWalker\n        $dql   = 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g LEFT JOIN g.users u GROUP BY g HAVING COUNT(u.id) > 0';\n        $query = $this->_em->createQuery($dql);\n\n        // If the Paginator detects the custom output walker it should fall back to using the\n        // Tree walkers for pagination, which leads to an exception. If the query works, the output walkers were used\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlWalker::class);\n        $paginator = new Paginator($query);\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Cannot count query that uses a HAVING clause. Use the output walkers for pagination');\n\n        count($paginator);\n    }\n\n    /**\n     * Test using a paginator when the entity attribute name and corresponding column name are not the same.\n     */\n    public function testPaginationWithColumnAttributeNameDifference(): void\n    {\n        $dql   = 'SELECT c FROM Doctrine\\Tests\\Models\\Pagination\\Company c ORDER BY c.id';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->getIterator();\n\n        self::assertCount(9, $paginator->getIterator());\n    }\n\n    public function testCloneQuery(): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $query = $this->_em->createQuery($dql);\n\n        $paginator = new Paginator($query);\n        $paginator->getIterator();\n\n        self::assertTrue($query->getParameters()->isEmpty());\n    }\n\n    public function testQueryWalkerIsKept(): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $query = $this->_em->createQuery($dql);\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CustomPaginationTestTreeWalker::class]);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(false);\n        self::assertCount(1, $paginator->getIterator());\n        self::assertEquals(1, $paginator->count());\n    }\n\n    #[Group('GH-7890')]\n    public function testCustomIdTypeWithoutOutputWalker(): void\n    {\n        $this->_em->persist(new CustomIdObjectTypeParent(new CustomIdObject('foo')));\n        $this->_em->flush();\n\n        $dql   = 'SELECT p FROM Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeParent p';\n        $query = $this->_em->createQuery($dql);\n        $query->setMaxResults(1);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(false);\n\n        $matchedItems = iterator_to_array($paginator->getIterator());\n\n        self::assertCount(1, $matchedItems);\n        self::assertInstanceOf(CustomIdObjectTypeParent::class, $matchedItems[0]);\n        self::assertSame('foo', (string) $matchedItems[0]->id);\n    }\n\n    public function testCountQueryStripsParametersInSelect(): void\n    {\n        $query = $this->_em->createQuery(\n            'SELECT u, (CASE WHEN u.id < :vipMaxId THEN 1 ELSE 0 END) AS hidden promotedFirst\n            FROM Doctrine\\\\Tests\\\\Models\\\\CMS\\\\CmsUser u\n            WHERE u.id < :id or 1=1',\n        );\n        $query->setParameter('vipMaxId', 10);\n        $query->setParameter('id', 100);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        $paginator = new Paginator($query);\n\n        $getCountQuery = new ReflectionMethod($paginator, 'getCountQuery');\n\n        self::assertCount(2, $getCountQuery->invoke($paginator)->getParameters());\n        self::assertCount(9, $paginator);\n\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlOutputWalker::class);\n\n        $paginator = new Paginator($query);\n\n        // if select part of query is replaced with count(...) paginator should remove\n        // parameters from query object not used in new query.\n        self::assertCount(1, $getCountQuery->invoke($paginator)->getParameters());\n        self::assertCount(9, $paginator);\n    }\n\n    #[DataProvider('useOutputWalkersAndFetchJoinCollection')]\n    public function testPaginationWithSubSelectOrderByExpression($useOutputWalker, $fetchJoinCollection): void\n    {\n        $query = $this->_em->createQuery(\n            <<<'SQL'\n            SELECT u,\n                (\n                    SELECT MAX(a.version)\n                    FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n                    WHERE a.user = u\n                ) AS HIDDEN max_version\n            FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n            ORDER BY max_version DESC\nSQL,\n        );\n\n        $paginator = new Paginator($query, $fetchJoinCollection);\n        $paginator->setUseOutputWalkers($useOutputWalker);\n\n        self::assertCount(9, $paginator->getIterator());\n    }\n\n    public function testDifferentResultLengthsDoNotRequireExtraQueryCacheEntries(): void\n    {\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id >= :id ORDER BY u.id';\n        $query = $this->_em->createQuery($dql);\n        $query->setMaxResults(10);\n\n        $query->setParameter('id', 1);\n        $paginator     = new Paginator($query);\n        $initialResult = iterator_to_array($paginator->getIterator()); // exercise the Paginator\n        self::assertCount(9, $initialResult);\n\n        $initialQueryCount = count(self::$queryCache->getValues());\n\n        $query->setParameter('id', $initialResult[1]->id); // skip the first result element\n        $paginator = new Paginator($query);\n        self::assertCount(8, $paginator->getIterator()); // exercise the Paginator again, with a smaller result set\n\n        $newCount = count(self::$queryCache->getValues());\n\n        self::assertSame($initialQueryCount, $newCount);\n    }\n\n    public function populate(): void\n    {\n        $groups = [];\n        for ($j = 0; $j < 3; $j++) {\n            $group       = new CmsGroup();\n            $group->name = sprintf('group%d', $j);\n            $groups[]    = $group;\n            $this->_em->persist($group);\n        }\n\n        for ($i = 0; $i < 9; $i++) {\n            $user               = new CmsUser();\n            $user->name         = 'Name' . $i;\n            $user->username     = 'username' . $i;\n            $user->status       = 'active';\n            $user->email        = new CmsEmail();\n            $user->email->user  = $user;\n            $user->email->email = sprintf('email%d', $i);\n            for ($j = 0; $j < 3; $j++) {\n                $user->addGroup($groups[$j]);\n            }\n\n            $this->_em->persist($user);\n            for ($j = 0; $j < $i + 1; $j++) {\n                $article        = new CmsArticle();\n                $article->topic = 'topic' . $i . $j;\n                $article->text  = 'text' . $i . $j;\n                $article->setAuthor($user);\n                $article->version = 0;\n                $this->_em->persist($article);\n            }\n        }\n\n        for ($i = 0; $i < 9; $i++) {\n            $company                    = new Company();\n            $company->name              = 'name' . $i;\n            $company->logo              = new Logo();\n            $company->logo->image       = 'image' . $i;\n            $company->logo->imageWidth  = 100 + $i;\n            $company->logo->imageHeight = 100 + $i;\n            $company->logo->company     = $company;\n            for ($j = 0; $j < 3; $j++) {\n                $department             = new Department();\n                $department->name       = 'name' . $i . $j;\n                $department->company    = $company;\n                $company->departments[] = $department;\n            }\n\n            $this->_em->persist($company);\n        }\n\n        for ($i = 0; $i < 9; $i++) {\n            $user        = new User1();\n            $user->name  = 'name' . $i;\n            $user->email = 'email' . $i;\n            $this->_em->persist($user);\n        }\n\n        $manager = new CompanyManager();\n        $manager->setName('Roman B.');\n        $manager->setTitle('Foo');\n        $manager->setDepartment('IT');\n        $manager->setSalary(100000);\n\n        $this->_em->persist($manager);\n\n        $this->_em->flush();\n    }\n\n    /** @phpstan-return list<array{bool}> */\n    public static function useOutputWalkers(): array\n    {\n        return [\n            [true],\n            [false],\n        ];\n    }\n\n    /** @phpstan-return list<array{bool}> */\n    public static function fetchJoinCollection(): array\n    {\n        return [\n            [true],\n            [false],\n        ];\n    }\n\n    /** @phpstan-return list<array{bool, bool}> */\n    public static function useOutputWalkersAndFetchJoinCollection(): array\n    {\n        return [\n            [true, false],\n            [true, true],\n            [false, false],\n            [false, true],\n        ];\n    }\n}\n\nclass CustomPaginationTestTreeWalker extends TreeWalkerAdapter\n{\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        $condition = new ConditionalPrimary();\n\n        $path       = new PathExpression(PathExpression::TYPE_STATE_FIELD, 'u', 'name');\n        $path->type = PathExpression::TYPE_STATE_FIELD;\n\n        $condition->simpleConditionalExpression = new ComparisonExpression(\n            $path,\n            '=',\n            new Literal(Literal::STRING, 'Name1'),\n        );\n\n        $selectStatement->whereClause = new WhereClause($condition);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ParserResultSerializationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Closure;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Exec\\FinalizedSelectExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\PreparedExecutorFinalizer;\nuse Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse ReflectionMethod;\nuse Symfony\\Component\\VarExporter\\VarExporter;\n\nuse function file_get_contents;\nuse function rtrim;\nuse function serialize;\nuse function unserialize;\n\nclass ParserResultSerializationTest extends OrmFunctionalTestCase\n{\n    use VerifyDeprecations;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */\n    #[DataProvider('provideToSerializedAndBack')]\n    #[IgnoreDeprecations]\n    public function testSerializeParserResultForQueryWithSqlWalker(Closure $toSerializedAndBack): void\n    {\n        $query = $this->_em\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee u WHERE u.name = :name');\n\n        // Use the (legacy) SqlWalker which directly puts an SqlExecutor instance into the parser result\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, Query\\SqlWalker::class);\n\n        $parserResult = self::parseQuery($query);\n        $unserialized = $toSerializedAndBack($parserResult);\n\n        $this->assertInstanceOf(ParserResult::class, $unserialized);\n        $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());\n        $this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());\n        $this->assertNotNull($unserialized->prepareSqlExecutor($query));\n    }\n\n    /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */\n    #[DataProvider('provideToSerializedAndBack')]\n    public function testSerializeParserResultForQueryWithSqlOutputWalker(Closure $toSerializedAndBack): void\n    {\n        $query = $this->_em\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee u WHERE u.name = :name');\n\n        $parserResult = self::parseQuery($query);\n        $unserialized = $toSerializedAndBack($parserResult);\n\n        $this->assertInstanceOf(ParserResult::class, $unserialized);\n        $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());\n        $this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());\n        $this->assertNotNull($unserialized->prepareSqlExecutor($query));\n    }\n\n    /** @return Generator<string, array{Closure(ParserResult): ParserResult}> */\n    public static function provideToSerializedAndBack(): Generator\n    {\n        yield 'native serialization function' => [\n            static function (ParserResult $parserResult): ParserResult {\n                return unserialize(serialize($parserResult));\n            },\n        ];\n\n        yield 'symfony/var-exporter' => [\n            static function (ParserResult $parserResult): ParserResult {\n                return eval('return ' . VarExporter::export($parserResult) . ';');\n            },\n        ];\n    }\n\n    #[DataProvider('provideSerializedSingleSelectResults')]\n    public function testUnserializeSingleSelectResult(string $serialized): void\n    {\n        $unserialized = unserialize($serialized);\n\n        $this->assertInstanceOf(ParserResult::class, $unserialized);\n        $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());\n        $this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());\n\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11188');\n        $this->assertInstanceOf(SingleSelectExecutor::class, $unserialized->getSqlExecutor());\n        $this->assertIsString($unserialized->getSqlExecutor()->getSqlStatements());\n    }\n\n    /** @return Generator<string, array{string}> */\n    public static function provideSerializedSingleSelectResults(): Generator\n    {\n        yield '2.17.0' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_17_0.txt'), \"\\n\")];\n    }\n\n    public function testSymfony44ProvidedData(): void\n    {\n        $sqlExecutor      = new FinalizedSelectExecutor('test');\n        $sqlFinalizer     = new PreparedExecutorFinalizer($sqlExecutor);\n        $resultSetMapping = $this->createMock(ResultSetMapping::class);\n\n        $parserResult = new ParserResult();\n        $parserResult->setSqlFinalizer($sqlFinalizer);\n        $parserResult->setResultSetMapping($resultSetMapping);\n        $parserResult->addParameterMapping('name', 0);\n\n        $exported     = VarExporter::export($parserResult);\n        $unserialized = eval('return ' . $exported . ';');\n\n        $this->assertInstanceOf(ParserResult::class, $unserialized);\n        $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());\n        $this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());\n        $this->assertEquals($sqlExecutor, $unserialized->prepareSqlExecutor($this->createMock(Query::class)));\n    }\n\n    private static function parseQuery(Query $query): ParserResult\n    {\n        $r = new ReflectionMethod($query, 'parse');\n\n        return $r->invoke($query);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PersistentCollectionCriteriaTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\LazyCriteriaCollection;\nuse Doctrine\\Tests\\Models\\Quote\\Group;\nuse Doctrine\\Tests\\Models\\Quote\\User as QuoteUser;\nuse Doctrine\\Tests\\Models\\Tweet\\Tweet;\nuse Doctrine\\Tests\\Models\\Tweet\\User;\nuse Doctrine\\Tests\\Models\\Tweet\\User as TweetUser;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass PersistentCollectionCriteriaTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('tweet');\n        $this->useModelSet('quote');\n        $this->useModelSet('vct_manytomany_extralazy');\n\n        parent::setUp();\n    }\n\n    public function tearDown(): void\n    {\n        if ($this->_em) {\n            $this->_em->getConfiguration()->setEntityNamespaces([]);\n        }\n\n        parent::tearDown();\n    }\n\n    public function loadTweetFixture(): void\n    {\n        $author       = new TweetUser();\n        $author->name = 'ngal';\n        $this->_em->persist($author);\n\n        $tweet1          = new Tweet();\n        $tweet1->content = 'Foo';\n        $author->addTweet($tweet1);\n\n        $tweet2          = new Tweet();\n        $tweet2->content = 'Bar';\n        $author->addTweet($tweet2);\n\n        $this->_em->flush();\n\n        unset($author, $tweet1, $tweet2);\n\n        $this->_em->clear();\n    }\n\n    public function loadQuoteFixture(): void\n    {\n        $user       = new QuoteUser();\n        $user->name = 'mgal';\n        $this->_em->persist($user);\n\n        $quote1 = new Group('quote1');\n        $user->groups->add($quote1);\n\n        $quote2 = new Group('quote2');\n        $user->groups->add($quote2);\n\n        $this->_em->flush();\n\n        $this->_em->clear();\n    }\n\n    public function testCanCountWithoutLoadingPersistentCollection(): void\n    {\n        $this->loadTweetFixture();\n\n        $repository = $this->_em->getRepository(User::class);\n\n        $user   = $repository->findOneBy(['name' => 'ngal']);\n        $tweets = $user->tweets->matching(Criteria::create(true));\n\n        self::assertInstanceOf(LazyCriteriaCollection::class, $tweets);\n        self::assertFalse($tweets->isInitialized());\n        self::assertCount(2, $tweets);\n        self::assertFalse($tweets->isInitialized());\n\n        // Make sure it works with constraints\n        $tweets = $user->tweets->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('content', 'Foo'),\n        ));\n\n        self::assertInstanceOf(LazyCriteriaCollection::class, $tweets);\n        self::assertFalse($tweets->isInitialized());\n        self::assertCount(1, $tweets);\n        self::assertFalse($tweets->isInitialized());\n    }\n\n    public function testCanHandleComplexTypesOnAssociation(): void\n    {\n        $parent      = new OwningManyToManyExtraLazyEntity();\n        $parent->id2 = 'Alice';\n\n        $this->_em->persist($parent);\n\n        $child      = new InversedManyToManyExtraLazyEntity();\n        $child->id1 = 'Bob';\n\n        $this->_em->persist($child);\n\n        $parent->associatedEntities->add($child);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $parent = $this->_em->find(OwningManyToManyExtraLazyEntity::class, $parent->id2);\n\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq('id1', 'Bob'));\n\n        $result = $parent->associatedEntities->matching($criteria);\n\n        $this->assertCount(1, $result);\n        $this->assertEquals('Bob', $result[0]->id1);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PersistentCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Persistence\\PersistentObject;\nuse Doctrine\\Tests\\Models\\PersistentObject\\PersistentCollectionContent;\nuse Doctrine\\Tests\\Models\\PersistentObject\\PersistentCollectionHolder;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\n\nclass PersistentCollectionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        if (! class_exists(PersistentObject::class)) {\n            self::markTestSkipped('This test requires doctrine/persistence 2');\n        }\n\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersistentCollectionHolder::class,\n            PersistentCollectionContent::class,\n        );\n\n        PersistentObject::setObjectManager($this->_em);\n    }\n\n    public function testPersist(): void\n    {\n        $collectionHolder = new PersistentCollectionHolder();\n        $content          = new PersistentCollectionContent('first element');\n        $collectionHolder->addElement($content);\n\n        $this->_em->persist($collectionHolder);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $collectionHolder = $this->_em->find(PersistentCollectionHolder::class, $collectionHolder->getId());\n        $collectionHolder->getCollection();\n\n        $content = new PersistentCollectionContent('second element');\n        $collectionHolder->addElement($content);\n\n        self::assertEquals(2, $collectionHolder->getCollection()->count());\n    }\n\n    /**\n     * Tests that PersistentCollection::isEmpty() does not initialize the collection when FETCH_EXTRA_LAZY is used.\n     */\n    public function testExtraLazyIsEmptyDoesNotInitializeCollection(): void\n    {\n        $collectionHolder = new PersistentCollectionHolder();\n\n        $this->_em->persist($collectionHolder);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $collectionHolder = $this->_em->find(PersistentCollectionHolder::class, $collectionHolder->getId());\n        $collection       = $collectionHolder->getRawCollection();\n\n        self::assertTrue($collection->isEmpty());\n        self::assertFalse($collection->isInitialized());\n\n        $collectionHolder->addElement(new PersistentCollectionContent());\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $collectionHolder = $this->_em->find(PersistentCollectionHolder::class, $collectionHolder->getId());\n        $collection       = $collectionHolder->getRawCollection();\n\n        self::assertFalse($collection->isEmpty());\n        self::assertFalse($collection->isInitialized());\n    }\n\n    #[Group('#1206')]\n    #[Group('DDC-3430')]\n    public function testMatchingDoesNotModifyTheGivenCriteria(): void\n    {\n        $collectionHolder = new PersistentCollectionHolder();\n\n        $this->_em->persist($collectionHolder);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $criteria = Criteria::create(true);\n\n        $collectionHolder = $this->_em->find(PersistentCollectionHolder::class, $collectionHolder->getId());\n        $collectionHolder->getCollection()->matching($criteria);\n\n        self::assertEmpty($criteria->getWhereExpression());\n        self::assertEmpty($criteria->getFirstResult());\n        self::assertEmpty($criteria->getMaxResults());\n        self::assertEmpty($criteria->getOrderings());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PostFlushEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\PostFlushEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\n\n/**\n * PostFlushEventTest\n */\nclass PostFlushEventTest extends OrmFunctionalTestCase\n{\n    private PostFlushListener $listener;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->listener = new PostFlushListener();\n        $evm            = $this->_em->getEventManager();\n        $evm->addEventListener(Events::postFlush, $this->listener);\n    }\n\n    public function testListenerShouldBeNotified(): void\n    {\n        $this->_em->persist($this->createNewValidUser());\n        $this->_em->flush();\n        self::assertTrue($this->listener->wasNotified);\n    }\n\n    public function testListenerShouldNotBeNotifiedWhenFlushThrowsException(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'dfreudenberger';\n        $this->_em->persist($user);\n        $exceptionRaised = false;\n\n        try {\n            $this->_em->flush();\n        } catch (Exception) {\n            $exceptionRaised = true;\n        }\n\n        self::assertTrue($exceptionRaised);\n        self::assertFalse($this->listener->wasNotified);\n    }\n\n    public function testListenerShouldReceiveEntityManagerThroughArgs(): void\n    {\n        $this->_em->persist($this->createNewValidUser());\n        $this->_em->flush();\n        $receivedEm = $this->listener->receivedArgs->getObjectManager();\n        self::assertSame($this->_em, $receivedEm);\n    }\n\n    private function createNewValidUser(): CmsUser\n    {\n        $user           = new CmsUser();\n        $user->username = 'dfreudenberger';\n        $user->name     = 'Daniel Freudenberger';\n\n        return $user;\n    }\n}\n\nclass PostFlushListener\n{\n    /** @var bool */\n    public $wasNotified = false;\n\n    /** @var PostFlushEventArgs */\n    public $receivedArgs;\n\n    public function postFlush(PostFlushEventArgs $args): void\n    {\n        $this->wasNotified  = true;\n        $this->receivedArgs = $args;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PostLoadEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\PostLoadEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse RuntimeException;\n\nclass PostLoadEventTest extends OrmFunctionalTestCase\n{\n    private int|null $userId = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->loadFixture();\n    }\n\n    public function testLoadedEntityUsingFindShouldTriggerEvent(): void\n    {\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsUser and CmsAddres, because it's a ToOne inverse side on CmsUser\n        $mockListener\n            ->expects(self::exactly(2))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $this->_em->find(CmsUser::class, $this->userId);\n    }\n\n    public function testLoadedEntityUsingQueryShouldTriggerEvent(): void\n    {\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsUser and CmsAddres, because it's a ToOne inverse side on CmsUser\n        $mockListener\n            ->expects(self::exactly(2))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $query = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :id');\n\n        $query->setParameter('id', $this->userId);\n        $query->getResult();\n    }\n\n    public function testLoadedAssociationToOneShouldTriggerEvent(): void\n    {\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsUser (root), CmsAddress (ToOne inverse side), CmsEmail (joined association)\n        $mockListener\n            ->expects(self::exactly(3))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $query = $this->_em->createQuery('SELECT u, e FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e WHERE u.id = :id');\n\n        $query->setParameter('id', $this->userId);\n        $query->getResult();\n    }\n\n    public function testLoadedAssociationToManyShouldTriggerEvent(): void\n    {\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsUser (root), CmsAddress (ToOne inverse side), 2 CmsPhonenumber (joined association)\n        $mockListener\n            ->expects(self::exactly(4))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $query = $this->_em->createQuery('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.phonenumbers p WHERE u.id = :id');\n\n        $query->setParameter('id', $this->userId);\n        $query->getResult();\n    }\n\n    public function testLoadedProxyEntityShouldTriggerEvent(): void\n    {\n        $eventManager = $this->_em->getEventManager();\n\n        // Should not be invoked during getReference call\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        $mockListener\n            ->expects(self::never())\n            ->method('postLoad');\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $userProxy = $this->_em->getReference(CmsUser::class, $this->userId);\n\n        // Now deactivate original listener and attach new one\n        $eventManager->removeEventListener([Events::postLoad], $mockListener);\n\n        $mockListener2 = $this->createMock(PostLoadListener::class);\n\n        $mockListener2\n            ->expects(self::exactly(2))\n            ->method('postLoad');\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener2);\n\n        $userProxy->getName();\n    }\n\n    public function testLoadedProxyPartialShouldTriggerEvent(): void\n    {\n        $eventManager = $this->_em->getEventManager();\n\n        // Should not be invoked during getReference call\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsUser (partially loaded), CmsAddress (inverse ToOne), 2 CmsPhonenumber\n        $mockListener\n            ->expects(self::exactly(4))\n            ->method('postLoad');\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $query = $this->_em->createQuery('SELECT PARTIAL u.{id, name}, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.phonenumbers p WHERE u.id = :id');\n\n        $query->setParameter('id', $this->userId);\n        $query->getResult();\n    }\n\n    public function testLoadedProxyAssociationToOneShouldTriggerEvent(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // CmsEmail (proxy)\n        $mockListener\n            ->expects(self::exactly(1))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $emailProxy = $user->getEmail();\n\n        $emailProxy->getEmail();\n    }\n\n    public function testLoadedProxyAssociationToManyShouldTriggerEvent(): void\n    {\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $mockListener = $this->createMock(PostLoadListener::class);\n\n        // 2 CmsPhonenumber (proxy)\n        $mockListener\n            ->expects(self::exactly(2))\n            ->method('postLoad');\n\n        $eventManager = $this->_em->getEventManager();\n\n        $eventManager->addEventListener([Events::postLoad], $mockListener);\n\n        $phonenumbersCol = $user->getPhonenumbers();\n\n        $phonenumbersCol->first();\n    }\n\n    #[Group('DDC-3005')]\n    public function testAssociationsArePopulatedWhenEventIsFired(): void\n    {\n        $checkerListener = new PostLoadListenerCheckAssociationsArePopulated();\n        $this->_em->getEventManager()->addEventListener([Events::postLoad], $checkerListener);\n\n        $qb = $this->_em->getRepository(CmsUser::class)->createQueryBuilder('u');\n        $qb->leftJoin('u.email', 'email');\n        $qb->addSelect('email');\n        $qb->getQuery()->getSingleResult();\n\n        self::assertTrue($checkerListener->checked, 'postLoad event is not invoked');\n        self::assertTrue($checkerListener->populated, 'Association of email is not populated in postLoad event');\n    }\n\n    #[Group('DDC-3005')]\n    public function testEventRaisedCorrectTimesWhenOtherEntityLoadedInEventHandler(): void\n    {\n        $eventManager = $this->_em->getEventManager();\n        $listener     = new PostLoadListenerLoadEntityInEventHandler();\n        $eventManager->addEventListener([Events::postLoad], $listener);\n\n        $this->_em->find(CmsUser::class, $this->userId);\n        self::assertSame(1, $listener->countHandledEvents(CmsUser::class), CmsUser::class . ' should be handled once!');\n        self::assertSame(1, $listener->countHandledEvents(CmsEmail::class), CmsEmail::class . ' should be handled once!');\n    }\n\n    private function loadFixture(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $user->setAddress($address);\n\n        $email = new CmsEmail();\n        $email->setEmail('roman@domain.com');\n\n        $user->setEmail($email);\n\n        $ph1              = new CmsPhonenumber();\n        $ph1->phonenumber = '0301234';\n\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '987654321';\n\n        $user->addPhonenumber($ph1);\n        $user->addPhonenumber($ph2);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->userId = $user->getId();\n\n        $this->_em->clear();\n    }\n}\n\nclass PostLoadListener\n{\n    public function postLoad(PostLoadEventArgs $event): void\n    {\n        // Expected to be mocked out\n        echo 'Should never be called!';\n    }\n}\n\nclass PostLoadListenerCheckAssociationsArePopulated\n{\n    /** @var bool */\n    public $checked = false;\n\n    /** @var bool */\n    public $populated = false;\n\n    public function postLoad(PostLoadEventArgs $event): void\n    {\n        $object = $event->getObject();\n        if ($object instanceof CmsUser) {\n            if ($this->checked) {\n                throw new RuntimeException('Expected to be one user!');\n            }\n\n            $this->checked   = true;\n            $this->populated = $object->getEmail() !== null;\n        }\n    }\n}\n\nclass PostLoadListenerLoadEntityInEventHandler\n{\n    /** @var array<class-string, int> */\n    private array $firedByClasses = [];\n\n    public function postLoad(PostLoadEventArgs $event): void\n    {\n        $object = $event->getObject();\n        $class  = DefaultProxyClassNameResolver::getClass($object);\n        if (! isset($this->firedByClasses[$class])) {\n            $this->firedByClasses[$class] = 1;\n        } else {\n            $this->firedByClasses[$class]++;\n        }\n\n        if ($object instanceof CmsUser) {\n            $object->getEmail()->getEmail();\n        }\n    }\n\n    public function countHandledEvents($className): int\n    {\n        return $this->firedByClasses[$className] ?? 0;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PrePersistEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Event\\PrePersistEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function uniqid;\n\nclass PrePersistEventTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            EntityWithUnmappedEntity::class,\n            EntityWithCascadeAssociation::class,\n        );\n    }\n\n    public function testCallingPersistInPrePersistHook(): void\n    {\n        $entityWithUnmapped = new EntityWithUnmappedEntity();\n        $entityWithCascade  = new EntityWithCascadeAssociation();\n\n        $entityWithUnmapped->unmapped = $entityWithCascade;\n        $entityWithCascade->cascaded  = $entityWithUnmapped;\n\n        $this->_em->getEventManager()->addEventListener(Events::prePersist, new PrePersistUnmappedPersistListener());\n        $this->_em->persist($entityWithUnmapped);\n\n        $this->assertTrue($this->_em->getUnitOfWork()->isScheduledForInsert($entityWithCascade));\n        $this->assertTrue($this->_em->getUnitOfWork()->isScheduledForInsert($entityWithUnmapped));\n    }\n}\n\nclass PrePersistUnmappedPersistListener\n{\n    public function prePersist(PrePersistEventArgs $args): void\n    {\n        $object = $args->getObject();\n\n        if ($object instanceof EntityWithUnmappedEntity) {\n            $uow = $args->getObjectManager()->getUnitOfWork();\n\n            if ($object->unmapped && ! $uow->isInIdentityMap($object->unmapped) && ! $uow->isScheduledForInsert($object->unmapped)) {\n                $args->getObjectManager()->persist($object->unmapped);\n            }\n        }\n    }\n}\n\n#[Entity]\nclass EntityWithUnmappedEntity\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    public EntityWithCascadeAssociation|null $unmapped = null;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n\n#[Entity]\nclass EntityWithCascadeAssociation\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public string $id;\n\n    #[ManyToOne(targetEntity: EntityWithUnmappedEntity::class, cascade: ['persist'])]\n    public EntityWithUnmappedEntity|null $cascaded = null;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/PropertyHooksTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Tests\\Models\\PropertyHooks\\MappingVirtualProperty;\nuse Doctrine\\Tests\\Models\\PropertyHooks\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\n\n#[RequiresPhp('>= 8.4.0')]\nclass PropertyHooksTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof AbstractMySQLPlatform) {\n            self::markTestSkipped('MySQL/MariaDB is case-insensitive by default, and the logic of this test relies on case sensitivity.');\n        }\n\n        if (! $this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            $this->markTestSkipped('Property hooks require native lazy objects to be enabled.');\n        }\n\n        $this->createSchemaForModels(\n            User::class,\n        );\n    }\n\n    public function testMapPropertyHooks(): void\n    {\n        $user           = new User();\n        $user->fullName = 'John Doe';\n        $user->language = 'EN';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $user->id);\n\n        self::assertEquals('John', $user->first);\n        self::assertEquals('Doe', $user->last);\n        self::assertEquals('John Doe', $user->fullName);\n        self::assertEquals('EN', $user->language, 'The property hook uppercases the language.');\n\n        $language = $this->_em->createQuery('SELECT u.language FROM ' . User::class . ' u WHERE u.id = :id')\n            ->setParameter('id', $user->id)\n            ->getSingleScalarResult();\n\n        $this->assertEquals('en', $language, 'Selecting a field from DQL does not go through the property hook, accessing raw data.');\n\n        $this->_em->clear();\n\n        $user = $this->_em->getRepository(User::class)->findOneBy(['language' => 'EN']);\n\n        self::assertNull($user);\n\n        $user = $this->_em->getRepository(User::class)->findOneBy(['language' => 'en']);\n\n        self::assertNotNull($user);\n    }\n\n    public function testTriggerLazyLoadingWhenAccessingPropertyHooks(): void\n    {\n        $user           = new User();\n        $user->fullName = 'Ludwig von Beethoven';\n        $user->language = 'DE';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(User::class, $user->id);\n\n        $this->assertTrue($this->_em->getUnitOfWork()->isUninitializedObject($user));\n\n        self::assertEquals('Ludwig', $user->first);\n        self::assertEquals('von Beethoven', $user->last);\n        self::assertEquals('Ludwig von Beethoven', $user->fullName);\n        self::assertEquals('DE', $user->language, 'The property hook uppercases the language.');\n\n        $this->assertFalse($this->_em->getUnitOfWork()->isUninitializedObject($user));\n\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(User::class, $user->id);\n\n        self::assertEquals('Ludwig von Beethoven', $user->fullName);\n    }\n\n    public function testMappingVirtualPropertyIsNotSupported(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Mapping virtual property \"fullName\" on entity \"Doctrine\\Tests\\Models\\PropertyHooks\\MappingVirtualProperty\" is not allowed.');\n\n        $this->_em->getClassMetadata(MappingVirtualProperty::class);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Doctrine\\Tests\\Proxies\\__CG__\\Doctrine\\Tests\\Models\\CMS\\CmsUser as CmsUserProxy;\n\n/**\n * Test that Doctrine ORM correctly works with proxy instances exactly like with ordinary Entities\n *\n * The test considers two possible cases:\n *  a) __initialized__ = true and no identifier set in proxy\n *  b) __initialized__ = false and identifier set in proxy and in property\n *\n * @todo All other cases would cause lazy loading\n */\nclass ProxiesLikeEntitiesTest extends OrmFunctionalTestCase\n{\n    /** @var CmsUser */\n    protected $user;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            CmsUser::class,\n            CmsTag::class,\n            CmsPhonenumber::class,\n            CmsArticle::class,\n            CmsAddress::class,\n            CmsEmail::class,\n            CmsGroup::class,\n        );\n\n        $this->user           = new CmsUser();\n        $this->user->username = 'ocramius';\n        $this->user->name     = 'Marco';\n        $this->_em->persist($this->user);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    /**\n     * Verifies that a proxy can be successfully persisted and updated\n     */\n    public function testPersistUpdate(): void\n    {\n        // Considering case (a)\n        $proxy = $this->_em->getProxyFactory()->getProxy(CmsUser::class, ['id' => $this->user->getId()]);\n\n        $proxy->id       = null;\n        $proxy->username = 'ocra';\n        $proxy->name     = 'Marco';\n        $this->_em->persist($proxy);\n        $this->_em->flush();\n        self::assertNotNull($proxy->getId());\n        $proxy->name = 'Marco Pivetta';\n        $this->_em->getUnitOfWork()\n            ->computeChangeSet($this->_em->getClassMetadata(CmsUser::class), $proxy);\n        self::assertNotEmpty($this->_em->getUnitOfWork()->getEntityChangeSet($proxy));\n        self::assertEquals('Marco Pivetta', $this->_em->find(CmsUser::class, $proxy->getId())->name);\n        $this->_em->remove($proxy);\n        $this->_em->flush();\n    }\n\n    public function testEntityWithIdentifier(): void\n    {\n        $userId             = $this->user->getId();\n        $uninitializedProxy = $this->_em->getReference(CmsUser::class, $userId);\n        $this->assertTrue($this->isUninitializedObject($uninitializedProxy));\n\n        $this->_em->persist($uninitializedProxy);\n        $this->_em->flush();\n        self::assertTrue($this->isUninitializedObject($uninitializedProxy), 'Proxy didn\\'t get initialized during flush operations');\n        self::assertEquals($userId, $uninitializedProxy->getId());\n        $this->_em->remove($uninitializedProxy);\n        $this->_em->flush();\n    }\n\n    /**\n     * Verifying that proxies can be used without problems as query parameters\n     */\n    public function testProxyAsDqlParameterPersist(): void\n    {\n        $proxy     = $this->_em->getReference(CmsUser::class, ['id' => $this->user->getId()]);\n        $proxy->id = $this->user->getId();\n        $result    = $this\n            ->_em\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u = ?1')\n            ->setParameter(1, $proxy)\n            ->getSingleResult();\n        self::assertSame($this->user->getId(), $result->getId());\n        $this->_em->remove($proxy);\n        $this->_em->flush();\n    }\n\n    /**\n     * Verifying that proxies can be used without problems as query parameters\n     */\n    public function testFindWithProxyName(): void\n    {\n        if ($this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            self::markTestSkipped('There is no such thing as a proxy class name when native lazy objects are enabled.');\n        }\n\n        $result = $this->_em->find(CmsUserProxy::class, $this->user->getId());\n        self::assertSame($this->user->getId(), $result->getId());\n        $this->_em->clear();\n\n        $result = $this->_em->getReference(CmsUserProxy::class, $this->user->getId());\n        self::assertSame($this->user->getId(), $result->getId());\n        $this->_em->clear();\n\n        $result = $this->_em->getRepository(CmsUserProxy::class)->findOneBy(['username' => $this->user->username]);\n        self::assertSame($this->user->getId(), $result->getId());\n        $this->_em->clear();\n\n        $result = $this->_em\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Proxies\\__CG__\\Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1')\n            ->setParameter(1, $this->user->getId())\n            ->getSingleResult();\n\n        self::assertSame($this->user->getId(), $result->getId());\n        $this->_em->clear();\n    }\n\n    protected function tearDown(): void\n    {\n        $this->_em->createQuery('DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u')->execute();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryBuilderParenthesisTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass QueryBuilderParenthesisTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(QueryBuilderParenthesisEntity::class);\n    }\n\n    public function testParenthesisOnSingleLine(): void\n    {\n        $queryBuilder = $this->_em->createQueryBuilder();\n        $queryBuilder->select('o')->from(QueryBuilderParenthesisEntity::class, 'o');\n        $queryBuilder->andWhere('o.property3 = :value3')->setParameter('value3', 'x');\n        $queryBuilder->andWhere('o.property1 = :value1 OR o.property2 = :value2');\n        $queryBuilder->andWhere('o.property1 = :value1 or o.property2 = :value2');\n        $queryBuilder->setParameter('value1', 'x');\n        $queryBuilder->setParameter('value2', 'x');\n\n        $query   = $queryBuilder->getQuery();\n        $results = $query->getResult();\n        self::assertCount(0, $results);\n\n        $dql = $query->getDQL();\n\n        self::assertSame(\n            'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1 OR o.property2 = :value2) AND (o.property1 = :value1 or o.property2 = :value2)',\n            $dql,\n        );\n    }\n\n    public function testParenthesisOnMultiLine(): void\n    {\n        $queryBuilder = $this->_em->createQueryBuilder();\n        $queryBuilder->select('o')->from(QueryBuilderParenthesisEntity::class, 'o');\n        $queryBuilder->andWhere('o.property3 = :value3')->setParameter('value3', 'x');\n\n        $queryBuilder->andWhere(\n            'o.property1 = :value1\nOR o.property2 = :value2',\n        );\n        $queryBuilder->setParameter('value1', 'x');\n        $queryBuilder->setParameter('value2', 'x');\n\n        $query   = $queryBuilder->getQuery();\n        $results = $query->getResult();\n        self::assertCount(0, $results);\n\n        $dql = $query->getDQL();\n\n        self::assertSame(\n            'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1\nOR o.property2 = :value2)',\n            $dql,\n        );\n    }\n}\n\n\n#[Entity]\nclass QueryBuilderParenthesisEntity\n{\n    /** @var int|null */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string|null */\n    #[Column]\n    public $property1;\n\n    /** @var string|null */\n    #[Column]\n    public $property2;\n\n    /** @var string|null */\n    #[Column]\n    public $property3;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor;\nuse Doctrine\\ORM\\Query\\Exec\\SqlFinalizer;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse Psr\\Cache\\CacheItemInterface;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function assert;\nuse function count;\n\nclass QueryCacheTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testQueryCacheDependsOnHints(): array\n    {\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $cache = new ArrayAdapter();\n        $query->setQueryCache($cache);\n\n        $query->getResult();\n        self::assertCount(1, $cache->getValues());\n\n        $query->setHint('foo', 'bar');\n\n        $query->getResult();\n        self::assertCount(2, $cache->getValues());\n\n        return [$query, $cache];\n    }\n\n    #[Depends('testQueryCacheDependsOnHints')]\n    public function testQueryCacheDoesNotDependOnFirstResultForDefaultOutputWalker(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof Query);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        $query->setFirstResult(10);\n        $query->setMaxResults(9999);\n\n        $query->getResult();\n        self::assertCount($cacheCount, $cache->getValues());\n    }\n\n    #[Depends('testQueryCacheDependsOnHints')]\n    public function testQueryCacheDoesNotDependOnMaxResultsForDefaultOutputWalker(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof Query);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        $query->setMaxResults(10);\n\n        $query->getResult();\n        self::assertCount($cacheCount, $cache->getValues());\n    }\n\n    #[Depends('testQueryCacheDependsOnHints')]\n    public function testQueryCacheDependsOnHydrationMode(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof Query);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        $query->getArrayResult();\n        self::assertCount($cacheCount + 1, $cache->getValues());\n    }\n\n    public function testQueryCacheNoHitSaveParserResult(): void\n    {\n        $this->_em->getConfiguration()->setQueryCache($this->createMock(CacheItemPoolInterface::class));\n\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $cache = $this->createMock(CacheItemPoolInterface::class);\n        $query->setQueryCache($cache);\n\n        $cacheItem = $this->createMock(CacheItemInterface::class);\n        $cacheItem->method('isHit')->willReturn(false);\n        $cacheItem->expects(self::never())->method('get');\n        $cacheItem->expects(self::once())->method('set')->with(self::isInstanceOf(ParserResult::class))->willReturnSelf();\n        $cacheItem->method('expiresAfter')->willReturnSelf();\n\n        $cache->expects(self::once())\n            ->method('getItem')\n            ->with(self::isType('string'))\n            ->willReturn($cacheItem);\n\n        $cache\n            ->expects(self::once())\n            ->method('save')\n            ->with(self::identicalTo($cacheItem))\n            ->willReturn(true);\n\n        $query->getResult();\n    }\n\n    public function testQueryCacheHitDoesNotSaveParserResult(): void\n    {\n        $this->_em->getConfiguration()->setQueryCache($this->createMock(CacheItemPoolInterface::class));\n\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $sqlExecutorStub = new class extends AbstractSqlExecutor {\n            public function execute(Connection $conn, array $params, array $types): int\n            {\n                return 10;\n            }\n        };\n\n        $sqlFinalizerMock = $this->createMock(SqlFinalizer::class);\n        $sqlFinalizerMock->method('createExecutor')->with($query)->willReturn($sqlExecutorStub);\n\n        $parserResultMock = new ParserResult();\n        $parserResultMock->setSqlFinalizer($sqlFinalizerMock);\n\n        $cache = $this->createMock(CacheItemPoolInterface::class);\n\n        $cacheItem = $this->createMock(CacheItemInterface::class);\n        $cacheItem->method('isHit')->willReturn(true);\n        $cacheItem->method('get')->willReturn($parserResultMock);\n        $cacheItem->expects(self::never())->method('set');\n\n        $cache->expects(self::once())\n            ->method('getItem')\n            ->with(self::isType('string'))\n            ->willReturn($cacheItem);\n\n        $cache->expects(self::never())\n              ->method('save');\n\n        $query->setQueryCache($cache);\n\n        $query->getResult();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryDqlFunctionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTimeImmutable;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function round;\nuse function sprintf;\n\n/**\n * Functional Query tests.\n */\nclass QueryDqlFunctionTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->generateFixture();\n    }\n\n    public function testAggregateSum(): void\n    {\n        $salarySum = $this->_em->createQuery('SELECT SUM(m.salary) AS salary FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m')\n                               ->getSingleResult();\n\n        self::assertEquals(1_500_000, $salarySum['salary']);\n    }\n\n    public function testAggregateAvg(): void\n    {\n        $salaryAvg = $this->_em->createQuery('SELECT AVG(m.salary) AS salary FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m')\n                               ->getSingleResult();\n\n        self::assertEquals(375000, round((float) $salaryAvg['salary'], 0));\n    }\n\n    public function testAggregateMin(): void\n    {\n        $salary = $this->_em->createQuery('SELECT MIN(m.salary) AS salary FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m')\n                               ->getSingleResult();\n\n        self::assertEquals(100000, $salary['salary']);\n    }\n\n    public function testAggregateMax(): void\n    {\n        $salary = $this->_em->createQuery('SELECT MAX(m.salary) AS salary FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m')\n                               ->getSingleResult();\n\n        self::assertEquals(800000, $salary['salary']);\n    }\n\n    public function testAggregateCount(): void\n    {\n        $managerCount = $this->_em->createQuery('SELECT COUNT(m.id) AS managers FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m')\n                               ->getSingleResult();\n\n        self::assertEquals(4, $managerCount['managers']);\n    }\n\n    public function testFunctionAbs(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, ABS(m.salary * -1) AS abs FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(100000, $result[0]['abs']);\n        self::assertEquals(200000, $result[1]['abs']);\n        self::assertEquals(400000, $result[2]['abs']);\n        self::assertEquals(800000, $result[3]['abs']);\n    }\n\n    public function testFunctionConcat(): void\n    {\n        $arg = $this->_em->createQuery('SELECT m, CONCAT(m.name, m.department) AS namedep FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $arg);\n        self::assertEquals('Roman B.IT', $arg[0]['namedep']);\n        self::assertEquals('Benjamin E.HR', $arg[1]['namedep']);\n        self::assertEquals('Guilherme B.Complaint Department', $arg[2]['namedep']);\n        self::assertEquals('Jonathan W.Administration', $arg[3]['namedep']);\n    }\n\n    public function testFunctionLength(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, LENGTH(CONCAT(m.name, m.department)) AS namedeplength FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(10, $result[0]['namedeplength']);\n        self::assertEquals(13, $result[1]['namedeplength']);\n        self::assertEquals(32, $result[2]['namedeplength']);\n        self::assertEquals(25, $result[3]['namedeplength']);\n    }\n\n    public function testFunctionLocate(): void\n    {\n        $dql = \"SELECT m, LOCATE('e', LOWER(m.name)) AS loc, LOCATE('e', LOWER(m.name), 7) AS loc2 \" .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC';\n\n        $result = $this->_em->createQuery($dql)\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(0, $result[0]['loc']);\n        self::assertEquals(2, $result[1]['loc']);\n        self::assertEquals(6, $result[2]['loc']);\n        self::assertEquals(0, $result[3]['loc']);\n        self::assertEquals(0, $result[0]['loc2']);\n        self::assertEquals(10, $result[1]['loc2']);\n        self::assertEquals(9, $result[2]['loc2']);\n        self::assertEquals(0, $result[3]['loc2']);\n    }\n\n    public function testFunctionLower(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, LOWER(m.name) AS lowername FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals('roman b.', $result[0]['lowername']);\n        self::assertEquals('benjamin e.', $result[1]['lowername']);\n        self::assertEquals('guilherme b.', $result[2]['lowername']);\n        self::assertEquals('jonathan w.', $result[3]['lowername']);\n    }\n\n    public function testFunctionMod(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, MOD(m.salary, 3500) AS amod FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(2000, $result[0]['amod']);\n        self::assertEquals(500, $result[1]['amod']);\n        self::assertEquals(1000, $result[2]['amod']);\n        self::assertEquals(2000, $result[3]['amod']);\n    }\n\n    public function testFunctionSqrt(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, SQRT(m.salary) AS sqrtsalary FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(316, round((float) $result[0]['sqrtsalary']));\n        self::assertEquals(447, round((float) $result[1]['sqrtsalary']));\n        self::assertEquals(632, round((float) $result[2]['sqrtsalary']));\n        self::assertEquals(894, round((float) $result[3]['sqrtsalary']));\n    }\n\n    public function testFunctionUpper(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, UPPER(m.name) AS uppername FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals('ROMAN B.', $result[0]['uppername']);\n        self::assertEquals('BENJAMIN E.', $result[1]['uppername']);\n        self::assertEquals('GUILHERME B.', $result[2]['uppername']);\n        self::assertEquals('JONATHAN W.', $result[3]['uppername']);\n    }\n\n    public function testFunctionSubstring(): void\n    {\n        $dql = 'SELECT m, SUBSTRING(m.name, 1, 3) AS str1, SUBSTRING(m.name, 5) AS str2 ' .\n                'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.name';\n\n        $result = $this->_em->createQuery($dql)\n                         ->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals('Ben', $result[0]['str1']);\n        self::assertEquals('Gui', $result[1]['str1']);\n        self::assertEquals('Jon', $result[2]['str1']);\n        self::assertEquals('Rom', $result[3]['str1']);\n\n        self::assertEquals('amin E.', $result[0]['str2']);\n        self::assertEquals('herme B.', $result[1]['str2']);\n        self::assertEquals('than W.', $result[2]['str2']);\n        self::assertEquals('n B.', $result[3]['str2']);\n    }\n\n    public function testFunctionTrim(): void\n    {\n        $dql = \"SELECT m, TRIM(TRAILING '.' FROM m.name) AS str1, \" .\n               \" TRIM(LEADING '.' FROM m.name) AS str2, TRIM(CONCAT(' ', CONCAT(m.name, ' '))) AS str3 \" .\n               'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC';\n\n        $result = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals('Roman B', $result[0]['str1']);\n        self::assertEquals('Benjamin E', $result[1]['str1']);\n        self::assertEquals('Guilherme B', $result[2]['str1']);\n        self::assertEquals('Jonathan W', $result[3]['str1']);\n        self::assertEquals('Roman B.', $result[0]['str2']);\n        self::assertEquals('Benjamin E.', $result[1]['str2']);\n        self::assertEquals('Guilherme B.', $result[2]['str2']);\n        self::assertEquals('Jonathan W.', $result[3]['str2']);\n        self::assertEquals('Roman B.', $result[0]['str3']);\n        self::assertEquals('Benjamin E.', $result[1]['str3']);\n        self::assertEquals('Guilherme B.', $result[2]['str3']);\n        self::assertEquals('Jonathan W.', $result[3]['str3']);\n    }\n\n    public function testOperatorAdd(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, m.salary+2500 AS add FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                ->getResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(102500, $result[0]['add']);\n        self::assertEquals(202500, $result[1]['add']);\n        self::assertEquals(402500, $result[2]['add']);\n        self::assertEquals(802500, $result[3]['add']);\n    }\n\n    public function testOperatorSub(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, m.salary-2500 AS sub FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                ->getResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(97500, $result[0]['sub']);\n        self::assertEquals(197500, $result[1]['sub']);\n        self::assertEquals(397500, $result[2]['sub']);\n        self::assertEquals(797500, $result[3]['sub']);\n    }\n\n    public function testOperatorMultiply(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, m.salary*2 AS op FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                ->getResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(200000, $result[0]['op']);\n        self::assertEquals(400000, $result[1]['op']);\n        self::assertEquals(800000, $result[2]['op']);\n        self::assertEquals(1_600_000, $result[3]['op']);\n    }\n\n    public function testOperatorDiv(): void\n    {\n        $result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ORDER BY m.salary ASC')\n                ->getResult();\n\n        self::assertCount(4, $result);\n        self::assertEquals(200000, $result[0]['op']);\n        self::assertEquals(400000, $result[1]['op']);\n        self::assertEquals(800000, $result[2]['op']);\n        self::assertEquals(1_600_000, $result[3]['op']);\n    }\n\n    public function testConcatFunction(): void\n    {\n        $arg = $this->_em->createQuery('SELECT CONCAT(m.name, m.department) AS namedep FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m order by namedep desc')\n                ->getArrayResult();\n\n        self::assertCount(4, $arg);\n        self::assertEquals('Roman B.IT', $arg[0]['namedep']);\n        self::assertEquals('Jonathan W.Administration', $arg[1]['namedep']);\n        self::assertEquals('Guilherme B.Complaint Department', $arg[2]['namedep']);\n        self::assertEquals('Benjamin E.HR', $arg[3]['namedep']);\n    }\n\n    #[Group('DDC-1014')]\n    public function testDateDiff(): void\n    {\n        $query = $this->_em->createQuery(\"SELECT DATE_DIFF(CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), 10, 'day')) AS diff FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m\");\n        $arg   = $query->getArrayResult();\n\n        self::assertEqualsWithDelta(-10, $arg[0]['diff'], 1, 'Should be roughly -10 (or -9)');\n\n        $query = $this->_em->createQuery(\"SELECT DATE_DIFF(DATE_ADD(CURRENT_TIMESTAMP(), 10, 'day'), CURRENT_TIMESTAMP()) AS diff FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m\");\n        $arg   = $query->getArrayResult();\n\n        self::assertEqualsWithDelta(10, $arg[0]['diff'], 1, 'Should be roughly 10 (or 9)');\n    }\n\n    #[DataProvider('dateAddSubProvider')]\n    #[Group('DDC-1014')]\n    #[Group('DDC-2938')]\n    public function testDateAdd(string $unit, int $amount, int $delta = 0): void\n    {\n        $query = sprintf(\n            'SELECT CURRENT_TIMESTAMP() as now, DATE_ADD(CURRENT_TIMESTAMP(), %d, \\'%s\\') AS add FROM %s m',\n            $amount,\n            $unit,\n            CompanyManager::class,\n        );\n\n        $result = $this->_em->createQuery($query)\n                            ->setMaxResults(1)\n                            ->getSingleResult(AbstractQuery::HYDRATE_ARRAY);\n\n        self::assertArrayHasKey('now', $result);\n        self::assertArrayHasKey('add', $result);\n\n        $now       = new DateTimeImmutable($result['now']);\n        $inOneUnit = $now->modify(sprintf('+%d %s', $amount, $unit));\n        if (\n            $unit === 'month'\n            && $inOneUnit->format('m') === $now->modify('+2 month')->format('m')\n            && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform\n        ) {\n            $inOneUnit = new DateTimeImmutable('last day of next month');\n        }\n\n        self::assertEqualsWithDelta(\n            $inOneUnit,\n            new DateTimeImmutable($result['add']),\n            $delta,\n        );\n    }\n\n    #[DataProvider('dateAddSubProvider')]\n    #[Group('DDC-1014')]\n    #[Group('DDC-2938')]\n    public function testDateSub(string $unit, int $amount, int $delta = 0): void\n    {\n        $query = sprintf(\n            'SELECT CURRENT_TIMESTAMP() as now, DATE_SUB(CURRENT_TIMESTAMP(), %d, \\'%s\\') AS sub FROM %s m',\n            $amount,\n            $unit,\n            CompanyManager::class,\n        );\n\n        $result = $this->_em->createQuery($query)\n                            ->setMaxResults(1)\n                            ->getSingleResult(AbstractQuery::HYDRATE_ARRAY);\n\n        self::assertArrayHasKey('now', $result);\n        self::assertArrayHasKey('sub', $result);\n\n        $now        = new DateTimeImmutable($result['now']);\n        $oneUnitAgo = $now->modify(sprintf('-%d %s', $amount, $unit));\n        if (\n            $unit === 'month'\n            && $oneUnitAgo->format('m') === $now->format('m')\n            && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform\n        ) {\n            $oneUnitAgo = new DateTimeImmutable('last day of previous month');\n        }\n\n        self::assertEqualsWithDelta(\n            $oneUnitAgo,\n            new DateTimeImmutable($result['sub']),\n            $delta,\n        );\n    }\n\n    public static function dateAddSubProvider(): array\n    {\n        $secondsInDay = 86400;\n\n        return [\n            'year'   => ['year', 1, $secondsInDay],\n            'month'  => ['month', 1, $secondsInDay],\n            'week'   => ['week', 1, $secondsInDay],\n            'day'    => ['day', 2, $secondsInDay],\n            'hour'   => ['hour', 1, 3600],\n            'minute' => ['minute', 1, 60],\n            'second' => ['second', 10, 10],\n        ];\n    }\n\n    #[Group('DDC-1213')]\n    public function testBitOrComparison(): void\n    {\n        $dql    = 'SELECT m, ' .\n                    'BIT_OR(4, 2) AS bit_or,' .\n                    'BIT_OR( (m.salary/100000) , 2 ) AS salary_bit_or ' .\n                    'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ' .\n                'ORDER BY ' .\n                    'm.id ';\n        $result = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertEquals(4 | 2, $result[0]['bit_or']);\n        self::assertEquals(4 | 2, $result[1]['bit_or']);\n        self::assertEquals(4 | 2, $result[2]['bit_or']);\n        self::assertEquals(4 | 2, $result[3]['bit_or']);\n\n        self::assertEquals($result[0][0]['salary'] / 100000 | 2, $result[0]['salary_bit_or']);\n        self::assertEquals($result[1][0]['salary'] / 100000 | 2, $result[1]['salary_bit_or']);\n        self::assertEquals($result[2][0]['salary'] / 100000 | 2, $result[2]['salary_bit_or']);\n        self::assertEquals($result[3][0]['salary'] / 100000 | 2, $result[3]['salary_bit_or']);\n    }\n\n    #[Group('DDC-1213')]\n    public function testBitAndComparison(): void\n    {\n        $dql    = 'SELECT m, ' .\n                    'BIT_AND(4, 2) AS bit_and,' .\n                    'BIT_AND( (m.salary/100000) , 2 ) AS salary_bit_and ' .\n                    'FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m ' .\n                'ORDER BY ' .\n                    'm.id ';\n        $result = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertEquals(4 & 2, $result[0]['bit_and']);\n        self::assertEquals(4 & 2, $result[1]['bit_and']);\n        self::assertEquals(4 & 2, $result[2]['bit_and']);\n        self::assertEquals(4 & 2, $result[3]['bit_and']);\n\n        self::assertEquals($result[0][0]['salary'] / 100000 & 2, $result[0]['salary_bit_and']);\n        self::assertEquals($result[1][0]['salary'] / 100000 & 2, $result[1]['salary_bit_and']);\n        self::assertEquals($result[2][0]['salary'] / 100000 & 2, $result[2]['salary_bit_and']);\n        self::assertEquals($result[3][0]['salary'] / 100000 & 2, $result[3]['salary_bit_and']);\n    }\n\n    public function testInArithmeticExpression1(): void\n    {\n        $dql = <<<'SQL'\n            SELECT m, m.name AS m_name\n            FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m\n            WHERE m.salary IN (800000 / 8, 100000 * 2)\n            ORDER BY m.name DESC\nSQL;\n\n        $result = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertCount(2, $result);\n        self::assertEquals('Roman B.', $result[0]['m_name']);\n        self::assertEquals('Benjamin E.', $result[1]['m_name']);\n    }\n\n    public function testInArithmeticExpression2(): void\n    {\n        $this->_em->getConfiguration()->addCustomStringFunction('FOO', static function ($funcName) {\n            return new NoOp($funcName); // See Doctrine/Tests/ORM/Functional/CustomFunctionsTest\n        });\n\n        $dql = <<<'SQL'\n            SELECT m, m.name AS m_name\n            FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m\n            WHERE m.department IN (FOO('Administration'))\nSQL;\n\n        $result = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertCount(1, $result);\n        self::assertEquals('Jonathan W.', $result[0]['m_name']);\n    }\n\n    protected function generateFixture(): void\n    {\n        $manager1 = new CompanyManager();\n        $manager1->setName('Roman B.');\n        $manager1->setTitle('Foo');\n        $manager1->setDepartment('IT');\n        $manager1->setSalary(100000);\n\n        $manager2 = new CompanyManager();\n        $manager2->setName('Benjamin E.');\n        $manager2->setTitle('Foo');\n        $manager2->setDepartment('HR');\n        $manager2->setSalary(200000);\n\n        $manager3 = new CompanyManager();\n        $manager3->setName('Guilherme B.');\n        $manager3->setTitle('Foo');\n        $manager3->setDepartment('Complaint Department');\n        $manager3->setSalary(400000);\n\n        $manager4 = new CompanyManager();\n        $manager4->setName('Jonathan W.');\n        $manager4->setTitle('Foo');\n        $manager4->setDepartment('Administration');\n        $manager4->setSalary(800000);\n\n        $this->_em->persist($manager1);\n        $this->_em->persist($manager2);\n        $this->_em->persist($manager3);\n        $this->_em->persist($manager4);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    #[Group('GH-11240')]\n    public function testDateAddWithColumnInterval(): void\n    {\n        $query = sprintf(\n            'SELECT DATE_ADD(CURRENT_TIMESTAMP(), m.salary, \\'day\\') AS add FROM %s m',\n            CompanyEmployee::class,\n        );\n\n        $result = $this->_em->createQuery($query)\n            ->setMaxResults(1)\n            ->getSingleResult(AbstractQuery::HYDRATE_ARRAY);\n\n        self::assertArrayHasKey('add', $result);\n    }\n\n    #[Group('GH-11240')]\n    public function testDateSubWithColumnInterval(): void\n    {\n        $query = sprintf(\n            'SELECT DATE_SUB(CURRENT_TIMESTAMP(), m.salary, \\'day\\') AS add FROM %s m',\n            CompanyEmployee::class,\n        );\n\n        $result = $this->_em->createQuery($query)\n            ->setMaxResults(1)\n            ->getSingleResult(AbstractQuery::HYDRATE_ARRAY);\n\n        self::assertArrayHasKey('add', $result);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryIterableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class QueryIterableTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testAlias(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('SELECT u AS user FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n\n        self::assertEquals('gblanco', $users[0]['user']->username);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testAliasInnerJoin(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $address->user = $user;\n        $user->address = $address;\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('SELECT u AS user, a AS address FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.address a');\n\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n\n        self::assertEquals('gblanco', $users[0]['user']->username);\n\n        $this->_em->clear();\n\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testIndexByQueryWithOneResult(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Antonio J.';\n        $user->username = 'ajgarlag';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u INDEX BY u.username');\n        IterableTester::assertResultsAreTheSame($query);\n    }\n\n    public function testIndexByQueryWithMultipleResults(): void\n    {\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n\n        $article3        = new CmsArticle();\n        $article3->topic = 'Laminas';\n        $article3->text  = 'This is an introduction to Laminas.';\n\n        $article4        = new CmsArticle();\n        $article4->topic = 'CodeIgniter';\n        $article4->text  = 'This is an introduction to CodeIgniter.';\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->persist($article3);\n        $this->_em->persist($article4);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a INDEX BY a.topic');\n        IterableTester::assertResultsAreTheSame($query);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryParameterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-11278')]\nfinal class QueryParameterTest extends OrmFunctionalTestCase\n{\n    private int $userId;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $user            = new CmsUser();\n        $user->name      = 'John Doe';\n        $user->username  = 'john';\n        $user2           = new CmsUser();\n        $user2->name     = 'Jane Doe';\n        $user2->username = 'jane';\n        $user3           = new CmsUser();\n        $user3->name     = 'Just Bill';\n        $user3->username = 'bill';\n\n        $this->_em->persist($user);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n        $this->_em->flush();\n\n        $this->userId = $user->id;\n\n        $this->_em->clear();\n    }\n\n    public function testParameterTypeInBuilder(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.id = :id')\n            ->setParameter('id', $this->userId, ParameterType::INTEGER)\n            ->getQuery()\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'John Doe']], $result);\n    }\n\n    public function testParameterTypeInQuery(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.id = :id')\n            ->getQuery()\n            ->setParameter('id', $this->userId, ParameterType::INTEGER)\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'John Doe']], $result);\n    }\n\n    public function testDbalTypeStringInBuilder(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.id = :id')\n            ->setParameter('id', $this->userId, Types::INTEGER)\n            ->getQuery()\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'John Doe']], $result);\n    }\n\n    public function testDbalTypeStringInQuery(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.id = :id')\n            ->getQuery()\n            ->setParameter('id', $this->userId, Types::INTEGER)\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'John Doe']], $result);\n    }\n\n    public function testArrayParameterTypeInBuilder(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.username IN (:usernames)')\n            ->orderBy('u.username')\n            ->setParameter('usernames', ['john', 'jane'], ArrayParameterType::STRING)\n            ->getQuery()\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'Jane Doe'], ['name' => 'John Doe']], $result);\n    }\n\n    public function testArrayParameterTypeInQuery(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.name')\n            ->where('u.username IN (:usernames)')\n            ->orderBy('u.username')\n            ->getQuery()\n            ->setParameter('usernames', ['john', 'jane'], ArrayParameterType::STRING)\n            ->getArrayResult();\n\n        self::assertSame([['name' => 'Jane Doe'], ['name' => 'John Doe']], $result);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/QueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\NonUniqueResultException;\nuse Doctrine\\ORM\\NoResultException;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\UnexpectedResultException;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Enums\\AccessLevel;\nuse Doctrine\\Tests\\Models\\Enums\\UserStatus;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\nuse function iterator_to_array;\n\n/**\n * Functional Query tests.\n */\nclass QueryTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testSimpleQueries(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery(\"select u, upper(u.name) from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = 'gblanco'\");\n\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CmsUser::class, $result[0][0]);\n        self::assertEquals('Guilherme', $result[0][0]->name);\n        self::assertEquals('gblanco', $result[0][0]->username);\n        self::assertEquals('developer', $result[0][0]->status);\n        self::assertEquals('GUILHERME', $result[0][1]);\n\n        $resultArray = $query->getArrayResult();\n        self::assertCount(1, $resultArray);\n        self::assertIsArray($resultArray[0][0]);\n        self::assertEquals('Guilherme', $resultArray[0][0]['name']);\n        self::assertEquals('gblanco', $resultArray[0][0]['username']);\n        self::assertEquals('developer', $resultArray[0][0]['status']);\n        self::assertEquals('GUILHERME', $resultArray[0][1]);\n\n        $scalarResult = $query->getScalarResult();\n        self::assertCount(1, $scalarResult);\n        self::assertEquals('Guilherme', $scalarResult[0]['u_name']);\n        self::assertEquals('gblanco', $scalarResult[0]['u_username']);\n        self::assertEquals('developer', $scalarResult[0]['u_status']);\n        self::assertEquals('GUILHERME', $scalarResult[0][1]);\n\n        $query = $this->_em->createQuery(\"select upper(u.name) from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = 'gblanco'\");\n        self::assertEquals('GUILHERME', $query->getSingleScalarResult());\n    }\n\n    public function testJoinQueries(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $user->addArticle($article1);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $user->addArticle($article2);\n\n        $this->_em->persist($user);\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select u, a from ' . CmsUser::class . ' u join u.articles a ORDER BY a.topic');\n        $users = $query->getResult();\n        self::assertCount(1, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertCount(2, $users[0]->articles);\n        self::assertEquals('Doctrine 2', $users[0]->articles[0]->topic);\n        self::assertEquals('Symfony 2', $users[0]->articles[1]->topic);\n    }\n\n    public function testJoinPartialArrayHydration(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $user->addArticle($article1);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $user->addArticle($article2);\n\n        $this->_em->persist($user);\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select partial u.{id, username}, partial a.{id, topic} from ' . CmsUser::class . ' u join u.articles a ORDER BY a.topic');\n        $users = $query->getArrayResult();\n\n        $this->assertEquals([\n            [\n                'id' => $user->id,\n                'username' => 'gblanco',\n                'articles' =>\n                [\n                    [\n                        'id' => $article1->id,\n                        'topic' => 'Doctrine 2',\n                    ],\n                    [\n                        'id' => $article2->id,\n                        'topic' => 'Symfony 2',\n                    ],\n                ],\n            ],\n        ], $users);\n    }\n\n    public function testUsingZeroBasedQueryParameterShouldWork(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Jonathan';\n        $user->username = 'jwage';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.username = ?0');\n        $q->setParameter(0, 'jwage');\n        $user = $q->getSingleResult();\n\n        self::assertNotNull($user);\n    }\n\n    public function testUsingUnknownQueryParameterShouldThrowException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Invalid parameter: token 2 is not defined in the query.');\n\n        $q = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?1');\n        $q->setParameter(2, 'jwage');\n        $user = $q->getSingleResult();\n    }\n\n    public function testTooManyParametersShouldThrowException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Too many parameters: the query defines 1 parameters and you bound 2');\n\n        $q = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?1');\n        $q->setParameter(1, 'jwage');\n        $q->setParameter(2, 'jwage');\n\n        $user = $q->getSingleResult();\n    }\n\n    public function testTooFewParametersShouldThrowException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Too few parameters: the query defines 1 parameters but you only bound 0');\n\n        $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?1')\n                  ->getSingleResult();\n    }\n\n    public function testInvalidInputParameterThrowsException(): void\n    {\n        $this->expectException(QueryException::class);\n\n        $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?')\n                  ->setParameter(1, 'jwage')\n                  ->getSingleResult();\n    }\n\n    public function testUseStringEnumCaseAsParameter(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'John';\n        $user->username = 'john';\n        $user->status   = 'inactive';\n        $this->_em->persist($user);\n\n        $user           = new CmsUser();\n        $user->name     = 'Jane';\n        $user->username = 'jane';\n        $user->status   = 'active';\n        $this->_em->persist($user);\n\n        unset($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.status = :status')\n            ->setParameter('status', UserStatus::Active)\n            ->getResult();\n\n        self::assertCount(1, $result);\n        self::assertSame('jane', $result[0]->username);\n    }\n\n    public function testUseIntegerEnumCaseAsParameter(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'John';\n        $user->username = 'john';\n        $user->status   = '1';\n        $this->_em->persist($user);\n\n        $user           = new CmsUser();\n        $user->name     = 'Jane';\n        $user->username = 'jane';\n        $user->status   = '2';\n        $this->_em->persist($user);\n\n        unset($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.status = :status')\n            ->setParameter('status', AccessLevel::User)\n            ->getResult();\n\n        self::assertCount(1, $result);\n        self::assertSame('jane', $result[0]->username);\n    }\n\n    public function testSetParameters(): void\n    {\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter(1, 'jwage'));\n        $parameters->add(new Parameter(2, 'active'));\n\n        $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?1 AND u.status = ?2')\n                  ->setParameters($parameters)\n                  ->getResult();\n\n        self::assertSame(\n            [1 => 'jwage', 2 => 'active'],\n            $this->getLastLoggedQuery()['params'],\n        );\n    }\n\n    public function testSetParametersBackwardsCompatible(): void\n    {\n        $parameters = [1 => 'jwage', 2 => 'active'];\n\n        $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.name = ?1 AND u.status = ?2')\n                  ->setParameters($parameters)\n                  ->getResult();\n\n        self::assertSame($parameters, $this->getLastLoggedQuery()['params']);\n    }\n\n    #[Group('DDC-1070')]\n    public function testIterateResultAsArrayAndParams(): void\n    {\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n        $articleId = $article1->id;\n\n        $query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a WHERE a.topic = ?1');\n\n        $expectedArticle = [\n            'id'      => $articleId,\n            'topic'   => 'Doctrine 2',\n            'text'    => 'This is an introduction to Doctrine 2.',\n            'version' => 1,\n        ];\n\n        $articles = $query->toIterable(\n            new ArrayCollection([new Parameter(1, 'Doctrine 2')]),\n            Query::HYDRATE_ARRAY,\n        );\n\n        $articles = IterableTester::iterableToArray($articles);\n\n        self::assertCount(1, $articles);\n        self::assertEquals([$expectedArticle], $articles);\n    }\n\n    public function testIterateResultIterativelyBuildUpUnitOfWork(): void\n    {\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query    = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a');\n        $articles = $query->toIterable();\n\n        $iteratedCount = 0;\n        $topics        = [];\n\n        foreach ($articles as $article) {\n            $topics[] = $article->topic;\n\n            $identityMap      = $this->_em->getUnitOfWork()->getIdentityMap();\n            $identityMapCount = count($identityMap[CmsArticle::class]);\n            self::assertGreaterThan($iteratedCount, $identityMapCount);\n\n            $iteratedCount++;\n        }\n\n        self::assertSame(['Doctrine 2', 'Symfony 2'], $topics);\n        self::assertSame(2, $iteratedCount);\n    }\n\n    public function testToIterableWithMultipleSelectElements(): void\n    {\n        $author           = new CmsUser();\n        $author->name     = 'Ben';\n        $author->username = 'beberlei';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $article1->setAuthor($author);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $article2->setAuthor($author);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->persist($author);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select a, u from ' . CmsArticle::class . ' a JOIN ' . CmsUser::class . ' u ON a.user = u');\n\n        $result = iterator_to_array($query->toIterable());\n\n        self::assertCount(2, $result);\n\n        foreach ($result as $row) {\n            self::assertCount(2, $row);\n            self::assertInstanceOf(CmsArticle::class, $row[0]);\n            self::assertInstanceOf(CmsUser::class, $row[1]);\n        }\n    }\n\n    public function testToIterableWithMixedResultEntityScalars(): void\n    {\n        $author           = new CmsUser();\n        $author->name     = 'Ben';\n        $author->username = 'beberlei';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $article1->setAuthor($author);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $article2->setAuthor($author);\n\n        $article3        = new CmsArticle();\n        $article3->topic = 'lala 2';\n        $article3->text  = 'This is an introduction to Symfony 2.';\n        $article3->setAuthor($author);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->persist($article3);\n        $this->_em->persist($author);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery(\n            'select a, a.topic, a.text from ' . CmsArticle::class . ' a order by a.id asc',\n        );\n        $result = $query->toIterable();\n\n        $it = iterator_to_array($result);\n        $this->assertCount(3, $it);\n        $this->assertEquals('Doctrine 2', $it[0]['topic']);\n        $this->assertEquals('Doctrine 2', $it[0][0]->topic);\n        $this->assertEquals('lala 2', $it[2]['topic']);\n        $this->assertEquals('lala 2', $it[2][0]->topic);\n    }\n\n    public function testToIterableWithMixedResultArbitraryJoinsScalars(): void\n    {\n        $author           = new CmsUser();\n        $author->name     = 'Ben';\n        $author->username = 'beberlei';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $article1->setAuthor($author);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $article2->setAuthor($author);\n\n        $article3        = new CmsArticle();\n        $article3->topic = 'lala 2';\n        $article3->text  = 'This is an introduction to Symfony 2.';\n        $article3->setAuthor($author);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->persist($article3);\n        $this->_em->persist($author);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery(\n            'select a, u, a.topic, a.text from ' . CmsArticle::class . ' a, ' . CmsUser::class . ' u WHERE a.user = u order by a.id asc',\n        );\n        $result = $query->toIterable();\n\n        $it = iterator_to_array($result);\n\n        $this->assertCount(3, $it);\n        $this->assertEquals('Doctrine 2', $it[0]['topic']);\n        $this->assertEquals('Doctrine 2', $it[0][0]->topic);\n        $this->assertEquals('beberlei', $it[0][1]->username);\n        $this->assertEquals('lala 2', $it[2]['topic']);\n        $this->assertEquals('lala 2', $it[2][0]->topic);\n        $this->assertEquals('beberlei', $it[2][1]->username);\n    }\n\n    public function testToIterableWithMixedResultScalarsOnly(): void\n    {\n        $author           = new CmsUser();\n        $author->name     = 'Ben';\n        $author->username = 'beberlei';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $article1->setAuthor($author);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $article2->setAuthor($author);\n\n        $article3        = new CmsArticle();\n        $article3->topic = 'lala 2';\n        $article3->text  = 'This is an introduction to Symfony 2.';\n        $article3->setAuthor($author);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->persist($article3);\n        $this->_em->persist($author);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery(\n            'select a.topic, a.text from ' . CmsArticle::class . ' a order by a.id asc',\n        );\n        $result = $query->toIterable();\n\n        $it = iterator_to_array($result);\n\n        $this->assertEquals([\n            ['topic' => 'Doctrine 2', 'text' => 'This is an introduction to Doctrine 2.'],\n            ['topic' => 'Symfony 2', 'text' => 'This is an introduction to Symfony 2.'],\n            ['topic' => 'lala 2', 'text' => 'This is an introduction to Symfony 2.'],\n        ], $it);\n    }\n\n    public function testIterateResultClearEveryCycle(): void\n    {\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery(\n            'select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a order by a.id asc',\n        );\n\n        $articles      = $query->toIterable();\n        $iteratedCount = 0;\n        $topics        = [];\n        foreach ($articles as $article) {\n            $topics[] = $article->topic;\n\n            $this->_em->clear();\n\n            $iteratedCount++;\n        }\n\n        self::assertSame(['Doctrine 2', 'Symfony 2'], $topics);\n        self::assertSame(2, $iteratedCount);\n\n        $this->_em->flush();\n    }\n\n    public function testToIterableResultFetchJoinedCollectionThrowsException(): void\n    {\n        $this->expectException(QueryException::class);\n\n        $query = $this->_em->createQuery(\"SELECT u, a FROM ' . CmsUser::class . ' u JOIN u.articles a\");\n        $query->toIterable();\n    }\n\n    public function testGetSingleResultThrowsExceptionOnNoResult(): void\n    {\n        $this->expectException(NoResultException::class);\n        $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a')\n             ->getSingleResult();\n    }\n\n    public function testGetSingleScalarResultThrowsExceptionOnNoResult(): void\n    {\n        $this->expectException(NoResultException::class);\n        $this->_em->createQuery('select a.id from Doctrine\\Tests\\Models\\CMS\\CmsArticle a')\n             ->getSingleScalarResult();\n    }\n\n    public function testGetSingleScalarResultThrowsExceptionOnSingleRowWithMultipleColumns(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Javier';\n        $user->username = 'phansys';\n        $user->status   = 'developer';\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->expectException(NonUniqueResultException::class);\n        $this->expectExceptionMessage(\n            'The query returned a row containing multiple columns. Change the query or use a different result function'\n            . ' like getScalarResult().',\n        );\n\n        $this->_em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->setMaxResults(1)\n            ->getSingleScalarResult();\n    }\n\n    public function testGetSingleScalarResultThrowsExceptionOnNonUniqueResult(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Doctrine 2';\n        $article1->text  = 'This is an introduction to Doctrine 2.';\n        $user->addArticle($article1);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Symfony 2';\n        $article2->text  = 'This is an introduction to Symfony 2.';\n        $user->addArticle($article2);\n\n        $this->_em->persist($user);\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->expectException(NonUniqueResultException::class);\n        $this->expectExceptionMessage(\n            'The query returned multiple rows. Change the query or use a different result function like getScalarResult().',\n        );\n\n        $this->_em->createQuery('select a.id from Doctrine\\Tests\\Models\\CMS\\CmsArticle a')\n             ->getSingleScalarResult();\n    }\n\n    public function testModifiedLimitQuery(): void\n    {\n        for ($i = 0; $i < 5; $i++) {\n            $user           = new CmsUser();\n            $user->name     = 'Guilherme' . $i;\n            $user->username = 'gblanco' . $i;\n            $user->status   = 'developer';\n            $this->_em->persist($user);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = 'SELECT u FROM ' . CmsUser::class . ' u ORDER BY u.username';\n\n        $data = $this->_em->createQuery($query)\n                  ->setFirstResult(1)\n                  ->setMaxResults(2)\n                  ->getResult();\n\n        self::assertCount(2, $data);\n        self::assertEquals('gblanco1', $data[0]->username);\n        self::assertEquals('gblanco2', $data[1]->username);\n\n        $data = $this->_em->createQuery($query)\n                  ->setFirstResult(3)\n                  ->setMaxResults(2)\n                  ->getResult();\n\n        self::assertCount(2, $data);\n        self::assertEquals('gblanco3', $data[0]->username);\n        self::assertEquals('gblanco4', $data[1]->username);\n\n        $data = $this->_em->createQuery($query)\n                  ->setFirstResult(3)\n                  ->setMaxResults(2)\n                  ->getScalarResult();\n    }\n\n    #[Group('DDC-604')]\n    public function testEntityParameters(): void\n    {\n        $article          = new CmsArticle();\n        $article->topic   = 'dr. dolittle';\n        $article->text    = 'Once upon a time ...';\n        $author           = new CmsUser();\n        $author->name     = 'anonymous';\n        $author->username = 'anon';\n        $author->status   = 'here';\n        $article->user    = $author;\n        $this->_em->persist($author);\n        $this->_em->persist($article);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a where a.topic = :topic and a.user = :user')\n                ->setParameter('user', $this->_em->getReference(CmsUser::class, $author->id))\n                ->setParameter('topic', 'dr. dolittle');\n\n        $result = $q->getResult();\n        self::assertEquals(1, count($result));\n        self::assertInstanceOf(CmsArticle::class, $result[0]);\n        self::assertEquals('dr. dolittle', $result[0]->topic);\n        self::assertTrue($this->isUninitializedObject($result[0]->user));\n    }\n\n    #[Group('DDC-952')]\n    public function testEnableFetchEagerMode(): void\n    {\n        for ($i = 0; $i < 10; $i++) {\n            $article          = new CmsArticle();\n            $article->topic   = 'dr. dolittle';\n            $article->text    = 'Once upon a time ...';\n            $author           = new CmsUser();\n            $author->name     = 'anonymous';\n            $author->username = 'anon' . $i;\n            $author->status   = 'here';\n            $article->user    = $author;\n            $this->_em->persist($author);\n            $this->_em->persist($article);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $articles = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a')\n                         ->setFetchMode(CmsArticle::class, 'user', ClassMetadata::FETCH_EAGER)\n                         ->getResult();\n\n        self::assertCount(10, $articles);\n        foreach ($articles as $article) {\n            self::assertFalse($this->isUninitializedObject($article));\n        }\n    }\n\n    #[Group('DDC-991')]\n    public function testgetOneOrNullResult(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select u from ' . CmsUser::class . \" u where u.username = 'gblanco'\");\n\n        $fetchedUser = $query->getOneOrNullResult();\n        self::assertInstanceOf(CmsUser::class, $fetchedUser);\n        self::assertEquals('gblanco', $fetchedUser->username);\n\n        $query           = $this->_em->createQuery('select u.username from ' . CmsUser::class . \" u where u.username = 'gblanco'\");\n        $fetchedUsername = $query->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);\n        self::assertEquals('gblanco', $fetchedUsername);\n    }\n\n    #[Group('DDC-991')]\n    public function testgetOneOrNullResultSeveralRows(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Guilherme';\n        $user->username = 'gblanco';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $this->expectException(NonUniqueResultException::class);\n\n        $fetchedUser = $query->getOneOrNullResult();\n    }\n\n    #[Group('DDC-991')]\n    public function testgetOneOrNullResultNoRows(): void\n    {\n        $query = $this->_em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n        self::assertNull($query->getOneOrNullResult());\n\n        $query = $this->_em->createQuery(\"select u.username from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = 'gblanco'\");\n        self::assertNull($query->getOneOrNullResult(Query::HYDRATE_SCALAR));\n    }\n\n    #[Group('DBAL-171')]\n    public function testParameterOrder(): void\n    {\n        $user1           = new CmsUser();\n        $user1->name     = 'Benjamin';\n        $user1->username = 'beberlei';\n        $user1->status   = 'developer';\n        $this->_em->persist($user1);\n\n        $user2           = new CmsUser();\n        $user2->name     = 'Roman';\n        $user2->username = 'romanb';\n        $user2->status   = 'developer';\n        $this->_em->persist($user2);\n\n        $user3           = new CmsUser();\n        $user3->name     = 'Jonathan';\n        $user3->username = 'jwage';\n        $user3->status   = 'developer';\n        $this->_em->persist($user3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.status = :a AND u.id IN (:b)');\n        $query->setParameters(new ArrayCollection(\n            [\n                new Parameter('b', [$user1->id, $user2->id, $user3->id]),\n                new Parameter('a', 'developer'),\n            ],\n        ));\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n    }\n\n    public function testDqlWithAutoInferOfParameters(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Benjamin';\n        $user->username = 'beberlei';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n\n        $user           = new CmsUser();\n        $user->name     = 'Jonathan';\n        $user->username = 'jwage';\n        $user->status   = 'developer';\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username IN (?0)');\n        $query->setParameter(0, ['beberlei', 'jwage']);\n\n        $users = $query->execute();\n\n        self::assertCount(2, $users);\n    }\n\n    public function testQueryBuilderWithStringWhereClauseContainingOrAndConditionalPrimary(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->innerJoin('u.articles', 'a')\n           ->where('(u.id = 0) OR (u.id IS NULL)');\n\n        $query = $qb->getQuery();\n        $users = $query->execute();\n\n        self::assertCount(0, $users);\n    }\n\n    public function testQueryWithArrayOfEntitiesAsParameter(): void\n    {\n        $userA           = new CmsUser();\n        $userA->name     = 'Benjamin';\n        $userA->username = 'beberlei';\n        $userA->status   = 'developer';\n        $this->_em->persist($userA);\n\n        $userB           = new CmsUser();\n        $userB->name     = 'Roman';\n        $userB->username = 'romanb';\n        $userB->status   = 'developer';\n        $this->_em->persist($userB);\n\n        $userC           = new CmsUser();\n        $userC->name     = 'Jonathan';\n        $userC->username = 'jwage';\n        $userC->status   = 'developer';\n        $this->_em->persist($userC);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u IN (?0) OR u.username = ?1');\n        $query->setParameter(0, [$userA, $userC]);\n        $query->setParameter(1, 'beberlei');\n\n        $users = $query->execute();\n\n        self::assertCount(2, $users);\n    }\n\n    public function testQueryWithHiddenAsSelectExpression(): void\n    {\n        $userA           = new CmsUser();\n        $userA->name     = 'Benjamin';\n        $userA->username = 'beberlei';\n        $userA->status   = 'developer';\n        $this->_em->persist($userA);\n\n        $userB           = new CmsUser();\n        $userB->name     = 'Roman';\n        $userB->username = 'romanb';\n        $userB->status   = 'developer';\n        $this->_em->persist($userB);\n\n        $userC           = new CmsUser();\n        $userC->name     = 'Jonathan';\n        $userC->username = 'jwage';\n        $userC->status   = 'developer';\n        $this->_em->persist($userC);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT u, (SELECT COUNT(u2.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) AS HIDDEN total FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n        $users = $query->execute();\n\n        self::assertCount(3, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n    }\n\n    #[Group('DDC-1651')]\n    public function testSetParameterBindingSingleIdentifierObject(): void\n    {\n        $userC           = new CmsUser();\n        $userC->name     = 'Jonathan';\n        $userC->username = 'jwage';\n        $userC->status   = 'developer';\n        $this->_em->persist($userC);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('SELECT DISTINCT u from Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1');\n        $q->setParameter(1, $userC);\n\n        self::assertEquals($userC, $q->getParameter(1)->getValue());\n\n        // Parameter is not converted before, but it should be converted during execution. Test should not fail here\n        $q->getResult();\n    }\n\n    #[Group('DDC-2319')]\n    public function testSetCollectionParameterBindingSingleIdentifierObject(): void\n    {\n        $u1           = new CmsUser();\n        $u1->name     = 'Name1';\n        $u1->username = 'username1';\n        $u1->status   = 'developer';\n        $this->_em->persist($u1);\n\n        $u2           = new CmsUser();\n        $u2->name     = 'Name2';\n        $u2->username = 'username2';\n        $u2->status   = 'tester';\n        $this->_em->persist($u2);\n\n        $u3           = new CmsUser();\n        $u3->name     = 'Name3';\n        $u3->username = 'username3';\n        $u3->status   = 'tester';\n        $this->_em->persist($u3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userCollection = new ArrayCollection();\n\n        $userCollection->add($u1);\n        $userCollection->add($u2);\n        $userCollection->add($u3->getId());\n\n        $q = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u IN (:users) ORDER BY u.id');\n        $q->setParameter('users', $userCollection);\n        $users = $q->execute();\n\n        self::assertCount(3, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertInstanceOf(CmsUser::class, $users[1]);\n        self::assertInstanceOf(CmsUser::class, $users[2]);\n\n        $resultUser1 = $users[0];\n        $resultUser2 = $users[1];\n        $resultUser3 = $users[2];\n\n        self::assertEquals($u1->username, $resultUser1->username);\n        self::assertEquals($u2->username, $resultUser2->username);\n        self::assertEquals($u3->username, $resultUser3->username);\n    }\n\n    #[Group('DDC-1822')]\n    public function testUnexpectedResultException(): void\n    {\n        $dql          = 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u';\n        $u1           = new CmsUser();\n        $u2           = new CmsUser();\n        $u1->name     = 'Fabio B. Silva';\n        $u1->username = 'FabioBatSilva';\n        $u1->status   = 'developer';\n        $u2->name     = 'Test';\n        $u2->username = 'test';\n        $u2->status   = 'tester';\n\n        try {\n            $this->_em->createQuery($dql)->getSingleResult();\n            self::fail('Expected exception \"\\Doctrine\\ORM\\NoResultException\".');\n        } catch (UnexpectedResultException $exc) {\n            self::assertInstanceOf(NoResultException::class, $exc);\n        }\n\n        $this->_em->persist($u1);\n        $this->_em->persist($u2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        try {\n            $this->_em->createQuery($dql)->getSingleResult();\n            self::fail('Expected exception \"\\Doctrine\\ORM\\NonUniqueResultException\".');\n        } catch (UnexpectedResultException $exc) {\n            self::assertInstanceOf(NonUniqueResultException::class, $exc);\n        }\n    }\n\n    public function testMultipleJoinComponentsUsingInnerJoin(): void\n    {\n        $userA           = new CmsUser();\n        $userA->name     = 'Benjamin';\n        $userA->username = 'beberlei';\n        $userA->status   = 'developer';\n\n        $phonenumberA              = new CmsPhonenumber();\n        $phonenumberA->phonenumber = '111111';\n        $userA->addPhonenumber($phonenumberA);\n\n        $userB           = new CmsUser();\n        $userB->name     = 'Alexander';\n        $userB->username = 'asm89';\n        $userB->status   = 'developer';\n\n        $this->_em->persist($userA);\n        $this->_em->persist($userB);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('\n            SELECT u, p\n              FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n             INNER JOIN Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p ON u = p.user\n        ');\n        $users = $query->execute();\n\n        self::assertCount(2, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertInstanceOf(CmsPhonenumber::class, $users[1]);\n    }\n\n    public function testMultipleJoinComponentsUsingLeftJoin(): void\n    {\n        $userA           = new CmsUser();\n        $userA->name     = 'Benjamin';\n        $userA->username = 'beberlei';\n        $userA->status   = 'developer';\n\n        $phonenumberA              = new CmsPhonenumber();\n        $phonenumberA->phonenumber = '111111';\n        $userA->addPhonenumber($phonenumberA);\n\n        $userB           = new CmsUser();\n        $userB->name     = 'Alexander';\n        $userB->username = 'asm89';\n        $userB->status   = 'developer';\n\n        $this->_em->persist($userA);\n        $this->_em->persist($userB);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('\n            SELECT u, p\n              FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n              LEFT JOIN Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p ON u = p.user\n        ');\n        $users = $query->execute();\n\n        self::assertCount(4, $users);\n        self::assertInstanceOf(CmsUser::class, $users[0]);\n        self::assertInstanceOf(CmsPhonenumber::class, $users[1]);\n        self::assertInstanceOf(CmsUser::class, $users[2]);\n        self::assertNull($users[3]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ReadOnlyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Functional Query tests.\n */\n#[Group('DDC-692')]\nclass ReadOnlyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(ReadOnlyEntity::class);\n    }\n\n    public function testReadOnlyEntityNeverChangeTracked(): void\n    {\n        $readOnly = new ReadOnlyEntity('Test1', 1234);\n        $this->_em->persist($readOnly);\n        $this->_em->flush();\n\n        $readOnly->name         = 'Test2';\n        $readOnly->numericValue = 4321;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dbReadOnly = $this->_em->find(ReadOnlyEntity::class, $readOnly->id);\n        self::assertEquals('Test1', $dbReadOnly->name);\n        self::assertEquals(1234, $dbReadOnly->numericValue);\n    }\n\n    #[Group('DDC-1659')]\n    public function testClearReadOnly(): void\n    {\n        $readOnly = new ReadOnlyEntity('Test1', 1234);\n        $this->_em->persist($readOnly);\n        $this->_em->flush();\n        $this->_em->getUnitOfWork()->markReadOnly($readOnly);\n\n        $this->_em->clear();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));\n    }\n\n    #[Group('DDC-1659')]\n    public function testClearEntitiesReadOnly(): void\n    {\n        $readOnly = new ReadOnlyEntity('Test1', 1234);\n        $this->_em->persist($readOnly);\n        $this->_em->flush();\n        $this->_em->getUnitOfWork()->markReadOnly($readOnly);\n\n        $this->_em->clear();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));\n    }\n\n    public function testReadOnlyQueryHint(): void\n    {\n        $user = new ReadOnlyEntity('beberlei', 1234);\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql = 'SELECT u FROM ' . ReadOnlyEntity::class . ' u WHERE u.id = ?1';\n\n        $query = $this->_em->createQuery($dql);\n        $query->setParameter(1, $user->id);\n        $query->setHint(Query::HINT_READ_ONLY, true);\n\n        $user = $query->getSingleResult();\n\n        self::assertTrue($this->_em->getUnitOfWork()->isReadOnly($user));\n    }\n\n    public function testNotReadOnlyQueryHint(): void\n    {\n        $user = new ReadOnlyEntity('beberlei', 1234);\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT u FROM ' . ReadOnlyEntity::class . ' u WHERE u.id = ?1');\n        $query->setParameter(1, $user->id);\n        $query->setHint(Query::HINT_READ_ONLY, false);\n\n        $user = $query->getSingleResult();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($user));\n    }\n\n    public function testNotReadOnlyIfObjectWasProxyBefore(): void\n    {\n        $user = new ReadOnlyEntity('beberlei', 1234);\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(ReadOnlyEntity::class, $user->id);\n\n        $dql = 'SELECT u FROM ' . ReadOnlyEntity::class . ' u WHERE u.id = ?1';\n\n        $query = $this->_em->createQuery($dql);\n        $query->setParameter(1, $user->id);\n        $query->setHint(Query::HINT_READ_ONLY, true);\n\n        $user = $query->getSingleResult();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($user));\n    }\n\n    public function testNotReadOnlyIfObjectWasKnownBefore(): void\n    {\n        $user = new ReadOnlyEntity('beberlei', 1234);\n\n        $this->_em->persist($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userIntoIdentityMap = $this->_em->find(ReadOnlyEntity::class, $user->id);\n\n        $dql = 'SELECT u FROM ' . ReadOnlyEntity::class . ' u WHERE u.id = ?1';\n\n        $query = $this->_em->createQuery($dql);\n        $query->setParameter(1, $user->id);\n        $query->setHint(Query::HINT_READ_ONLY, true);\n\n        $user = $query->getSingleResult();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($user));\n    }\n}\n\n#[Entity(readOnly: true)]\nclass ReadOnlyEntity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n        #[Column(type: 'integer')]\n        public int $numericValue,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Models\\ReadonlyProperties\\Author;\nuse Doctrine\\Tests\\Models\\ReadonlyProperties\\Book;\nuse Doctrine\\Tests\\Models\\ReadonlyProperties\\SimpleBook;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Doctrine\\Tests\\TestUtil;\n\nclass ReadonlyPropertiesTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        if (! isset(static::$sharedConn)) {\n            static::$sharedConn = TestUtil::getConnection();\n        }\n\n        $attributeDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../Models/ReadonlyProperties']);\n\n        $this->_em         = $this->getEntityManager(null, $attributeDriver);\n        $this->_schemaTool = new SchemaTool($this->_em);\n\n        parent::setUp();\n\n        $this->setUpEntitySchema([Author::class, Book::class, SimpleBook::class]);\n    }\n\n    public function testSimpleEntity(): void\n    {\n        $connection = $this->_em->getConnection();\n\n        $connection->insert('author', ['name' => 'Jane Austen']);\n        $authorId = $connection->lastInsertId();\n\n        $author = $this->_em->find(Author::class, $authorId);\n\n        self::assertSame('Jane Austen', $author->getName());\n        self::assertEquals($authorId, $author->getId());\n    }\n\n    public function testEntityWithLazyManyToOne(): void\n    {\n        $connection = $this->_em->getConnection();\n\n        $connection->insert('author', ['name' => 'Jane Austen']);\n        $authorId = $connection->lastInsertId();\n\n        $connection->insert('simple_book', ['title' => 'Pride and Prejudice', 'author_id' => $authorId]);\n        $bookId = $connection->lastInsertId();\n\n        $book = $this->_em->find(SimpleBook::class, $bookId);\n\n        self::assertSame('Pride and Prejudice', $book->getTitle());\n        self::assertEquals($bookId, $book->getId());\n        self::assertSame('Jane Austen', $book->getAuthor()->getName());\n    }\n\n    public function testEntityWithEagerManyToOne(): void\n    {\n        $connection = $this->_em->getConnection();\n\n        $connection->insert('author', ['name' => 'Jane Austen']);\n        $authorId = $connection->lastInsertId();\n\n        $connection->insert('simple_book', ['title' => 'Pride and Prejudice', 'author_id' => $authorId]);\n        $bookId = $connection->lastInsertId();\n\n        [$book] = $this->_em->createQueryBuilder()\n            ->from(SimpleBook::class, 'b')\n            ->join('b.author', 'a')\n            ->select('b', 'a')\n            ->where('b.id = :id')\n            ->setParameter('id', $bookId)\n            ->getQuery()\n            ->execute();\n\n        self::assertInstanceOf(SimpleBook::class, $book);\n        self::assertSame('Pride and Prejudice', $book->getTitle());\n        self::assertEquals($bookId, $book->getId());\n        self::assertSame('Jane Austen', $book->getAuthor()->getName());\n    }\n\n    public function testEntityWithManyToMany(): void\n    {\n        $connection = $this->_em->getConnection();\n\n        $connection->insert('author', ['name' => 'Jane Austen']);\n        $authorId = $connection->lastInsertId();\n\n        $connection->insert('book', ['title' => 'Pride and Prejudice']);\n        $bookId = $connection->lastInsertId();\n\n        $connection->insert('book_author', ['book_id' => $bookId, 'author_id' => $authorId]);\n\n        $book = $this->_em->find(Book::class, $bookId);\n\n        self::assertSame('Pride and Prejudice', $book->getTitle());\n        self::assertEquals($bookId, $book->getId());\n        self::assertSame('Jane Austen', $book->getAuthors()[0]->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ReferenceProxyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Proxy\\Proxy as CommonProxy;\nuse Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct2;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceShipping;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function file_exists;\nuse function str_replace;\nuse function strlen;\nuse function substr;\n\nuse const DIRECTORY_SEPARATOR;\n\n/**\n * Tests the generation of a proxy object for lazy loading.\n */\nclass ReferenceProxyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function createProduct(): int\n    {\n        $product = new ECommerceProduct();\n        $product->setName('Doctrine Cookbook');\n        $this->_em->persist($product);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return $product->getId();\n    }\n\n    public function createAuction(): int\n    {\n        $event = new CompanyAuction();\n        $event->setData('Doctrine Cookbook');\n        $this->_em->persist($event);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return $event->getId();\n    }\n\n    public function testLazyLoadsFieldValuesFromDatabase(): void\n    {\n        $id = $this->createProduct();\n\n        $productProxy = $this->_em->getReference(ECommerceProduct::class, ['id' => $id]);\n        self::assertEquals('Doctrine Cookbook', $productProxy->getName());\n    }\n\n    #[Group('DDC-727')]\n    public function testAccessMetatadaForProxy(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        $class  = $this->_em->getClassMetadata($entity::class);\n\n        self::assertEquals(ECommerceProduct::class, $class->name);\n    }\n\n    #[Group('DDC-1033')]\n    public function testReferenceFind(): void\n    {\n        $id = $this->createProduct();\n\n        $entity  = $this->_em->getReference(ECommerceProduct::class, $id);\n        $entity2 = $this->_em->find(ECommerceProduct::class, $id);\n\n        self::assertSame($entity, $entity2);\n        self::assertEquals('Doctrine Cookbook', $entity2->getName());\n    }\n\n    #[Group('DDC-1033')]\n    public function testCloneProxy(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n\n        $clone = clone $entity;\n        assert($clone instanceof ECommerceProduct);\n\n        self::assertEquals($id, $entity->getId());\n        self::assertEquals('Doctrine Cookbook', $entity->getName());\n\n        self::assertFalse($this->_em->contains($clone), 'Cloning a reference proxy should return an unmanaged/detached entity.');\n        self::assertEquals($id, $clone->getId(), 'Cloning a reference proxy should return same id.');\n        self::assertEquals('Doctrine Cookbook', $clone->getName(), 'Cloning a reference proxy should return same product name.');\n\n        // domain logic, Product::__clone sets isCloned public property\n        self::assertTrue($clone->isCloned);\n        self::assertFalse($entity->isCloned);\n    }\n\n    public function testCloneProxyWithResetId(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct2::class, $id);\n        assert($entity instanceof ECommerceProduct2);\n\n        $clone = clone $entity;\n        assert($clone instanceof ECommerceProduct2);\n\n        self::assertEquals($id, $entity->getId());\n        self::assertEquals('Doctrine Cookbook', $entity->getName());\n\n        self::assertFalse($this->_em->contains($clone));\n        self::assertNull($clone->getId());\n        self::assertEquals('Clone of Doctrine Cookbook', $clone->getName());\n    }\n\n    #[Group('DDC-733')]\n    public function testInitializeProxy(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n\n        self::assertTrue($this->isUninitializedObject($entity), 'Pre-Condition: Object is unitialized proxy.');\n        $this->_em->getUnitOfWork()->initializeObject($entity);\n        self::assertFalse($this->isUninitializedObject($entity), 'Should be initialized after called UnitOfWork::initializeObject()');\n    }\n\n    #[Group('DDC-1163')]\n    public function testInitializeChangeAndFlushProxy(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n        $entity->setName('Doctrine 2 Cookbook');\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        self::assertEquals('Doctrine 2 Cookbook', $entity->getName());\n    }\n\n    #[Group('DDC-1022')]\n    public function testWakeupOnProxy(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n\n        self::assertFalse($entity->wakeUp);\n\n        $entity->setName('Doctrine 2 Cookbook');\n\n        if ($entity instanceof CommonProxy) {\n            self::assertTrue($entity->wakeUp, 'Loading the proxy should call __wakeup().');\n        } else {\n            self::assertFalse($entity->wakeUp, 'Loading the proxy should call __wakeup().');\n        }\n    }\n\n    public function testDoNotInitializeProxyOnGettingTheIdentifier(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n\n        self::assertTrue($this->isUninitializedObject($entity), 'Pre-Condition: Object is unitialized proxy.');\n        self::assertEquals($id, $entity->getId());\n        self::assertTrue($this->isUninitializedObject($entity), \"Getting the identifier doesn't initialize the proxy.\");\n    }\n\n    #[Group('DDC-1625')]\n    public function testDoNotInitializeProxyOnGettingTheIdentifierDDC1625(): void\n    {\n        $id = $this->createAuction();\n\n        $entity = $this->_em->getReference(CompanyAuction::class, $id);\n        assert($entity instanceof CompanyAuction);\n\n        self::assertTrue($this->isUninitializedObject($entity), 'Pre-Condition: Object is unitialized proxy.');\n        self::assertEquals($id, $entity->getId());\n        self::assertTrue($this->isUninitializedObject($entity), \"Getting the identifier doesn't initialize the proxy when extending.\");\n    }\n\n    public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType(): void\n    {\n        $product = new ECommerceProduct();\n        $product->setName('Doctrine Cookbook');\n\n        $shipping = new ECommerceShipping();\n        $shipping->setDays(1);\n        $product->setShipping($shipping);\n        $this->_em->persist($product);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $id = $shipping->getId();\n\n        $product = $this->_em->getRepository(ECommerceProduct::class)->find($product->getId());\n\n        $entity = $product->getShipping();\n        self::assertTrue($this->isUninitializedObject($entity), 'Pre-Condition: Object is unitialized proxy.');\n        self::assertEquals($id, $entity->getId());\n        self::assertSame($id, $entity->getId(), \"Check that the id's are the same value, and type.\");\n        self::assertTrue($this->isUninitializedObject($entity), \"Getting the identifier doesn't initialize the proxy.\");\n    }\n\n    public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier(): void\n    {\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n\n        self::assertTrue($this->isUninitializedObject($entity), 'Pre-Condition: Object is unitialized proxy.');\n        self::assertEquals('Doctrine Cookbook', $entity->getName());\n        self::assertFalse($this->isUninitializedObject($entity), 'Getting something other than the identifier initializes the proxy.');\n    }\n\n    #[Group('DDC-1604')]\n    public function testCommonPersistenceProxy(): void\n    {\n        if ($this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            self::markTestSkipped('Test only works with proxy generation disabled.');\n        }\n\n        $id = $this->createProduct();\n\n        $entity = $this->_em->getReference(ECommerceProduct::class, $id);\n        assert($entity instanceof ECommerceProduct);\n        $className = DefaultProxyClassNameResolver::getClass($entity);\n\n        self::assertTrue($this->isUninitializedObject($entity));\n        self::assertEquals(ECommerceProduct::class, $className);\n\n        $restName      = str_replace($this->_em->getConfiguration()->getProxyNamespace(), '', $entity::class);\n        $restName      = substr($entity::class, strlen($this->_em->getConfiguration()->getProxyNamespace()) + 1);\n        $proxyFileName = $this->_em->getConfiguration()->getProxyDir() . DIRECTORY_SEPARATOR . str_replace('\\\\', '', $restName) . '.php';\n        self::assertTrue(file_exists($proxyFileName), 'Proxy file name cannot be found generically.');\n\n        $this->initializeObject($entity);\n        self::assertFalse($this->isUninitializedObject($entity));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ResultCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Cache\\QueryCacheProfile;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\NativeQuery;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function assert;\nuse function count;\nuse function iterator_to_array;\nuse function sprintf;\n\nclass ResultCacheTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testResultCache(): void\n    {\n        $user = new CmsUser();\n\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'dev';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $cache = new ArrayAdapter();\n\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('my_cache_id');\n\n        self::assertCacheDoesNotHaveItem('my_cache_id', $cache);\n\n        $users = $query->getResult();\n\n        self::assertCacheHasItem('my_cache_id', $cache);\n        self::assertCount(1, $users);\n        self::assertEquals('Roman', $users[0]->name);\n\n        $this->_em->clear();\n\n        $query2 = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n        $this->setResultCache($query2, $cache);\n        $query2->setResultCacheId('my_cache_id');\n\n        $users = $query2->getResult();\n\n        self::assertCacheHasItem('my_cache_id', $cache);\n        self::assertCount(1, $users);\n        self::assertEquals('Roman', $users[0]->name);\n    }\n\n    public function testSetResultCacheId(): void\n    {\n        $cache = new ArrayAdapter();\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('testing_result_cache_id');\n\n        self::assertCacheDoesNotHaveItem('testing_result_cache_id', $cache);\n\n        $query->getResult();\n\n        self::assertCacheHasItem('testing_result_cache_id', $cache);\n    }\n\n    #[Group('DDC-1026')]\n    public function testUseResultCacheParams(): void\n    {\n        $cache = new ArrayAdapter();\n        $this->getQueryLog()->reset()->enable();\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux WHERE ux.id = ?1');\n\n        $this->setResultCache($query, $cache);\n\n        // these queries should result in cache miss:\n        $query->setParameter(1, 1);\n        $query->getResult();\n        $query->setParameter(1, 2);\n        $query->getResult();\n\n        $this->assertQueryCount(2, 'Two non-cached queries.');\n\n        // these two queries should actually be cached, as they repeat previous ones:\n        $query->setParameter(1, 1);\n        $query->getResult();\n        $query->setParameter(1, 2);\n        $query->getResult();\n\n        $this->assertQueryCount(2, 'The next two sql queries should have been cached, but were not.');\n    }\n\n    public function testEnableResultCache(): void\n    {\n        $cache = new ArrayAdapter();\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $query->enableResultCache();\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('testing_result_cache_id');\n        $query->getResult();\n\n        self::assertCacheHasItem('testing_result_cache_id', $cache);\n\n        $this->resetCache();\n    }\n\n    public function testEnableResultCacheWithIterable(): void\n    {\n        $cache = new ArrayAdapter();\n        $this->getQueryLog()->reset()->enable();\n\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n        $query->enableResultCache();\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('testing_iterable_result_cache_id');\n        iterator_to_array($query->toIterable());\n\n        $this->_em->clear();\n\n        $this->assertQueryCount(1);\n        self::assertCacheHasItem('testing_iterable_result_cache_id', $cache);\n\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n        $query->enableResultCache();\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('testing_iterable_result_cache_id');\n        iterator_to_array($query->toIterable());\n\n        $this->assertQueryCount(1, 'Expected query to be cached');\n\n        $this->resetCache();\n    }\n\n    #[Group('DDC-1026')]\n    public function testEnableResultCacheParams(): void\n    {\n        $cache = new ArrayAdapter();\n        $this->getQueryLog()->reset()->enable();\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux WHERE ux.id = ?1');\n\n        $this->setResultCache($query, $cache);\n        $query->enableResultCache();\n\n        // these queries should result in cache miss:\n        $query->setParameter(1, 1);\n        $query->getResult();\n        $query->setParameter(1, 2);\n        $query->getResult();\n\n        $this->assertQueryCount(2, 'Two non-cached queries.');\n\n        // these two queries should actually be cached, as they repeat previous ones:\n        $query->setParameter(1, 1);\n        $query->getResult();\n        $query->setParameter(1, 2);\n        $query->getResult();\n\n        $this->assertQueryCount(2, 'The next two sql queries should have been cached, but were not.');\n    }\n\n    public function testDisableResultCache(): void\n    {\n        $cache = new ArrayAdapter();\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $this->setResultCache($query, $cache);\n        $query->setResultCacheId('testing_result_cache_id');\n        $query->disableResultCache();\n        $query->getResult();\n\n        self::assertFalse($cache->hasItem('testing_result_cache_id'));\n\n        $this->resetCache();\n    }\n\n    public function testNativeQueryResultCaching(): array\n    {\n        $cache = new ArrayAdapter();\n        $rsm   = new ResultSetMapping();\n\n        $rsm->addScalarResult('id', 'u', 'integer');\n\n        $query = $this->_em->createNativeQuery('select u.id FROM cms_users u WHERE u.id = ?', $rsm);\n\n        $query->setParameter(1, 10);\n        $this->setResultCache($query, $cache);\n        $query->enableResultCache();\n\n        self::assertEmpty($cache->getValues());\n\n        $query->getResult();\n\n        self::assertNotEmpty($cache->getValues());\n\n        return [$query, $cache];\n    }\n\n    #[Depends('testNativeQueryResultCaching')]\n    public function testResultCacheNotDependsOnQueryHints(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof NativeQuery);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        $query->setHint('foo', 'bar');\n        $query->getResult();\n\n        self::assertCount($cacheCount, $cache->getValues());\n    }\n\n    #[Depends('testNativeQueryResultCaching')]\n    public function testResultCacheDependsOnParameters(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof NativeQuery);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        $query->setParameter(1, 50);\n        $query->getResult();\n\n        self::assertCount($cacheCount + 1, $cache->getValues());\n    }\n\n    #[Depends('testNativeQueryResultCaching')]\n    public function testResultCacheNotDependsOnHydrationMode(array $previous): void\n    {\n        [$query, $cache] = $previous;\n        assert($query instanceof NativeQuery);\n        assert($cache instanceof ArrayAdapter);\n\n        $cacheCount = count($cache->getValues());\n\n        self::assertNotEquals(Query::HYDRATE_ARRAY, $query->getHydrationMode());\n        $query->getArrayResult();\n\n        self::assertCount($cacheCount, $cache->getValues());\n    }\n\n    #[Group('DDC-909')]\n    public function testResultCacheWithObjectParameter(): void\n    {\n        $user1           = new CmsUser();\n        $user1->name     = 'Roman';\n        $user1->username = 'romanb';\n        $user1->status   = 'dev';\n\n        $user2           = new CmsUser();\n        $user2->name     = 'Benjamin';\n        $user2->username = 'beberlei';\n        $user2->status   = 'dev';\n\n        $article        = new CmsArticle();\n        $article->text  = 'foo';\n        $article->topic = 'baz';\n        $article->user  = $user1;\n\n        $this->_em->persist($article);\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = ?1');\n        $query->setParameter(1, $user1);\n\n        $cache = new ArrayAdapter();\n\n        $this->setResultCache($query, $cache);\n        $query->enableResultCache();\n\n        $articles = $query->getResult();\n\n        self::assertCount(1, $articles);\n        self::assertEquals('baz', $articles[0]->topic);\n\n        $this->_em->clear();\n\n        $query2 = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = ?1');\n        $query2->setParameter(1, $user1);\n\n        $this->setResultCache($query2, $cache);\n        $query2->enableResultCache();\n\n        $articles = $query2->getResult();\n\n        self::assertCount(1, $articles);\n        self::assertEquals('baz', $articles[0]->topic);\n\n        $query3 = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = ?1');\n        $query3->setParameter(1, $user2);\n\n        $this->setResultCache($query3, $cache);\n        $query3->enableResultCache();\n\n        $articles = $query3->getResult();\n\n        self::assertCount(0, $articles);\n    }\n\n    private function setResultCache(AbstractQuery $query, CacheItemPoolInterface $cache): void\n    {\n        $query->setResultCacheProfile(\n            (new QueryCacheProfile())\n                ->setResultCache($cache),\n        );\n    }\n\n    private static function assertCacheHasItem(string $key, CacheItemPoolInterface $cache): void\n    {\n        self::assertTrue(\n            $cache->hasItem($key),\n            sprintf('Failed asserting that a given cache contains the key \"%s\".', $key),\n        );\n    }\n\n    private function resetCache(): void\n    {\n        $this->_em->getConfiguration()->setResultCache(new ArrayAdapter());\n    }\n\n    private static function assertCacheDoesNotHaveItem(string $key, CacheItemPoolInterface $cache): void\n    {\n        self::assertFalse(\n            $cache->hasItem($key),\n            sprintf('Failed asserting that a given cache does not contain the key \"%s\".', $key),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SQLFilterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\Filter\\FilterException;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\ORM\\Query\\FilterCollection;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEvent;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyOrganization;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse ReflectionMethod;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function count;\nuse function in_array;\nuse function method_exists;\nuse function serialize;\n\n/**\n * Tests SQLFilter functionality.\n */\n#[Group('non-cacheable')]\nclass SQLFilterTest extends OrmFunctionalTestCase\n{\n    private int $userId;\n    private int $userId2;\n    private int $articleId;\n    private int $articleId2;\n    private int $groupId;\n    private int $groupId2;\n    private int $managerId;\n    private int $managerId2;\n    private int $contractId1;\n    private int $contractId2;\n    private int $organizationId;\n    private int $eventId1;\n    private int $eventId2;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function tearDown(): void\n    {\n        parent::tearDown();\n\n        $class                                         = $this->_em->getClassMetadata(CmsUser::class);\n        $class->associationMappings['groups']->fetch   = ClassMetadata::FETCH_LAZY;\n        $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_LAZY;\n    }\n\n    public function testConfigureFilter(): void\n    {\n        $config = new Configuration();\n\n        $config->addFilter('locale', '\\Doctrine\\Tests\\ORM\\Functional\\MyLocaleFilter');\n\n        self::assertEquals('\\Doctrine\\Tests\\ORM\\Functional\\MyLocaleFilter', $config->getFilterClassName('locale'));\n        self::assertNull($config->getFilterClassName('foo'));\n    }\n\n    public function testEntityManagerEnableFilter(): void\n    {\n        $em = $this->getEntityManager();\n        $this->configureFilters($em);\n\n        // Enable an existing filter\n        $filter = $em->getFilters()->enable('locale');\n        self::assertInstanceOf(MyLocaleFilter::class, $filter);\n\n        // Enable the filter again\n        $filter2 = $em->getFilters()->enable('locale');\n        self::assertEquals($filter, $filter2);\n\n        // Enable a non-existing filter\n        $exceptionThrown = false;\n        try {\n            $filter = $em->getFilters()->enable('foo');\n        } catch (InvalidArgumentException) {\n            $exceptionThrown = true;\n        }\n\n        self::assertTrue($exceptionThrown);\n    }\n\n    public function testEntityManagerEnabledFilters(): void\n    {\n        $em = $this->getEntityManager();\n\n        // No enabled filters\n        self::assertEquals([], $em->getFilters()->getEnabledFilters());\n\n        $this->configureFilters($em);\n        $filter = $em->getFilters()->enable('locale');\n        $filter = $em->getFilters()->enable('soft_delete');\n\n        // Two enabled filters\n        self::assertCount(2, $em->getFilters()->getEnabledFilters());\n    }\n\n    public function testEntityManagerDisableFilter(): void\n    {\n        $em = $this->getEntityManager();\n        $this->configureFilters($em);\n\n        // Enable the filter\n        $filter = $em->getFilters()->enable('locale');\n\n        // Disable it\n        self::assertEquals($filter, $em->getFilters()->disable('locale'));\n        self::assertCount(0, $em->getFilters()->getEnabledFilters());\n\n        // Disable a non-existing filter\n        $exceptionThrown = false;\n        try {\n            $filter = $em->getFilters()->disable('foo');\n        } catch (InvalidArgumentException) {\n            $exceptionThrown = true;\n        }\n\n        self::assertTrue($exceptionThrown);\n\n        // Disable a non-enabled filter\n        $exceptionThrown = false;\n        try {\n            $filter = $em->getFilters()->disable('locale');\n        } catch (InvalidArgumentException) {\n            $exceptionThrown = true;\n        }\n\n        self::assertTrue($exceptionThrown);\n    }\n\n    public function testEntityManagerGetFilter(): void\n    {\n        $em = $this->getEntityManager();\n        $this->configureFilters($em);\n\n        // Enable the filter\n        $filter = $em->getFilters()->enable('locale');\n\n        // Get the filter\n        self::assertEquals($filter, $em->getFilters()->getFilter('locale'));\n\n        // Get a non-enabled filter\n        $exceptionThrown = false;\n        try {\n            $filter = $em->getFilters()->getFilter('soft_delete');\n        } catch (InvalidArgumentException) {\n            $exceptionThrown = true;\n        }\n\n        self::assertTrue($exceptionThrown);\n    }\n\n    #[Group('DDC-2203')]\n    public function testEntityManagerIsFilterEnabled(): void\n    {\n        $em = $this->getEntityManager();\n        $this->configureFilters($em);\n\n        // Check for an enabled filter\n        $em->getFilters()->enable('locale');\n        self::assertTrue($em->getFilters()->isEnabled('locale'));\n\n        // Check for a disabled filter\n        $em->getFilters()->disable('locale');\n        self::assertFalse($em->getFilters()->isEnabled('locale'));\n\n        // Check a non-existing filter\n        self::assertFalse($em->getFilters()->isEnabled('foo_filter'));\n    }\n\n    private function configureFilters(EntityManagerInterface $em): void\n    {\n        // Add filters to the configuration of the EM\n        $config = $em->getConfiguration();\n        $config->addFilter('locale', '\\Doctrine\\Tests\\ORM\\Functional\\MyLocaleFilter');\n        $config->addFilter('soft_delete', '\\Doctrine\\Tests\\ORM\\Functional\\MySoftDeleteFilter');\n    }\n\n    private function getMockConnection(): Connection&MockObject\n    {\n        $connection = $this->createMock(Connection::class);\n        if (method_exists($connection, 'getEventManager')) {\n            $connection->method('getEventManager')\n                ->willReturn(new EventManager());\n        }\n\n        return $connection;\n    }\n\n    private function getMockEntityManager(): EntityManagerInterface&MockObject\n    {\n        return $this->createMock(EntityManagerInterface::class);\n    }\n\n    private function addMockFilterCollection(EntityManagerInterface&MockObject $em): FilterCollection&MockObject\n    {\n        $filterCollection = $this->createMock(FilterCollection::class);\n\n        $em->expects(self::any())\n            ->method('getFilters')\n            ->willReturn($filterCollection);\n\n        return $filterCollection;\n    }\n\n    public function testSQLFilterGetSetParameter(): void\n    {\n        // Setup mock connection\n        $conn = $this->getMockConnection();\n        $conn->expects(self::once())\n            ->method('quote')\n            ->with(self::equalTo('en'))\n            ->willReturn(\"'en'\");\n\n        $em = $this->getMockEntityManager();\n        $em->expects(self::once())\n            ->method('getConnection')\n            ->willReturn($conn);\n\n        $filterCollection = $this->addMockFilterCollection($em);\n        $filterCollection\n            ->expects(self::once())\n            ->method('setFiltersStateDirty');\n\n        $filter = new MyLocaleFilter($em);\n\n        $filter->setParameter('locale', 'en', Types::STRING);\n\n        self::assertEquals(\"'en'\", $filter->getParameter('locale'));\n    }\n\n    #[Group('DDC-3161')]\n    #[Group('1054')]\n    public function testSQLFilterGetConnection(): void\n    {\n        // Setup mock connection\n        $conn = $this->getMockConnection();\n\n        $em = $this->getMockEntityManager();\n        $em->expects(self::once())\n            ->method('getConnection')\n            ->willReturn($conn);\n\n        $filter = new MyLocaleFilter($em);\n\n        $reflMethod = new ReflectionMethod(SQLFilter::class, 'getConnection');\n\n        self::assertSame($conn, $reflMethod->invoke($filter));\n    }\n\n    public function testSQLFilterSetParameterInfersType(): void\n    {\n        // Setup mock connection\n        $conn = $this->getMockConnection();\n        $conn->expects(self::once())\n            ->method('quote')\n            ->with(self::equalTo('en'))\n            ->willReturn(\"'en'\");\n\n        $em = $this->getMockEntityManager();\n        $em->expects(self::once())\n            ->method('getConnection')\n            ->willReturn($conn);\n\n        $filterCollection = $this->addMockFilterCollection($em);\n        $filterCollection\n            ->expects(self::once())\n            ->method('setFiltersStateDirty');\n\n        $filter = new MyLocaleFilter($em);\n\n        $filter->setParameter('locale', 'en');\n\n        self::assertEquals(\"'en'\", $filter->getParameter('locale'));\n    }\n\n    public function testSQLFilterSetArrayParameterInfersType(): void\n    {\n        // Setup mock connection\n        $conn = $this->getMockConnection();\n        $conn->method('quote')\n             ->willReturnCallback(static fn ($value) => \"'\" . $value . \"'\");\n\n        $em = $this->getMockEntityManager();\n        $em->method('getConnection')\n           ->willReturn($conn);\n\n        $filterCollection = $this->addMockFilterCollection($em);\n        $filterCollection\n            ->expects(self::once())\n            ->method('setFiltersStateDirty');\n\n        $filter = new MyLocaleFilter($em);\n\n        $filter->setParameterList('locale', ['en', 'es']);\n\n        self::assertEquals(\"'en','es'\", $filter->getParameterList('locale'));\n    }\n\n    public function testSQLFilterAddConstraint(): void\n    {\n        // Set up metadata mock\n        $targetEntity = $this->getMockBuilder(ClassMetadata::class)\n            ->disableOriginalConstructor()\n            ->getMock();\n\n        $filter = new MySoftDeleteFilter($this->getMockEntityManager());\n\n        // Test for an entity that gets extra filter data\n        $targetEntity->name = 'MyEntity\\SoftDeleteNewsItem';\n        self::assertEquals('t1_.deleted = 0', $filter->addFilterConstraint($targetEntity, 't1_'));\n\n        // Test for an entity that doesn't get extra filter data\n        $targetEntity->name = 'MyEntity\\NoSoftDeleteNewsItem';\n        self::assertEquals('', $filter->addFilterConstraint($targetEntity, 't1_'));\n    }\n\n    public function testSQLFilterToString(): void\n    {\n        $em               = $this->getMockEntityManager();\n        $filterCollection = $this->addMockFilterCollection($em);\n\n        $filter = new MyLocaleFilter($em);\n        $filter->setParameter('locale', 'en', Types::STRING);\n        $filter->setParameter('foo', 'bar', Types::STRING);\n\n        $filter2 = new MyLocaleFilter($em);\n        $filter2->setParameter('foo', 'bar', Types::STRING);\n        $filter2->setParameter('locale', 'en', Types::STRING);\n\n        $parameters = [\n            'foo' => ['value' => 'bar', 'type' => Types::STRING, 'is_list' => false],\n            'locale' => ['value' => 'en', 'type' => Types::STRING, 'is_list' => false],\n        ];\n\n        self::assertEquals(serialize($parameters), '' . $filter);\n        self::assertEquals('' . $filter, '' . $filter2);\n    }\n\n    public function testQueryCacheDependsOnFilters(): void\n    {\n        $query = $this->_em->createQuery('select ux from Doctrine\\Tests\\Models\\CMS\\CmsUser ux');\n\n        $cache = new ArrayAdapter();\n        $query->setQueryCache($cache);\n\n        $query->getResult();\n        self::assertCount(1, $cache->getValues());\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('locale', MyLocaleFilter::class);\n        $this->_em->getFilters()->enable('locale');\n\n        $query->getResult();\n        self::assertCount(2, $cache->getValues());\n\n        // Another time doesn't add another cache entry\n        $query->getResult();\n        self::assertCount(2, $cache->getValues());\n    }\n\n    public function testQueryGenerationDependsOnFilters(): void\n    {\n        $query         = $this->_em->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsAddress a');\n        $firstSQLQuery = $query->getSQL();\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('country', '\\Doctrine\\Tests\\ORM\\Functional\\CMSCountryFilter');\n        $this->_em->getFilters()->enable('country')\n            ->setParameterList('country', ['en'], Types::STRING);\n\n        self::assertNotEquals($firstSQLQuery, $query->getSQL());\n    }\n\n    public function testRepositoryFind(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertNotNull($this->_em->getRepository(CmsGroup::class)->find($this->groupId));\n        self::assertNotNull($this->_em->getRepository(CmsGroup::class)->find($this->groupId2));\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertNotNull($this->_em->getRepository(CmsGroup::class)->find($this->groupId));\n        self::assertNull($this->_em->getRepository(CmsGroup::class)->find($this->groupId2));\n    }\n\n    public function testRepositoryFindAll(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertCount(2, $this->_em->getRepository(CmsGroup::class)->findAll());\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertCount(1, $this->_em->getRepository(CmsGroup::class)->findAll());\n    }\n\n    public function testRepositoryFindBy(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertCount(1, $this->_em->getRepository(CmsGroup::class)->findBy(\n            ['id' => $this->groupId2],\n        ));\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertCount(0, $this->_em->getRepository(CmsGroup::class)->findBy(\n            ['id' => $this->groupId2],\n        ));\n    }\n\n    public function testRepositoryFindByX(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertCount(1, $this->_em->getRepository(CmsGroup::class)->findById($this->groupId2));\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertCount(0, $this->_em->getRepository(CmsGroup::class)->findById($this->groupId2));\n    }\n\n    public function testRepositoryFindOneBy(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertNotNull($this->_em->getRepository(CmsGroup::class)->findOneBy(\n            ['id' => $this->groupId2],\n        ));\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->getRepository(CmsGroup::class)->findOneBy(\n            ['id' => $this->groupId2],\n        ));\n    }\n\n    public function testRepositoryFindOneByX(): void\n    {\n        $this->loadFixtureData();\n\n        self::assertNotNull($this->_em->getRepository(CmsGroup::class)->findOneById($this->groupId2));\n\n        $this->useCMSGroupPrefixFilter();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->getRepository(CmsGroup::class)->findOneById($this->groupId2));\n    }\n\n    public function testToOneFilter(): void\n    {\n        $this->loadFixtureData();\n\n        $query = $this->_em->createQuery('select ux, ua from Doctrine\\Tests\\Models\\CMS\\CmsUser ux JOIN ux.address ua');\n\n        // We get two users before enabling the filter\n        self::assertCount(2, $query->getResult());\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('country', '\\Doctrine\\Tests\\ORM\\Functional\\CMSCountryFilter');\n        $this->_em->getFilters()->enable('country')->setParameterList('country', ['Germany'], Types::STRING);\n\n        // We get one user after enabling the filter\n        self::assertCount(1, $query->getResult());\n\n        $this->_em->getFilters()->enable('country')->setParameterList('country', ['Germany', 'France'], Types::STRING);\n\n        self::assertEquals(2, count($query->getResult()));\n    }\n\n    public function testOneToOneInverseSideWithFilter(): void\n    {\n        $this->loadFixtureData();\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('country', '\\Doctrine\\Tests\\ORM\\Functional\\CMSCountryFilter');\n        $this->_em->getFilters()->enable('country')->setParameterList('country', ['Germany'], Types::STRING);\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n        self::assertNotEmpty($user->address);\n\n        $user2 = $this->_em->find(CmsUser::class, $this->userId2);\n        self::assertEmpty($user2->address);\n    }\n\n    public function testManyToManyFilter(): void\n    {\n        $this->loadFixtureData();\n        $query = $this->_em->createQuery('select ux, ug from Doctrine\\Tests\\Models\\CMS\\CmsUser ux JOIN ux.groups ug');\n\n        // We get two users before enabling the filter\n        self::assertCount(2, $query->getResult());\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('group_prefix', '\\Doctrine\\Tests\\ORM\\Functional\\CMSGroupPrefixFilter');\n        $this->_em->getFilters()->enable('group_prefix')->setParameter('prefix', 'bar_%', Types::STRING);\n\n        // We get one user after enabling the filter\n        self::assertCount(1, $query->getResult());\n    }\n\n    public function testWhereFilter(): void\n    {\n        $this->loadFixtureData();\n        $query = $this->_em->createQuery('select ug from Doctrine\\Tests\\Models\\CMS\\CmsGroup ug WHERE 1=1');\n\n        // We get two users before enabling the filter\n        self::assertCount(2, $query->getResult());\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('group_prefix', '\\Doctrine\\Tests\\ORM\\Functional\\CMSGroupPrefixFilter');\n        $this->_em->getFilters()->enable('group_prefix')->setParameter('prefix', 'bar_%', Types::STRING);\n\n        // We get one user after enabling the filter\n        self::assertCount(1, $query->getResult());\n    }\n\n    public function testWhereOrFilter(): void\n    {\n        $this->loadFixtureData();\n        $query = $this->_em->createQuery('select ug from Doctrine\\Tests\\Models\\CMS\\CmsGroup ug WHERE 1=1 OR 1=1');\n\n        // We get two users before enabling the filter\n        self::assertCount(2, $query->getResult());\n\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('group_prefix', '\\Doctrine\\Tests\\ORM\\Functional\\CMSGroupPrefixFilter');\n        $this->_em->getFilters()->enable('group_prefix')->setParameter('prefix', 'bar_%', Types::STRING);\n\n        // We get one user after enabling the filter\n        self::assertCount(1, $query->getResult());\n    }\n\n    private function loadLazyFixtureData(): void\n    {\n        $class                                         = $this->_em->getClassMetadata(CmsUser::class);\n        $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['groups']->fetch   = ClassMetadata::FETCH_EXTRA_LAZY;\n        $this->loadFixtureData();\n    }\n\n    private function useCMSArticleTopicFilter(): void\n    {\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('article_topic', '\\Doctrine\\Tests\\ORM\\Functional\\CMSArticleTopicFilter');\n        $this->_em->getFilters()->enable('article_topic')->setParameter('topic', 'Test1', Types::STRING);\n    }\n\n    public function testOneToManyExtraLazyCountWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        self::assertFalse($user->articles->isInitialized());\n        self::assertCount(2, $user->articles);\n\n        $this->useCMSArticleTopicFilter();\n\n        self::assertCount(1, $user->articles);\n    }\n\n    public function testOneToManyExtraLazyContainsWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n        $user            = $this->_em->find(CmsUser::class, $this->userId);\n        $filteredArticle = $this->_em->find(CmsArticle::class, $this->articleId2);\n\n        self::assertFalse($user->articles->isInitialized());\n        self::assertTrue($user->articles->contains($filteredArticle));\n\n        $this->useCMSArticleTopicFilter();\n\n        self::assertFalse($user->articles->contains($filteredArticle));\n    }\n\n    public function testOneToManyExtraLazySliceWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        self::assertFalse($user->articles->isInitialized());\n        self::assertCount(2, $user->articles->slice(0, 10));\n\n        $this->useCMSArticleTopicFilter();\n\n        self::assertCount(1, $user->articles->slice(0, 10));\n    }\n\n    private function useCMSGroupPrefixFilter(): void\n    {\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('group_prefix', '\\Doctrine\\Tests\\ORM\\Functional\\CMSGroupPrefixFilter');\n        $this->_em->getFilters()->enable('group_prefix')->setParameter('prefix', 'foo%', Types::STRING);\n    }\n\n    public function testManyToManyExtraLazyCountWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertCount(2, $user->groups);\n\n        $this->useCMSGroupPrefixFilter();\n\n        self::assertCount(1, $user->groups);\n    }\n\n    public function testManyToManyExtraLazyContainsWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n        $user            = $this->_em->find(CmsUser::class, $this->userId2);\n        $filteredArticle = $this->_em->find(CmsGroup::class, $this->groupId2);\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertTrue($user->groups->contains($filteredArticle));\n\n        $this->useCMSGroupPrefixFilter();\n\n        self::assertFalse($user->groups->contains($filteredArticle));\n    }\n\n    public function testManyToManyExtraLazySliceWithFilter(): void\n    {\n        $this->loadLazyFixtureData();\n        $user = $this->_em->find(CmsUser::class, $this->userId2);\n\n        self::assertFalse($user->groups->isInitialized());\n        self::assertCount(2, $user->groups->slice(0, 10));\n\n        $this->useCMSGroupPrefixFilter();\n\n        self::assertCount(1, $user->groups->slice(0, 10));\n    }\n\n    private function loadFixtureData(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Roman';\n        $user->username = 'romanb';\n        $user->status   = 'developer';\n\n        $address          = new CmsAddress();\n        $address->country = 'Germany';\n        $address->city    = 'Berlin';\n        $address->zip     = '12345';\n\n        $user->address = $address; // inverse side\n        $address->user = $user; // owning side!\n\n        $group       = new CmsGroup();\n        $group->name = 'foo_group';\n        $user->addGroup($group);\n\n        $article1        = new CmsArticle();\n        $article1->topic = 'Test1';\n        $article1->text  = 'Test';\n        $article1->setAuthor($user);\n\n        $article2        = new CmsArticle();\n        $article2->topic = 'Test2';\n        $article2->text  = 'Test';\n        $article2->setAuthor($user);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $this->_em->persist($user);\n\n        $user2           = new CmsUser();\n        $user2->name     = 'Guilherme';\n        $user2->username = 'gblanco';\n        $user2->status   = 'developer';\n\n        $address2          = new CmsAddress();\n        $address2->country = 'France';\n        $address2->city    = 'Paris';\n        $address2->zip     = '12345';\n\n        $user->address  = $address2; // inverse side\n        $address2->user = $user2; // owning side!\n\n        $user2->addGroup($group);\n        $group2       = new CmsGroup();\n        $group2->name = 'bar_group';\n        $user2->addGroup($group2);\n\n        $this->_em->persist($user2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->userId     = $user->getId();\n        $this->userId2    = $user2->getId();\n        $this->articleId  = $article1->id;\n        $this->articleId2 = $article2->id;\n        $this->groupId    = $group->id;\n        $this->groupId2   = $group2->id;\n    }\n\n    public function testJoinSubclassPersisterFilterOnlyOnRootTableWhenFetchingSubEntity(): void\n    {\n        $this->loadCompanyJoinedSubclassFixtureData();\n        // Persister\n        self::assertCount(2, $this->_em->getRepository(CompanyManager::class)->findAll());\n        // SQLWalker\n        self::assertCount(2, $this->_em->createQuery('SELECT cm FROM Doctrine\\Tests\\Models\\Company\\CompanyManager cm')->getResult());\n\n        // Enable the filter\n        $this->usePersonNameFilter('Guilh%');\n\n        $managers = $this->_em->getRepository(CompanyManager::class)->findAll();\n        self::assertCount(1, $managers);\n        self::assertEquals('Guilherme', $managers[0]->getName());\n\n        self::assertCount(1, $this->_em->createQuery('SELECT cm FROM Doctrine\\Tests\\Models\\Company\\CompanyManager cm')->getResult());\n    }\n\n    public function testJoinSubclassPersisterFilterOnlyOnRootTableWhenFetchingRootEntity(): void\n    {\n        $this->loadCompanyJoinedSubclassFixtureData();\n        self::assertCount(3, $this->_em->getRepository(CompanyPerson::class)->findAll());\n        self::assertCount(3, $this->_em->createQuery('SELECT cp FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson cp')->getResult());\n\n        // Enable the filter\n        $this->usePersonNameFilter('Guilh%');\n\n        $persons = $this->_em->getRepository(CompanyPerson::class)->findAll();\n        self::assertCount(1, $persons);\n        self::assertEquals('Guilherme', $persons[0]->getName());\n\n        self::assertCount(1, $this->_em->createQuery('SELECT cp FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson cp')->getResult());\n    }\n\n    private function loadCompanyJoinedSubclassFixtureData(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('Roman');\n        $manager->setTitle('testlead');\n        $manager->setSalary(42);\n        $manager->setDepartment('persisters');\n\n        $manager2 = new CompanyManager();\n        $manager2->setName('Guilherme');\n        $manager2->setTitle('devlead');\n        $manager2->setSalary(42);\n        $manager2->setDepartment('parsers');\n\n        $person = new CompanyPerson();\n        $person->setName('Benjamin');\n\n        $this->_em->persist($manager);\n        $this->_em->persist($manager2);\n        $this->_em->persist($person);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testSingleTableInheritanceFilterOnlyOnRootTableWhenFetchingSubEntity(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n        // Persister\n        self::assertCount(2, $this->_em->getRepository(CompanyFlexUltraContract::class)->findAll());\n        // SQLWalker\n        self::assertCount(2, $this->_em->createQuery('SELECT cfc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract cfc')->getResult());\n\n        // Enable the filter\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('completed_contract', '\\Doctrine\\Tests\\ORM\\Functional\\CompletedContractFilter');\n        $this->_em->getFilters()\n            ->enable('completed_contract')\n            ->setParameter('completed', true, Types::BOOLEAN);\n\n        self::assertCount(1, $this->_em->getRepository(CompanyFlexUltraContract::class)->findAll());\n        self::assertCount(1, $this->_em->createQuery('SELECT cfc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract cfc')->getResult());\n    }\n\n    public function testSingleTableInheritanceFilterOnlyOnRootTableWhenFetchingRootEntity(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n        self::assertCount(4, $this->_em->getRepository(CompanyFlexContract::class)->findAll());\n        self::assertCount(4, $this->_em->createQuery('SELECT cfc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexContract cfc')->getResult());\n\n        // Enable the filter\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('completed_contract', '\\Doctrine\\Tests\\ORM\\Functional\\CompletedContractFilter');\n        $this->_em->getFilters()\n            ->enable('completed_contract')\n            ->setParameter('completed', true, Types::BOOLEAN);\n\n        self::assertCount(2, $this->_em->getRepository(CompanyFlexContract::class)->findAll());\n        self::assertCount(2, $this->_em->createQuery('SELECT cfc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexContract cfc')->getResult());\n    }\n\n    private function loadCompanySingleTableInheritanceFixtureData(): void\n    {\n        $contract1 = new CompanyFlexUltraContract();\n        $contract2 = new CompanyFlexUltraContract();\n        $contract2->markCompleted();\n\n        $contract3 = new CompanyFlexContract();\n        $contract4 = new CompanyFlexContract();\n        $contract4->markCompleted();\n\n        $manager = new CompanyManager();\n        $manager->setName('Alexander');\n        $manager->setSalary(42);\n        $manager->setDepartment('Doctrine');\n        $manager->setTitle('Filterer');\n\n        $manager2 = new CompanyManager();\n        $manager2->setName('Benjamin');\n        $manager2->setSalary(1337);\n        $manager2->setDepartment('Doctrine');\n        $manager2->setTitle('Maintainer');\n\n        $contract1->addManager($manager);\n        $contract2->addManager($manager);\n        $contract3->addManager($manager);\n        $contract4->addManager($manager);\n\n        $contract1->addManager($manager2);\n\n        $contract1->setSalesPerson($manager);\n        $contract2->setSalesPerson($manager);\n\n        $this->_em->persist($manager);\n        $this->_em->persist($manager2);\n        $this->_em->persist($contract1);\n        $this->_em->persist($contract2);\n        $this->_em->persist($contract3);\n        $this->_em->persist($contract4);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->managerId   = $manager->getId();\n        $this->managerId2  = $manager2->getId();\n        $this->contractId1 = $contract1->getId();\n        $this->contractId2 = $contract2->getId();\n    }\n\n    private function useCompletedContractFilter(): void\n    {\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('completed_contract', '\\Doctrine\\Tests\\ORM\\Functional\\CompletedContractFilter');\n        $this->_em->getFilters()\n            ->enable('completed_contract')\n            ->setParameter('completed', true, Types::BOOLEAN);\n    }\n\n    public function testManyToManyExtraLazyCountWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager = $this->_em->find(CompanyManager::class, $this->managerId);\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertCount(4, $manager->managedContracts);\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertCount(2, $manager->managedContracts);\n    }\n\n    public function testManyToManyExtraLazyContainsWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager   = $this->_em->find(CompanyManager::class, $this->managerId);\n        $contract1 = $this->_em->find(CompanyContract::class, $this->contractId1);\n        $contract2 = $this->_em->find(CompanyContract::class, $this->contractId2);\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertTrue($manager->managedContracts->contains($contract1));\n        self::assertTrue($manager->managedContracts->contains($contract2));\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertFalse($manager->managedContracts->contains($contract1));\n        self::assertTrue($manager->managedContracts->contains($contract2));\n    }\n\n    public function testManyToManyExtraLazySliceWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager = $this->_em->find(CompanyManager::class, $this->managerId);\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertCount(4, $manager->managedContracts->slice(0, 10));\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->managedContracts->isInitialized());\n        self::assertCount(2, $manager->managedContracts->slice(0, 10));\n    }\n\n    private function usePersonNameFilter($name): void\n    {\n        // Enable the filter\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('person_name', '\\Doctrine\\Tests\\ORM\\Functional\\CompanyPersonNameFilter');\n        $this->_em->getFilters()\n            ->enable('person_name')\n            ->setParameter('name', $name, Types::STRING);\n    }\n\n    public function testManyToManyExtraLazyCountWithFilterOnCTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $contract = $this->_em->find(CompanyFlexUltraContract::class, $this->contractId1);\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertCount(2, $contract->managers);\n\n        // Enable the filter\n        $this->usePersonNameFilter('Benjamin');\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertCount(1, $contract->managers);\n    }\n\n    public function testManyToManyExtraLazyContainsWithFilterOnCTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $contract = $this->_em->find(CompanyFlexUltraContract::class, $this->contractId1);\n        $manager1 = $this->_em->find(CompanyManager::class, $this->managerId);\n        $manager2 = $this->_em->find(CompanyManager::class, $this->managerId2);\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertTrue($contract->managers->contains($manager1));\n        self::assertTrue($contract->managers->contains($manager2));\n\n        // Enable the filter\n        $this->usePersonNameFilter('Benjamin');\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertFalse($contract->managers->contains($manager1));\n        self::assertTrue($contract->managers->contains($manager2));\n    }\n\n    public function testManyToManyExtraLazySliceWithFilterOnCTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $contract = $this->_em->find(CompanyFlexUltraContract::class, $this->contractId1);\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertCount(2, $contract->managers->slice(0, 10));\n\n        // Enable the filter\n        $this->usePersonNameFilter('Benjamin');\n\n        self::assertFalse($contract->managers->isInitialized());\n        self::assertCount(1, $contract->managers->slice(0, 10));\n    }\n\n    public function testOneToManyExtraLazyCountWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager = $this->_em->find(CompanyManager::class, $this->managerId);\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertCount(2, $manager->soldContracts);\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertCount(1, $manager->soldContracts);\n    }\n\n    public function testOneToManyExtraLazyContainsWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager   = $this->_em->find(CompanyManager::class, $this->managerId);\n        $contract1 = $this->_em->find(CompanyContract::class, $this->contractId1);\n        $contract2 = $this->_em->find(CompanyContract::class, $this->contractId2);\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertTrue($manager->soldContracts->contains($contract1));\n        self::assertTrue($manager->soldContracts->contains($contract2));\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertFalse($manager->soldContracts->contains($contract1));\n        self::assertTrue($manager->soldContracts->contains($contract2));\n    }\n\n    public function testOneToManyExtraLazySliceWithFilterOnSTI(): void\n    {\n        $this->loadCompanySingleTableInheritanceFixtureData();\n\n        $manager = $this->_em->find(CompanyManager::class, $this->managerId);\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertCount(2, $manager->soldContracts->slice(0, 10));\n\n        // Enable the filter\n        $this->useCompletedContractFilter();\n\n        self::assertFalse($manager->soldContracts->isInitialized());\n        self::assertCount(1, $manager->soldContracts->slice(0, 10));\n    }\n\n    private function loadCompanyOrganizationEventJoinedSubclassFixtureData(): void\n    {\n        $organization = new CompanyOrganization();\n\n        $event1 = new CompanyAuction();\n        $event1->setData('foo');\n\n        $event2 = new CompanyAuction();\n        $event2->setData('bar');\n\n        $organization->addEvent($event1);\n        $organization->addEvent($event2);\n\n        $this->_em->persist($organization);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->organizationId = $organization->getId();\n        $this->eventId1       = $event1->getId();\n        $this->eventId2       = $event2->getId();\n    }\n\n    private function useCompanyEventIdFilter(): void\n    {\n        // Enable the filter\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('event_id', CompanyEventFilter::class);\n        $this->_em->getFilters()\n            ->enable('event_id')\n            ->setParameter('id', $this->eventId2);\n    }\n\n    public function testOneToManyExtraLazyCountWithFilterOnCTI(): void\n    {\n        $this->loadCompanyOrganizationEventJoinedSubclassFixtureData();\n\n        $organization = $this->_em->find(CompanyOrganization::class, $this->organizationId);\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertCount(2, $organization->events);\n\n        // Enable the filter\n        $this->useCompanyEventIdFilter();\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertCount(1, $organization->events);\n    }\n\n    public function testOneToManyExtraLazyContainsWithFilterOnCTI(): void\n    {\n        $this->loadCompanyOrganizationEventJoinedSubclassFixtureData();\n\n        $organization = $this->_em->find(CompanyOrganization::class, $this->organizationId);\n\n        $event1 = $this->_em->find(CompanyEvent::class, $this->eventId1);\n        $event2 = $this->_em->find(CompanyEvent::class, $this->eventId2);\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertTrue($organization->events->contains($event1));\n        self::assertTrue($organization->events->contains($event2));\n\n        // Enable the filter\n        $this->useCompanyEventIdFilter();\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertFalse($organization->events->contains($event1));\n        self::assertTrue($organization->events->contains($event2));\n    }\n\n    public function testOneToManyExtraLazySliceWithFilterOnCTI(): void\n    {\n        $this->loadCompanyOrganizationEventJoinedSubclassFixtureData();\n\n        $organization = $this->_em->find(CompanyOrganization::class, $this->organizationId);\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertCount(2, $organization->events->slice(0, 10));\n\n        // Enable the filter\n        $this->useCompanyEventIdFilter();\n\n        self::assertFalse($organization->events->isInitialized());\n        self::assertCount(1, $organization->events->slice(0, 10));\n    }\n\n    public function testRetrieveSingleAsListThrowsException(): void\n    {\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('country', '\\Doctrine\\Tests\\ORM\\Functional\\CMSCountryFilter');\n\n        $this->_em->getFilters()->enable('country');\n\n        $this->expectException(FilterException::class);\n        $this->expectExceptionMessage('Cannot convert single SQL filter parameter \"country\" into a list value.');\n\n        $this->_em->getFilters()->getFilter('country')->setParameter('country', 'DE');\n        $this->_em->getFilters()->getFilter('country')->getParameterList('country');\n    }\n\n    public function testRetrieveListAsSingleThrowsException(): void\n    {\n        $conf = $this->_em->getConfiguration();\n        $conf->addFilter('country', '\\Doctrine\\Tests\\ORM\\Functional\\CMSCountryFilter');\n\n        $this->_em->getFilters()->enable('country');\n\n        $this->expectException(FilterException::class);\n        $this->expectExceptionMessage('Cannot convert list-based SQL filter parameter \"country\" into a single value.');\n\n        $this->_em->getFilters()->getFilter('country')->setParameterList('country', ['DE']);\n        $this->_em->getFilters()->getFilter('country')->getParameter('country');\n    }\n}\n\nclass MySoftDeleteFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== 'MyEntity\\SoftDeleteNewsItem') {\n            return '';\n        }\n\n        return $targetTableAlias . '.deleted = 0';\n    }\n}\n\nclass MyLocaleFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if (! in_array('LocaleAware', $targetEntity->reflClass->getInterfaceNames(), true)) {\n            return '';\n        }\n\n        return $targetTableAlias . '.locale = ' . $this->getParameter('locale'); // getParam uses connection to quote the value.\n    }\n}\n\nclass CMSCountryFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CmsAddress::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.country IN (' . $this->getParameterList('country') . ')'; // getParam uses connection to quote the value.\n    }\n}\n\nclass CMSGroupPrefixFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CmsGroup::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.name LIKE ' . $this->getParameter('prefix'); // getParam uses connection to quote the value.\n    }\n}\n\nclass CMSArticleTopicFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CmsArticle::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.topic = ' . $this->getParameter('topic'); // getParam uses connection to quote the value.\n    }\n}\n\nclass CompanyPersonNameFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CompanyPerson::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.name LIKE ' . $this->getParameter('name');\n    }\n}\n\nclass CompletedContractFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CompanyContract::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.completed = ' . $this->getParameter('completed');\n    }\n}\n\nclass CompanyEventFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        if ($targetEntity->name !== CompanyEvent::class) {\n            return '';\n        }\n\n        return $targetTableAlias . '.id = ' . $this->getParameter('id');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\SchemaTool;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy.\n */\nclass CompanySchemaTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-966')]\n    public function testGeneratedSchema(): Schema\n    {\n        $schema = $this->createSchemaManager()->introspectSchema();\n\n        self::assertTrue($schema->hasTable('company_contracts'));\n\n        return $schema;\n    }\n\n    #[Depends('testGeneratedSchema')]\n    #[Group('DDC-966')]\n    public function testSingleTableInheritance(Schema $schema): void\n    {\n        $table = $schema->getTable('company_contracts');\n\n        // Check nullability constraints\n        self::assertTrue($table->getColumn('id')->getNotnull());\n        self::assertTrue($table->getColumn('completed')->getNotnull());\n        self::assertFalse($table->getColumn('salesPerson_id')->getNotnull());\n        self::assertTrue($table->getColumn('discr')->getNotnull());\n        self::assertFalse($table->getColumn('fixPrice')->getNotnull());\n        self::assertFalse($table->getColumn('hoursWorked')->getNotnull());\n        self::assertFalse($table->getColumn('pricePerHour')->getNotnull());\n        self::assertFalse($table->getColumn('maxPrice')->getNotnull());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaTool/DBAL483Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\SchemaTool;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_filter;\nuse function str_contains;\n\nclass DBAL483Test extends OrmFunctionalTestCase\n{\n    #[Group('DBAL-483')]\n    public function testDefaultValueIsComparedCorrectly(): void\n    {\n        $class = DBAL483Default::class;\n\n        $this->createSchemaForModels($class);\n\n        $updateSql = $this->getUpdateSchemaSqlForModels($class);\n\n        $updateSql = array_filter($updateSql, static fn ($sql) => str_contains($sql, 'DBAL483'));\n\n        self::assertCount(0, $updateSql);\n    }\n}\n\n#[Entity]\nclass DBAL483Default\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'integer', options: ['default' => 0])]\n    public $num;\n\n    /** @var string */\n    #[Column(type: 'string', options: ['default' => 'foo'])]\n    public $str = 'foo';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaTool/DDC214Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\SchemaTool;\n\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\DBAL\\Schema\\ComparatorConfig;\nuse Doctrine\\Tests\\Models;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_filter;\nuse function class_exists;\nuse function implode;\nuse function str_contains;\n\nuse const PHP_EOL;\n\n/**\n * WARNING: This test should be run as last test! It can affect others very easily!\n */\nclass DDC214Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $conn = $this->_em->getConnection();\n\n        if ($conn->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('SQLite does not support ALTER TABLE statements.');\n        }\n    }\n\n    #[Group('DDC-214')]\n    public function testCmsAddressModel(): void\n    {\n        $this->assertCreatedSchemaNeedsNoUpdates(\n            Models\\CMS\\CmsUser::class,\n            Models\\CMS\\CmsPhonenumber::class,\n            Models\\CMS\\CmsAddress::class,\n            Models\\CMS\\CmsGroup::class,\n            Models\\CMS\\CmsArticle::class,\n            Models\\CMS\\CmsEmail::class,\n        );\n    }\n\n    #[Group('DDC-214')]\n    public function testCompanyModel(): void\n    {\n        $this->assertCreatedSchemaNeedsNoUpdates(\n            Models\\Company\\CompanyPerson::class,\n            Models\\Company\\CompanyEmployee::class,\n            Models\\Company\\CompanyManager::class,\n            Models\\Company\\CompanyOrganization::class,\n            Models\\Company\\CompanyEvent::class,\n            Models\\Company\\CompanyAuction::class,\n            Models\\Company\\CompanyRaffle::class,\n            Models\\Company\\CompanyCar::class,\n        );\n    }\n\n    /** @param class-string ...$classes */\n    public function assertCreatedSchemaNeedsNoUpdates(string ...$classes): void\n    {\n        $this->createSchemaForModels(...$classes);\n\n        $sm = $this->createSchemaManager();\n\n        $fromSchema = $sm->introspectSchema();\n        $toSchema   = $this->getSchemaForModels(...$classes);\n\n        if (class_exists(ComparatorConfig::class)) {\n            $comparator = $sm->createComparator((new ComparatorConfig())->withReportModifiedIndexes(false));\n        } else {\n            $comparator = $sm->createComparator();\n        }\n\n        $schemaDiff = $comparator->compareSchemas($fromSchema, $toSchema);\n\n        $sql = $this->_em->getConnection()->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff);\n\n        $sql = array_filter($sql, static fn ($sql) => ! str_contains($sql, 'DROP'));\n\n        self::assertCount(0, $sql, 'SQL: ' . implode(PHP_EOL, $sql));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\SchemaTool;\n\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\DBAL\\Schema\\Visitor\\Visitor;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\Models\\Generic\\DecimalModel;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\n\nclass MySqlSchemaToolTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof MySQLPlatform) {\n            self::markTestSkipped('The ' . self::class . ' requires the use of mysql.');\n        }\n    }\n\n    public function testGetCreateSchemaSql(): void\n    {\n        $classes = [\n            $this->_em->getClassMetadata(CmsGroup::class),\n            $this->_em->getClassMetadata(CmsUser::class),\n            $this->_em->getClassMetadata(CmsTag::class),\n            $this->_em->getClassMetadata(CmsAddress::class),\n            $this->_em->getClassMetadata(CmsEmail::class),\n            $this->_em->getClassMetadata(CmsPhonenumber::class),\n        ];\n\n        $tool = new SchemaTool($this->_em);\n        $sql  = $tool->getCreateSchemaSql($classes);\n\n        self::assertThat($sql[0], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_groups (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_groups (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[1], self::logicalOr(\n            // DBAL 3\n            self::equalTo('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4 (see https://github.com/doctrine/dbal/pull/4777)\n            self::equalTo('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, email_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, email_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[2], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY (user_id, group_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[3], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_users_tags (user_id INT NOT NULL, tag_id INT NOT NULL, INDEX IDX_93F5A1ADA76ED395 (user_id), INDEX IDX_93F5A1ADBAD26311 (tag_id), PRIMARY KEY(user_id, tag_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_users_tags (user_id INT NOT NULL, tag_id INT NOT NULL, INDEX IDX_93F5A1ADA76ED395 (user_id), INDEX IDX_93F5A1ADBAD26311 (tag_id), PRIMARY KEY (user_id, tag_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[4], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_tags (id INT AUTO_INCREMENT NOT NULL, tag_name VARCHAR(50) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_tags (id INT AUTO_INCREMENT NOT NULL, tag_name VARCHAR(50) DEFAULT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[5], self::logicalOr(\n            // DBAL 3\n            self::equalTo('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4 (see https://github.com/doctrine/dbal/pull/4777)\n            self::equalTo('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[6], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_emails (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(250) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_emails (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(250) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[7], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY (phonenumber)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertEquals('ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id)', $sql[8]);\n        self::assertEquals('ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)', $sql[9]);\n        self::assertEquals('ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id)', $sql[10]);\n        self::assertEquals('ALTER TABLE cms_users_tags ADD CONSTRAINT FK_93F5A1ADA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)', $sql[11]);\n        self::assertEquals('ALTER TABLE cms_users_tags ADD CONSTRAINT FK_93F5A1ADBAD26311 FOREIGN KEY (tag_id) REFERENCES cms_tags (id)', $sql[12]);\n        self::assertEquals('ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)', $sql[13]);\n        self::assertEquals('ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)', $sql[14]);\n\n        self::assertCount(15, $sql);\n    }\n\n    public function testGetCreateSchemaSql2(): void\n    {\n        $classes = [$this->_em->getClassMetadata(DecimalModel::class)];\n\n        $tool = new SchemaTool($this->_em);\n        $sql  = $tool->getCreateSchemaSql($classes);\n\n        self::assertCount(1, $sql);\n        self::assertThat($sql[0], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE decimal_model (id INT AUTO_INCREMENT NOT NULL, `decimal` NUMERIC(5, 2) NOT NULL, `high_scale` NUMERIC(14, 4) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE decimal_model (id INT AUTO_INCREMENT NOT NULL, `decimal` NUMERIC(5, 2) NOT NULL, `high_scale` NUMERIC(14, 4) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n    }\n\n    public function testGetCreateSchemaSql3(): void\n    {\n        $classes = [$this->_em->getClassMetadata(BooleanModel::class)];\n\n        $tool = new SchemaTool($this->_em);\n        $sql  = $tool->getCreateSchemaSql($classes);\n\n        self::assertCount(1, $sql);\n        self::assertThat($sql[0], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT(1) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.4 (see https://github.com/doctrine/dbal/pull/7221)\n            self::equalTo('CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n    }\n\n    #[Group('DBAL-204')]\n    public function testGetCreateSchemaSql4(): void\n    {\n        if (! class_exists(Visitor::class)) {\n            self::markTestSkipped('Test valid for doctrine/dbal:3.x only.');\n        }\n\n        $classes = [$this->_em->getClassMetadata(MysqlSchemaNamespacedEntity::class)];\n\n        $tool = new SchemaTool($this->_em);\n        $sql  = $tool->getCreateSchemaSql($classes);\n\n        self::assertCount(0, $sql);\n    }\n}\n\n#[Table('namespace.entity')]\n#[Entity]\nclass MysqlSchemaNamespacedEntity\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\SchemaTool;\n\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_filter;\nuse function array_values;\nuse function implode;\nuse function str_starts_with;\n\nclass PostgreSqlSchemaToolTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n        if (! $platform instanceof PostgreSQLPlatform) {\n            self::markTestSkipped('The ' . self::class . ' requires the use of postgresql.');\n        }\n    }\n\n    #[Group('DDC-1657')]\n    public function testUpdateSchemaWithPostgreSQLSchema(): void\n    {\n        $sql = $this->getUpdateSchemaSqlForModels(\n            DDC1657Screen::class,\n            DDC1657Avatar::class,\n        );\n        $sql = array_filter($sql, static fn ($sql) => str_starts_with($sql, 'DROP SEQUENCE stonewood.'));\n\n        self::assertCount(0, $sql, implode(\"\\n\", $sql));\n    }\n\n    public function testSetDeferrableForeignKey(): void\n    {\n        $schema = $this->getSchemaForModels(\n            EntityWithSelfReferencingAssociation::class,\n        );\n\n        $table = $schema->getTable('entitywithselfreferencingassociation');\n        $fks   = array_values($table->getForeignKeys());\n\n        self::assertCount(1, $fks);\n\n        self::assertTrue($fks[0]->getOption('deferrable'));\n    }\n}\n\n#[Table(name: 'stonewood.screen')]\n#[Entity]\nclass DDC1657Screen\n{\n    /**\n     * Identifier\n     */\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    #[Column(name: 'pk', type: 'integer', nullable: false)]\n    private int $pk;\n\n    /**\n     * Title\n     */\n    #[Column(name: 'title', type: 'string', length: 255, nullable: false)]\n    private string $title;\n\n    /**\n     * Path\n     */\n    #[Column(name: 'path', type: 'string', length: 255, nullable: false)]\n    private string $path;\n\n    /**\n     * Register date\n     *\n     * @var Date\n     */\n    #[Column(name: 'ddate', type: 'date', nullable: false)]\n    private $ddate;\n\n    /**\n     * Avatar\n     *\n     * @var Stonewood\\Model\\Entity\\Avatar\n     */\n    #[ManyToOne(targetEntity: 'DDC1657Avatar')]\n    #[JoinColumn(name: 'pk_avatar', referencedColumnName: 'pk', nullable: true, onDelete: 'CASCADE')]\n    private $avatar;\n}\n\n#[Table(name: 'stonewood.avatar')]\n#[Entity]\nclass DDC1657Avatar\n{\n    /**\n     * Identifier\n     */\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    #[Column(name: 'pk', type: 'integer', nullable: false)]\n    private int $pk;\n}\n\n#[Table(name: 'entitywithselfreferencingassociation')]\n#[Entity]\nclass EntityWithSelfReferencingAssociation\n{\n    /**\n     * Identifier\n     */\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    #[Column(type: 'integer', nullable: false)]\n    private int $id;\n\n    #[ManyToOne(targetEntity: self::class)]\n    #[JoinColumn(deferrable: true)]\n    private self $parent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SchemaValidatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Exception as DBALException;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObjectType;\nuse Doctrine\\Tests\\DbalTypes\\NegativeToPositiveType;\nuse Doctrine\\Tests\\DbalTypes\\UpperCaseStringType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_keys;\nuse function constant;\nuse function implode;\n\n/**\n * Test the validity of all modelsets\n */\n#[Group('DDC-1601')]\nclass SchemaValidatorTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->registerType(CustomIdObjectType::class);\n        $this->registerType(UpperCaseStringType::class);\n        $this->registerType(NegativeToPositiveType::class);\n\n        parent::setUp();\n    }\n\n    /** @throws DBALException */\n    private function registerType(string $className): void\n    {\n        $type = constant($className . '::NAME');\n\n        if (DBALType::hasType($type)) {\n            DBALType::overrideType($type, $className);\n\n            return;\n        }\n\n        DBALType::addType($type, $className);\n    }\n\n    public static function dataValidateModelSets(): array\n    {\n        $modelSets = [];\n\n        foreach (array_keys(self::$modelSets) as $modelSet) {\n            $modelSets[$modelSet] = [$modelSet];\n        }\n\n        return $modelSets;\n    }\n\n    #[DataProvider('dataValidateModelSets')]\n    public function testValidateModelSets(string $modelSet): void\n    {\n        $validator = new SchemaValidator($this->_em);\n        $classes   = [];\n\n        foreach (self::$modelSets[$modelSet] as $className) {\n            $classes[] = $this->_em->getClassMetadata($className);\n        }\n\n        foreach ($classes as $class) {\n            $ce = $validator->validateClass($class);\n\n            self::assertEmpty($ce, 'Invalid Modelset: ' . $modelSet . ' class ' . $class->name . ': ' . implode(\"\\n\", $ce));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Flight;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheCompositePrimaryKeyTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testPutAndLoadCompositPrimaryKeyEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n        $this->evictRegions();\n\n        $leavingFromId = $this->cities[0]->getId();\n        $goingToId     = $this->cities[1]->getId();\n        $leavingFrom   = $this->_em->find(City::class, $leavingFromId);\n        $goingTo       = $this->_em->find(City::class, $goingToId);\n        $flight        = new Flight($leavingFrom, $goingTo);\n        $id            = [\n            'leavingFrom'   => $leavingFromId,\n            'goingTo'       => $goingToId,\n        ];\n\n        $flight->setDeparture(new DateTime('tomorrow'));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->_em->persist($flight);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Flight::class, $id));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $flight      = $this->_em->find(Flight::class, $id);\n        $leavingFrom = $flight->getLeavingFrom();\n        $goingTo     = $flight->getGoingTo();\n\n        self::assertInstanceOf(Flight::class, $flight);\n        self::assertInstanceOf(City::class, $goingTo);\n        self::assertInstanceOf(City::class, $leavingFrom);\n\n        self::assertEquals($goingTo->getId(), $goingToId);\n        self::assertEquals($leavingFrom->getId(), $leavingFromId);\n        $this->assertQueryCount(0);\n    }\n\n    public function testRemoveCompositPrimaryKeyEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n        $this->evictRegions();\n\n        $leavingFromId = $this->cities[0]->getId();\n        $goingToId     = $this->cities[1]->getId();\n        $leavingFrom   = $this->_em->find(City::class, $leavingFromId);\n        $goingTo       = $this->_em->find(City::class, $goingToId);\n        $flight        = new Flight($leavingFrom, $goingTo);\n        $id            = [\n            'leavingFrom'   => $leavingFromId,\n            'goingTo'       => $goingToId,\n        ];\n\n        $flight->setDeparture(new DateTime('tomorrow'));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->_em->persist($flight);\n        $this->_em->flush();\n\n        self::assertTrue($this->cache->containsEntity(Flight::class, $id));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->_em->remove($flight);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Flight::class, $id));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        self::assertNull($this->_em->find(Flight::class, $id));\n    }\n\n    public function testUpdateCompositPrimaryKeyEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n        $this->evictRegions();\n\n        $now           = new DateTime('now');\n        $tomorrow      = new DateTime('tomorrow');\n        $leavingFromId = $this->cities[0]->getId();\n        $goingToId     = $this->cities[1]->getId();\n        $leavingFrom   = $this->_em->find(City::class, $leavingFromId);\n        $goingTo       = $this->_em->find(City::class, $goingToId);\n        $flight        = new Flight($leavingFrom, $goingTo);\n        $id            = [\n            'leavingFrom'   => $leavingFromId,\n            'goingTo'       => $goingToId,\n        ];\n\n        $flight->setDeparture($now);\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->_em->persist($flight);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Flight::class, $id));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $flight      = $this->_em->find(Flight::class, $id);\n        $leavingFrom = $flight->getLeavingFrom();\n        $goingTo     = $flight->getGoingTo();\n\n        self::assertInstanceOf(Flight::class, $flight);\n        self::assertInstanceOf(City::class, $goingTo);\n        self::assertInstanceOf(City::class, $leavingFrom);\n\n        self::assertEquals($goingTo->getId(), $goingToId);\n        self::assertEquals($flight->getDeparture(), $now);\n        self::assertEquals($leavingFrom->getId(), $leavingFromId);\n        self::assertEquals($leavingFrom->getId(), $leavingFromId);\n        $this->assertQueryCount(0);\n\n        $flight->setDeparture($tomorrow);\n\n        $this->_em->persist($flight);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Flight::class, $id));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $flight      = $this->_em->find(Flight::class, $id);\n        $leavingFrom = $flight->getLeavingFrom();\n        $goingTo     = $flight->getGoingTo();\n\n        self::assertInstanceOf(Flight::class, $flight);\n        self::assertInstanceOf(City::class, $goingTo);\n        self::assertInstanceOf(City::class, $leavingFrom);\n\n        self::assertEquals($goingTo->getId(), $goingToId);\n        self::assertEquals($flight->getDeparture(), $tomorrow);\n        self::assertEquals($leavingFrom->getId(), $leavingFromId);\n        self::assertEquals($leavingFrom->getId(), $leavingFromId);\n        $this->assertQueryCount(0);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyWithAssociationsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1AlternateName;\nuse Doctrine\\Tests\\Models\\GeoNames\\Country;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass SecondLevelCacheCompositePrimaryKeyWithAssociationsTest extends OrmFunctionalTestCase\n{\n    /** @var Cache */\n    protected $cache;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n        $this->useModelSet('geonames');\n\n        parent::setUp();\n\n        $this->cache = $this->_em->getCache();\n\n        $it = new Country('IT', 'Italy');\n\n        $this->_em->persist($it);\n        $this->_em->flush();\n\n        $admin1 = new Admin1(1, 'Rome', $it);\n\n        $this->_em->persist($admin1);\n        $this->_em->flush();\n\n        $name1 = new Admin1AlternateName(1, 'Roma', $admin1);\n        $name2 = new Admin1AlternateName(2, 'Rome', $admin1);\n\n        $admin1->names[] = $name1;\n        $admin1->names[] = $name2;\n\n        $this->_em->persist($admin1);\n        $this->_em->persist($name1);\n        $this->_em->persist($name2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n        $this->evictRegions();\n    }\n\n    public function testFindByReturnsCachedEntity(): void\n    {\n        $admin1Repo = $this->_em->getRepository(Admin1::class);\n\n        $this->getQueryLog()->reset()->enable();\n\n        $admin1Rome = $admin1Repo->findOneBy(['country' => 'IT', 'id' => 1]);\n\n        self::assertEquals('Italy', $admin1Rome->country->name);\n        self::assertCount(2, $admin1Rome->names);\n        $this->assertQueryCount(3);\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $admin1Rome = $admin1Repo->findOneBy(['country' => 'IT', 'id' => 1]);\n\n        self::assertEquals('Italy', $admin1Rome->country->name);\n        self::assertCount(2, $admin1Rome->names);\n        $this->assertQueryCount(0);\n    }\n\n    private function evictRegions(): void\n    {\n        $this->cache->evictQueryRegions();\n        $this->cache->evictEntityRegions();\n        $this->cache->evictCollectionRegions();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheConcurrentTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache\\CollectionCacheKey;\nuse Doctrine\\ORM\\Cache\\DefaultCacheFactory;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Lock;\nuse Doctrine\\ORM\\Cache\\Region\\DefaultRegion;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Mocks\\ConcurrentRegionMock;\nuse Doctrine\\Tests\\Mocks\\TimestampRegionMock;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nuse function assert;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheConcurrentTest extends SecondLevelCacheFunctionalTestCase\n{\n    private CacheFactorySecondLevelCacheConcurrentTest $cacheFactory;\n    private ClassMetadata $countryMetadata;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->cacheFactory = new CacheFactorySecondLevelCacheConcurrentTest($this->getSharedSecondLevelCache());\n\n        $this->_em->getConfiguration()\n            ->getSecondLevelCacheConfiguration()\n            ->setCacheFactory($this->cacheFactory);\n\n        $this->countryMetadata = $this->_em->getClassMetadata(Country::class);\n        $countryMetadata       = clone $this->countryMetadata;\n\n        $countryMetadata->cache['usage'] = ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE;\n\n        $this->_em->getMetadataFactory()->setMetadataFor(Country::class, $countryMetadata);\n    }\n\n    protected function tearDown(): void\n    {\n        parent::tearDown();\n\n        $this->_em->getMetadataFactory()->setMetadataFor(Country::class, $this->countryMetadata);\n    }\n\n    public function testBasicConcurrentEntityReadLock(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        $countryId = $this->countries[0]->getId();\n        $cacheId   = new EntityCacheKey(Country::class, ['id' => $countryId]);\n        $region    = $this->_em->getCache()->getEntityCacheRegion(Country::class);\n        assert($region instanceof ConcurrentRegionMock);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $countryId));\n\n        $region->setLock($cacheId, Lock::createLockRead()); // another proc lock the entity cache\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $countryId));\n\n        $this->getQueryLog()->reset()->enable();\n        $country = $this->_em->find(Country::class, $countryId);\n\n        self::assertInstanceOf(Country::class, $country);\n        $this->assertQueryCount(1);\n        self::assertFalse($this->cache->containsEntity(Country::class, $countryId));\n    }\n\n    public function testBasicConcurrentCollectionReadLock(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n        $this->evictRegions();\n\n        $stateId = $this->states[0]->getId();\n        $state   = $this->_em->find(State::class, $stateId);\n\n        self::assertInstanceOf(State::class, $state);\n        self::assertInstanceOf(Country::class, $state->getCountry());\n        self::assertNotNull($state->getCountry()->getName());\n        self::assertCount(2, $state->getCities());\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $stateId = $this->states[0]->getId();\n        $cacheId = new CollectionCacheKey(State::class, 'cities', ['id' => $stateId]);\n        $region  = $this->_em->getCache()->getCollectionCacheRegion(State::class, 'cities');\n        assert($region instanceof ConcurrentRegionMock);\n\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $stateId));\n\n        $region->setLock($cacheId, Lock::createLockRead()); // another proc lock the entity cache\n\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $stateId));\n\n        $this->getQueryLog()->reset()->enable();\n        $state = $this->_em->find(State::class, $stateId);\n\n        self::assertEquals(0, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getHitCount());\n\n        self::assertEquals(0, $this->secondLevelCacheLogger->getRegionMissCount($this->getEntityRegion(State::class)));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n\n        self::assertInstanceOf(State::class, $state);\n        self::assertCount(2, $state->getCities());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getCollectionRegion(State::class, 'cities')));\n\n        $this->assertQueryCount(1);\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $stateId));\n    }\n}\n\nclass CacheFactorySecondLevelCacheConcurrentTest extends DefaultCacheFactory\n{\n    public function __construct(private CacheItemPoolInterface $cache)\n    {\n    }\n\n    public function getRegion(array $cache): ConcurrentRegionMock\n    {\n        $region = new DefaultRegion($cache['region'], $this->cache);\n\n        return new ConcurrentRegionMock($region);\n    }\n\n    public function getTimestampRegion(): TimestampRegionMock\n    {\n        return new TimestampRegionMock();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheCountQueriesTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionProperty;\n\nuse function array_diff;\nuse function array_filter;\nuse function file_exists;\nuse function rmdir;\nuse function scandir;\nuse function strpos;\nuse function sys_get_temp_dir;\n\nuse const DIRECTORY_SEPARATOR;\n\n/** @phpstan-type SupportedCacheUsage 0|ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE|ClassMetadata::CACHE_USAGE_READ_WRITE */\n#[Group('DDC-2183')]\nclass SecondLevelCacheCountQueriesTest extends SecondLevelCacheFunctionalTestCase\n{\n    /** @var string */\n    private $tmpDir;\n\n    protected function tearDown(): void\n    {\n        if ($this->tmpDir !== null && file_exists($this->tmpDir)) {\n            foreach (array_diff(scandir($this->tmpDir), ['.', '..']) as $f) {\n                rmdir($this->tmpDir . DIRECTORY_SEPARATOR . $f);\n            }\n\n            rmdir($this->tmpDir);\n        }\n\n        parent::tearDown();\n    }\n\n    /** @param SupportedCacheUsage $cacheUsage */\n    private function setupCountryModel(int $cacheUsage): void\n    {\n        $metadata = $this->_em->getClassMetaData(Country::class);\n\n        if ($cacheUsage === 0) {\n            $metadataCacheReflection = new ReflectionProperty(ClassMetadata::class, 'cache');\n            $metadataCacheReflection->setValue($metadata, null);\n\n            return;\n        }\n\n        if ($cacheUsage === ClassMetadata::CACHE_USAGE_READ_WRITE) {\n            $this->tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::class;\n            $this->secondLevelCacheFactory->setFileLockRegionDirectory($this->tmpDir);\n        }\n\n        $metadata->enableCache(['usage' => $cacheUsage]);\n    }\n\n    private function loadFixturesCountriesWithoutPostInsertIdentifier(): void\n    {\n        $metadata = $this->_em->getClassMetaData(Country::class);\n        $metadata->setIdGenerator(new AssignedGenerator());\n\n        $c1 = new Country('Brazil');\n        $c1->setId(10);\n        $c2 = new Country('Germany');\n        $c2->setId(20);\n\n        $this->countries[] = $c1;\n        $this->countries[] = $c2;\n\n        $this->_em->persist($c1);\n        $this->_em->persist($c2);\n        $this->_em->flush();\n    }\n\n    /** @param 'INSERT'|'UPDATE'|'DELETE' $type */\n    private function assertQueryCountByType(string $type, int $expectedCount): void\n    {\n        $queries = array_filter($this->getQueryLog()->queries, static function (array $entry) use ($type): bool {\n            return strpos($entry['sql'], $type) === 0;\n        });\n\n        self::assertCount($expectedCount, $queries);\n    }\n\n    /** @param SupportedCacheUsage $cacheUsage */\n    #[DataProvider('cacheUsageProvider')]\n    public function testInsertWithPostInsertIdentifier(int $cacheUsage): void\n    {\n        $this->setupCountryModel($cacheUsage);\n\n        self::assertQueryCountByType('INSERT', 0);\n\n        $this->loadFixturesCountries();\n\n        self::assertCount(2, $this->countries);\n        self::assertQueryCountByType('INSERT', 2);\n    }\n\n    /** @param SupportedCacheUsage $cacheUsage */\n    #[DataProvider('cacheUsageProvider')]\n    public function testInsertWithoutPostInsertIdentifier(int $cacheUsage): void\n    {\n        $this->setupCountryModel($cacheUsage);\n\n        self::assertQueryCountByType('INSERT', 0);\n\n        $this->loadFixturesCountriesWithoutPostInsertIdentifier();\n\n        self::assertCount(2, $this->countries);\n        self::assertQueryCountByType('INSERT', 2);\n    }\n\n    /** @param SupportedCacheUsage $cacheUsage */\n    #[DataProvider('cacheUsageProvider')]\n    public function testDelete(int $cacheUsage): void\n    {\n        $this->setupCountryModel($cacheUsage);\n        $this->loadFixturesCountries();\n\n        $c1 = $this->_em->find(Country::class, $this->countries[0]->getId());\n        $c2 = $this->_em->find(Country::class, $this->countries[1]->getId());\n\n        $this->_em->remove($c1);\n        $this->_em->remove($c2);\n        $this->_em->flush();\n\n        self::assertQueryCountByType('DELETE', 2);\n    }\n\n    /** @param SupportedCacheUsage $cacheUsage */\n    #[DataProvider('cacheUsageProvider')]\n    public function testUpdate(int $cacheUsage): void\n    {\n        $this->setupCountryModel($cacheUsage);\n        $this->loadFixturesCountries();\n\n        $c1 = $this->_em->find(Country::class, $this->countries[0]->getId());\n        $c2 = $this->_em->find(Country::class, $this->countries[1]->getId());\n\n        $c1->setName('Czech Republic');\n        $c2->setName('Hungary');\n\n        $this->_em->persist($c1);\n        $this->_em->persist($c2);\n        $this->_em->flush();\n\n        self::assertQueryCountByType('UPDATE', 2);\n    }\n\n    /** @return list<array{SupportedCacheUsage}> */\n    public static function cacheUsageProvider(): array\n    {\n        return [\n            [0],\n            [ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE],\n            [ClassMetadata::CACHE_USAGE_READ_WRITE],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheCriteriaTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testMatchingPut(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n        $name    = $this->countries[0]->getName();\n        $result1 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $name),\n        ));\n\n        // Because matching returns lazy collection, we force initialization\n        $result1->toArray();\n\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n\n        $this->_em->clear();\n\n        $result2 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $name),\n        ));\n\n        $this->assertQueryCount(1);\n        self::assertCount(1, $result2);\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n    }\n\n    public function testRepositoryMatching(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n        $result1 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $this->countries[0]->getName()),\n        ));\n\n        // Because matching returns lazy collection, we force initialization\n        $result1->toArray();\n\n        self::assertCount(1, $result1);\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n\n        $this->_em->clear();\n\n        $result2 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $this->countries[0]->getName()),\n        ));\n\n        // Because matching returns lazy collection, we force initialization\n        $result2->toArray();\n\n        $this->assertQueryCount(1);\n        self::assertCount(1, $result2);\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n\n        self::assertEquals($this->countries[0]->getId(), $result2[0]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result2[0]->getName());\n\n        $result3 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $this->countries[1]->getName()),\n        ));\n\n        // Because matching returns lazy collection, we force initialization\n        $result3->toArray();\n\n        $this->assertQueryCount(2);\n        self::assertCount(1, $result3);\n\n        self::assertInstanceOf(Country::class, $result3[0]);\n\n        self::assertEquals($this->countries[1]->getId(), $result3[0]->getId());\n        self::assertEquals($this->countries[1]->getName(), $result3[0]->getName());\n\n        $result4 = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $this->countries[1]->getName()),\n        ));\n\n        $this->assertQueryCount(2);\n        self::assertCount(1, $result4);\n\n        self::assertInstanceOf(Country::class, $result4[0]);\n\n        self::assertEquals($this->countries[1]->getId(), $result4[0]->getId());\n        self::assertEquals($this->countries[1]->getName(), $result4[0]->getName());\n    }\n\n    public function testCollectionMatching(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $entity   = $this->_em->find(State::class, $this->states[0]->getId());\n        $itemName = $this->states[0]->getCities()->get(0)->getName();\n        $this->getQueryLog()->reset()->enable();\n        $collection = $entity->getCities();\n        $matching   = $collection->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $itemName),\n        ));\n\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(Collection::class, $matching);\n        self::assertCount(1, $matching);\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(State::class, $this->states[0]->getId());\n        $this->getQueryLog()->reset()->enable();\n        $collection = $entity->getCities();\n        $matching   = $collection->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('name', $itemName),\n        ));\n\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(Collection::class, $matching);\n        self::assertCount(1, $matching);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheExtraLazyCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Travel;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheExtraLazyCollectionTest extends SecondLevelCacheFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $sourceEntity = $this->_em->getClassMetadata(Travel::class);\n        $targetEntity = $this->_em->getClassMetadata(City::class);\n\n        $sourceEntity->associationMappings['visitedCities']->fetch = ClassMetadata::FETCH_EXTRA_LAZY;\n        $targetEntity->associationMappings['travels']->fetch       = ClassMetadata::FETCH_EXTRA_LAZY;\n    }\n\n    public function tearDown(): void\n    {\n        parent::tearDown();\n\n        $sourceEntity = $this->_em->getClassMetadata(Travel::class);\n        $targetEntity = $this->_em->getClassMetadata(City::class);\n\n        $sourceEntity->associationMappings['visitedCities']->fetch = ClassMetadata::FETCH_LAZY;\n        $targetEntity->associationMappings['travels']->fetch       = ClassMetadata::FETCH_LAZY;\n    }\n\n    public function testCacheCountAfterAddThenFlush(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTraveler();\n        $this->loadFixturesTravels();\n\n        $this->_em->clear();\n\n        $ownerId = $this->travels[0]->getId();\n        $owner   = $this->_em->find(Travel::class, $ownerId);\n        $ref     = $this->_em->find(State::class, $this->states[1]->getId());\n\n        self::assertTrue($this->cache->containsEntity(Travel::class, $ownerId));\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $ownerId));\n\n        $newItem = new City('New City', $ref);\n        $owner->getVisitedCities()->add($newItem);\n\n        $this->_em->persist($newItem);\n        $this->_em->persist($owner);\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($owner->getVisitedCities()->isInitialized());\n        self::assertEquals(4, $owner->getVisitedCities()->count());\n        self::assertFalse($owner->getVisitedCities()->isInitialized());\n        $this->assertQueryCount(0);\n\n        $this->_em->flush();\n\n        self::assertFalse($owner->getVisitedCities()->isInitialized());\n        self::assertFalse($this->cache->containsCollection(Travel::class, 'visitedCities', $ownerId));\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $owner = $this->_em->find(Travel::class, $ownerId);\n\n        self::assertEquals(4, $owner->getVisitedCities()->count());\n        self::assertFalse($owner->getVisitedCities()->isInitialized());\n        $this->assertQueryCount(1);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheFunctionalTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\Tests\\Models\\Cache\\Address;\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionContactInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionLocationInfo;\nuse Doctrine\\Tests\\Models\\Cache\\Bar;\nuse Doctrine\\Tests\\Models\\Cache\\Beach;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\Person;\nuse Doctrine\\Tests\\Models\\Cache\\Restaurant;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Travel;\nuse Doctrine\\Tests\\Models\\Cache\\Traveler;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfile;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfileInfo;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nabstract class SecondLevelCacheFunctionalTestCase extends OrmFunctionalTestCase\n{\n    /** @phpstan-var list<Person> */\n    protected array $people = [];\n\n    /** @phpstan-var list<Address> */\n    protected array $addresses = [];\n\n    /** @phpstan-var list<Country> */\n    protected array $countries = [];\n\n    /** @phpstan-var list<State> */\n    protected array $states = [];\n\n    /** @phpstan-var list<City> */\n    protected array $cities = [];\n\n    /** @phpstan-var list<Travel> */\n    protected array $travels = [];\n\n    /** @phpstan-var list<Traveler> */\n    protected array $travelers = [];\n\n    /** @phpstan-var list<Attraction> */\n    protected array $attractions = [];\n\n    /** @phpstan-var list<AttractionInfo> */\n    protected array $attractionsInfo = [];\n\n    /** @phpstan-var list<TravelerProfile> */\n    protected array $travelersWithProfile = [];\n\n    protected Cache $cache;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        $this->useModelSet('cache');\n\n        parent::setUp();\n\n        $this->cache = $this->_em->getCache();\n    }\n\n    protected function loadFixturesCountries(): void\n    {\n        $brazil  = new Country('Brazil');\n        $germany = new Country('Germany');\n\n        $this->countries[] = $brazil;\n        $this->countries[] = $germany;\n\n        $this->_em->persist($brazil);\n        $this->_em->persist($germany);\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesStates(): void\n    {\n        $saopaulo = new State('São Paulo', $this->countries[0]);\n        $rio      = new State('Rio de janeiro', $this->countries[0]);\n        $berlin   = new State('Berlin', $this->countries[1]);\n        $bavaria  = new State('Bavaria', $this->countries[1]);\n\n        $this->states[] = $saopaulo;\n        $this->states[] = $rio;\n        $this->states[] = $bavaria;\n        $this->states[] = $berlin;\n\n        $this->_em->persist($saopaulo);\n        $this->_em->persist($rio);\n        $this->_em->persist($bavaria);\n        $this->_em->persist($berlin);\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesCities(): void\n    {\n        $saopaulo = new City('São Paulo', $this->states[0]);\n        $rio      = new City('Rio de janeiro', $this->states[0]);\n        $berlin   = new City('Berlin', $this->states[1]);\n        $munich   = new City('Munich', $this->states[1]);\n\n        $this->states[0]->addCity($saopaulo);\n        $this->states[0]->addCity($rio);\n        $this->states[1]->addCity($berlin);\n        $this->states[1]->addCity($berlin);\n\n        $this->cities[] = $saopaulo;\n        $this->cities[] = $rio;\n        $this->cities[] = $munich;\n        $this->cities[] = $berlin;\n\n        $this->_em->persist($saopaulo);\n        $this->_em->persist($rio);\n        $this->_em->persist($munich);\n        $this->_em->persist($berlin);\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesTraveler(): void\n    {\n        $t1 = new Traveler('Fabio Silva');\n        $t2 = new Traveler('Doctrine Bot');\n\n        $this->_em->persist($t1);\n        $this->_em->persist($t2);\n\n        $this->travelers[] = $t1;\n        $this->travelers[] = $t2;\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesTravelersWithProfile(): void\n    {\n        $t1 = new Traveler('Test traveler 1');\n        $t2 = new Traveler('Test traveler 2');\n        $p1 = new TravelerProfile('First Traveler Profile');\n        $p2 = new TravelerProfile('Second Traveler Profile');\n\n        $t1->setProfile($p1);\n        $t2->setProfile($p2);\n\n        $this->_em->persist($p1);\n        $this->_em->persist($p2);\n        $this->_em->persist($t1);\n        $this->_em->persist($t2);\n\n        $this->travelersWithProfile[] = $t1;\n        $this->travelersWithProfile[] = $t2;\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesTravelersProfileInfo(): void\n    {\n        $p1 = $this->travelersWithProfile[0]->getProfile();\n        $p2 = $this->travelersWithProfile[1]->getProfile();\n        $i1 = new TravelerProfileInfo($p1, 'First Profile Info ...');\n        $i2 = new TravelerProfileInfo($p2, 'Second  Profile Info ...');\n\n        $p1->setInfo($i1);\n        $p2->setInfo($i2);\n\n        $this->_em->persist($i1);\n        $this->_em->persist($i2);\n        $this->_em->persist($p1);\n        $this->_em->persist($p2);\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesTravels(): void\n    {\n        $t1 = new Travel($this->travelers[0]);\n        $t2 = new Travel($this->travelers[1]);\n        $t3 = new Travel($this->travelers[1]);\n\n        $t1->addVisitedCity($this->cities[0]);\n        $t1->addVisitedCity($this->cities[1]);\n        $t1->addVisitedCity($this->cities[2]);\n\n        $t2->addVisitedCity($this->cities[1]);\n        $t2->addVisitedCity($this->cities[3]);\n\n        $this->_em->persist($t1);\n        $this->_em->persist($t2);\n        $this->_em->persist($t3);\n\n        $this->travels[] = $t1;\n        $this->travels[] = $t2;\n        $this->travels[] = $t3;\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesAttractions(): void\n    {\n        $this->attractions[] = new Bar('Boteco São Bento', $this->cities[0]);\n        $this->attractions[] = new Bar('Prainha Paulista', $this->cities[0]);\n        $this->attractions[] = new Beach('Copacabana', $this->cities[1]);\n        $this->attractions[] = new Beach('Ipanema', $this->cities[1]);\n        $this->attractions[] = new Bar('Schneider Weisse', $this->cities[2]);\n        $this->attractions[] = new Restaurant('Reinstoff', $this->cities[3]);\n        $this->attractions[] = new Restaurant('Fischers Fritz', $this->cities[3]);\n\n        $this->cities[0]->addAttraction($this->attractions[0]);\n        $this->cities[0]->addAttraction($this->attractions[1]);\n        $this->cities[1]->addAttraction($this->attractions[2]);\n        $this->cities[1]->addAttraction($this->attractions[3]);\n        $this->cities[2]->addAttraction($this->attractions[4]);\n        $this->cities[3]->addAttraction($this->attractions[5]);\n        $this->cities[3]->addAttraction($this->attractions[6]);\n\n        foreach ($this->attractions as $attraction) {\n            $this->_em->persist($attraction);\n        }\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesAttractionsInfo(): void\n    {\n        $this->attractionsInfo[] = new AttractionContactInfo('0000-0000', $this->attractions[0]);\n        $this->attractionsInfo[] = new AttractionContactInfo('1111-1111', $this->attractions[1]);\n        $this->attractionsInfo[] = new AttractionLocationInfo('Some St 1', $this->attractions[2]);\n        $this->attractionsInfo[] = new AttractionLocationInfo('Some St 2', $this->attractions[3]);\n\n        foreach ($this->attractionsInfo as $info) {\n            $this->_em->persist($info);\n        }\n\n        $this->_em->flush();\n    }\n\n    protected function loadFixturesPersonWithAddress(): void\n    {\n        $person1  = new Person('Guilherme Blanco');\n        $address1 = new Address('Canada');\n\n        $person1->address = $address1;\n        $address1->person = $person1;\n\n        $person2  = new Person('Marco Pivetta');\n        $address2 = new Address('Germany');\n\n        $person2->address = $address2;\n        $address2->person = $person2;\n\n        $this->people[] = $person1;\n        $this->people[] = $person2;\n\n        $this->addresses[] = $address1;\n        $this->addresses[] = $address2;\n\n        foreach ($this->people as $person) {\n            $this->_em->persist($person);\n        }\n\n        foreach ($this->addresses as $address) {\n            $this->_em->persist($address);\n        }\n\n        $this->_em->flush();\n    }\n\n    protected function getEntityRegion(string $className): string\n    {\n        return $this->cache->getEntityCacheRegion($className)->getName();\n    }\n\n    protected function getCollectionRegion(string $className, string $association): string\n    {\n        return $this->cache->getCollectionCacheRegion($className, $association)->getName();\n    }\n\n    protected function getDefaultQueryRegionName(): string\n    {\n        return $this->cache->getQueryCache()->getRegion()->getName();\n    }\n\n    protected function evictRegions(): void\n    {\n        $this->cache->evictQueryRegions();\n        $this->cache->evictEntityRegions();\n        $this->cache->evictCollectionRegions();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheJoinTableInheritanceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionContactInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionLocationInfo;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheJoinTableInheritanceTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testUseSameRegion(): void\n    {\n        $infoRegion     = $this->cache->getEntityCacheRegion(AttractionInfo::class);\n        $contactRegion  = $this->cache->getEntityCacheRegion(AttractionContactInfo::class);\n        $locationRegion = $this->cache->getEntityCacheRegion(AttractionLocationInfo::class);\n\n        self::assertEquals($infoRegion->getName(), $contactRegion->getName());\n        self::assertEquals($infoRegion->getName(), $locationRegion->getName());\n    }\n\n    public function testPutOnPersistJoinTableInheritance(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $this->attractionsInfo[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $this->attractionsInfo[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $this->attractionsInfo[2]->getId()));\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $this->attractionsInfo[3]->getId()));\n    }\n\n    public function testJoinTableCountaisRootClass(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n\n        $this->_em->clear();\n\n        foreach ($this->attractionsInfo as $info) {\n            self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $info->getId()));\n            self::assertTrue($this->cache->containsEntity($info::class, $info->getId()));\n        }\n    }\n\n    public function testPutAndLoadJoinTableEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(AttractionInfo::class);\n\n        $entityId1 = $this->attractionsInfo[0]->getId();\n        $entityId2 = $this->attractionsInfo[1]->getId();\n\n        self::assertFalse($this->cache->containsEntity(AttractionInfo::class, $entityId1));\n        self::assertFalse($this->cache->containsEntity(AttractionInfo::class, $entityId2));\n        self::assertFalse($this->cache->containsEntity(AttractionContactInfo::class, $entityId1));\n        self::assertFalse($this->cache->containsEntity(AttractionContactInfo::class, $entityId2));\n\n        $this->getQueryLog()->reset()->enable();\n        $entity1 = $this->_em->find(AttractionInfo::class, $entityId1);\n        $entity2 = $this->_em->find(AttractionInfo::class, $entityId2);\n\n        //load entity and relation whit sub classes\n        $this->assertQueryCount(4);\n\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $entityId1));\n        self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $entityId2));\n        self::assertTrue($this->cache->containsEntity(AttractionContactInfo::class, $entityId1));\n        self::assertTrue($this->cache->containsEntity(AttractionContactInfo::class, $entityId2));\n\n        self::assertInstanceOf(AttractionInfo::class, $entity1);\n        self::assertInstanceOf(AttractionInfo::class, $entity2);\n        self::assertInstanceOf(AttractionContactInfo::class, $entity1);\n        self::assertInstanceOf(AttractionContactInfo::class, $entity2);\n\n        self::assertEquals($this->attractionsInfo[0]->getId(), $entity1->getId());\n        self::assertEquals($this->attractionsInfo[0]->getFone(), $entity1->getFone());\n\n        self::assertEquals($this->attractionsInfo[1]->getId(), $entity2->getId());\n        self::assertEquals($this->attractionsInfo[1]->getFone(), $entity2->getFone());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $entity3 = $this->_em->find(AttractionInfo::class, $entityId1);\n        $entity4 = $this->_em->find(AttractionInfo::class, $entityId2);\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(AttractionInfo::class, $entity3);\n        self::assertInstanceOf(AttractionInfo::class, $entity4);\n        self::assertInstanceOf(AttractionContactInfo::class, $entity3);\n        self::assertInstanceOf(AttractionContactInfo::class, $entity4);\n\n        self::assertNotSame($entity1, $entity3);\n        self::assertEquals($entity1->getId(), $entity3->getId());\n        self::assertEquals($entity1->getFone(), $entity3->getFone());\n\n        self::assertNotSame($entity2, $entity4);\n        self::assertEquals($entity2->getId(), $entity4->getId());\n        self::assertEquals($entity2->getFone(), $entity4->getFone());\n    }\n\n    public function testQueryCacheFindAllJoinTableEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT i, a FROM Doctrine\\Tests\\Models\\Cache\\AttractionInfo i JOIN i.attraction a';\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractionsInfo), $result1);\n        $this->assertQueryCount(1);\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractionsInfo), $result2);\n        $this->assertQueryCount(1);\n\n        foreach ($result2 as $entity) {\n            self::assertInstanceOf(AttractionInfo::class, $entity);\n        }\n    }\n\n    public function testOneToManyRelationJoinTable(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $entity = $this->_em->find(Attraction::class, $this->attractions[0]->getId());\n\n        self::assertInstanceOf(Attraction::class, $entity);\n        self::assertInstanceOf(PersistentCollection::class, $entity->getInfos());\n        self::assertCount(1, $entity->getInfos());\n\n        $ownerId = $this->attractions[0]->getId();\n\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $ownerId));\n        self::assertTrue($this->cache->containsCollection(Attraction::class, 'infos', $ownerId));\n\n        self::assertInstanceOf(AttractionContactInfo::class, $entity->getInfos()->get(0));\n        self::assertEquals($this->attractionsInfo[0]->getFone(), $entity->getInfos()->get(0)->getFone());\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(Attraction::class, $this->attractions[0]->getId());\n\n        self::assertInstanceOf(Attraction::class, $entity);\n        self::assertInstanceOf(PersistentCollection::class, $entity->getInfos());\n        self::assertCount(1, $entity->getInfos());\n\n        self::assertInstanceOf(AttractionContactInfo::class, $entity->getInfos()->get(0));\n        self::assertEquals($this->attractionsInfo[0]->getFone(), $entity->getInfos()->get(0)->getFone());\n    }\n\n    public function testQueryCacheShouldBeEvictedOnTimestampUpdate(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->loadFixturesAttractionsInfo();\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql = 'SELECT attractionInfo FROM Doctrine\\Tests\\Models\\Cache\\AttractionInfo attractionInfo';\n\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractionsInfo), $result1);\n        $this->assertQueryCount(5);\n\n        $contact = new AttractionContactInfo(\n            '1234-1234',\n            $this->_em->find(Attraction::class, $this->attractions[5]->getId()),\n        );\n\n        $this->_em->persist($contact);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractionsInfo) + 1, $result2);\n        $this->assertQueryCount(6);\n\n        foreach ($result2 as $entity) {\n            self::assertInstanceOf(AttractionInfo::class, $entity);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheManyToManyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Travel;\nuse Doctrine\\Tests\\Models\\Cache\\Traveler;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheManyToManyTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testShouldPutManyToManyCollectionOwningSideOnPersist(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTraveler();\n        $this->loadFixturesTravels();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Travel::class, $this->travels[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Travel::class, $this->travels[1]->getId()));\n\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[1]->getId()));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[2]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[3]->getId()));\n    }\n\n    public function testPutAndLoadManyToManyRelation(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTraveler();\n        $this->loadFixturesTravels();\n\n        $this->_em->clear();\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictEntityRegion(Travel::class);\n        $this->cache->evictCollectionRegion(Travel::class, 'visitedCities');\n\n        $this->secondLevelCacheLogger->clearStats();\n\n        self::assertFalse($this->cache->containsEntity(Travel::class, $this->travels[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Travel::class, $this->travels[1]->getId()));\n\n        self::assertFalse($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[0]->getId()));\n        self::assertFalse($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[1]->getId()));\n\n        self::assertFalse($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->cities[2]->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->cities[3]->getId()));\n\n        $t1 = $this->_em->find(Travel::class, $this->travels[0]->getId());\n        $t2 = $this->_em->find(Travel::class, $this->travels[1]->getId());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Travel::class)));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getEntityRegion(Travel::class)));\n\n        //trigger lazy load\n        self::assertCount(3, $t1->getVisitedCities());\n        self::assertCount(2, $t2->getVisitedCities());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(4, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getCollectionRegion(Travel::class, 'visitedCities')));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getCollectionRegion(Travel::class, 'visitedCities')));\n\n        self::assertInstanceOf(City::class, $t1->getVisitedCities()->get(0));\n        self::assertInstanceOf(City::class, $t1->getVisitedCities()->get(1));\n        self::assertInstanceOf(City::class, $t1->getVisitedCities()->get(2));\n\n        self::assertInstanceOf(City::class, $t2->getVisitedCities()->get(0));\n        self::assertInstanceOf(City::class, $t2->getVisitedCities()->get(1));\n\n        self::assertTrue($this->cache->containsEntity(Travel::class, $this->travels[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Travel::class, $this->travels[1]->getId()));\n\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[1]->getId()));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[2]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[3]->getId()));\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $t3 = $this->_em->find(Travel::class, $this->travels[0]->getId());\n        $t4 = $this->_em->find(Travel::class, $this->travels[1]->getId());\n\n        //trigger lazy load from cache\n        self::assertCount(3, $t3->getVisitedCities());\n        self::assertCount(2, $t4->getVisitedCities());\n\n        self::assertInstanceOf(City::class, $t3->getVisitedCities()->get(0));\n        self::assertInstanceOf(City::class, $t3->getVisitedCities()->get(1));\n        self::assertInstanceOf(City::class, $t3->getVisitedCities()->get(2));\n\n        self::assertInstanceOf(City::class, $t4->getVisitedCities()->get(0));\n        self::assertInstanceOf(City::class, $t4->getVisitedCities()->get(1));\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(Travel::class)));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(Travel::class, 'visitedCities')));\n\n        self::assertNotSame($t1->getVisitedCities()->get(0), $t3->getVisitedCities()->get(0));\n        self::assertEquals($t1->getVisitedCities()->get(0)->getId(), $t3->getVisitedCities()->get(0)->getId());\n        self::assertEquals($t1->getVisitedCities()->get(0)->getName(), $t3->getVisitedCities()->get(0)->getName());\n\n        self::assertNotSame($t1->getVisitedCities()->get(1), $t3->getVisitedCities()->get(1));\n        self::assertEquals($t1->getVisitedCities()->get(1)->getId(), $t3->getVisitedCities()->get(1)->getId());\n        self::assertEquals($t1->getVisitedCities()->get(1)->getName(), $t3->getVisitedCities()->get(1)->getName());\n\n        self::assertNotSame($t1->getVisitedCities()->get(2), $t3->getVisitedCities()->get(2));\n        self::assertEquals($t1->getVisitedCities()->get(2)->getId(), $t3->getVisitedCities()->get(2)->getId());\n        self::assertEquals($t1->getVisitedCities()->get(2)->getName(), $t3->getVisitedCities()->get(2)->getName());\n\n        self::assertNotSame($t2->getVisitedCities()->get(0), $t4->getVisitedCities()->get(0));\n        self::assertEquals($t2->getVisitedCities()->get(0)->getId(), $t4->getVisitedCities()->get(0)->getId());\n        self::assertEquals($t2->getVisitedCities()->get(0)->getName(), $t4->getVisitedCities()->get(0)->getName());\n\n        self::assertNotSame($t2->getVisitedCities()->get(1), $t4->getVisitedCities()->get(1));\n        self::assertEquals($t2->getVisitedCities()->get(1)->getId(), $t4->getVisitedCities()->get(1)->getId());\n        self::assertEquals($t2->getVisitedCities()->get(1)->getName(), $t4->getVisitedCities()->get(1)->getName());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getHitCount());\n        $this->assertQueryCount(0);\n    }\n\n    public function testStoreManyToManyAssociationWhitCascade(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictEntityRegion(Traveler::class);\n        $this->cache->evictEntityRegion(Travel::class);\n        $this->cache->evictCollectionRegion(State::class, 'cities');\n        $this->cache->evictCollectionRegion(Traveler::class, 'travels');\n\n        $traveler = new Traveler('Doctrine Bot');\n        $travel   = new Travel($traveler);\n\n        $travel->addVisitedCity($this->cities[0]);\n        $travel->addVisitedCity($this->cities[1]);\n        $travel->addVisitedCity($this->cities[3]);\n\n        $this->_em->persist($traveler);\n        $this->_em->persist($travel);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Travel::class, $travel->getId()));\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $traveler->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[3]->getId()));\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $travel->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $t1 = $this->_em->find(Travel::class, $travel->getId());\n\n        self::assertInstanceOf(Travel::class, $t1);\n        self::assertCount(3, $t1->getVisitedCities());\n        $this->assertQueryCount(0);\n    }\n\n    public function testReadOnlyCollection(): void\n    {\n        $this->expectException(CacheException::class);\n\n        $this->expectExceptionMessage('Cannot update a readonly collection \"Doctrine\\Tests\\Models\\Cache\\Travel#visitedCities');\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTraveler();\n        $this->loadFixturesTravels();\n\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Travel::class, $this->travels[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(Travel::class, 'visitedCities', $this->travels[0]->getId()));\n\n        $travel = $this->_em->find(Travel::class, $this->travels[0]->getId());\n\n        self::assertCount(3, $travel->getVisitedCities());\n\n        $travel->getVisitedCities()->remove(0);\n\n        $this->_em->persist($travel);\n        $this->_em->flush();\n    }\n\n    public function testManyToManyWithEmptyRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTraveler();\n        $this->loadFixturesTravels();\n        $this->_em->clear();\n\n        $this->evictRegions();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $entitiId = $this->travels[2]->getId(); //empty travel\n        $entity   = $this->_em->find(Travel::class, $entitiId);\n\n        self::assertEquals(0, $entity->getVisitedCities()->count());\n        $this->assertQueryCount(2);\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(Travel::class, $entitiId);\n\n        $this->getQueryLog()->reset()->enable();\n        self::assertEquals(0, $entity->getVisitedCities()->count());\n        $this->assertQueryCount(0);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Proxy\\Proxy as CommonProxy;\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\Tests\\Models\\Cache\\Action;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\ComplexAction;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Token;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheManyToOneTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testPutOnPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->states[0]->getCountry()->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->states[1]->getCountry()->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n    }\n\n    public function testPutAndLoadManyToOneRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictEntityRegion(Country::class);\n\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->states[0]->getCountry()->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->states[1]->getCountry()->getId()));\n\n        $c1 = $this->_em->find(State::class, $this->states[0]->getId());\n        $c2 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        //trigger lazy load\n        self::assertNotNull($c1->getCountry()->getName());\n        self::assertNotNull($c2->getCountry()->getName());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->states[0]->getCountry()->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->states[1]->getCountry()->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertInstanceOf(State::class, $c1);\n        self::assertInstanceOf(State::class, $c2);\n        self::assertInstanceOf(Country::class, $c1->getCountry());\n        self::assertInstanceOf(Country::class, $c2->getCountry());\n\n        self::assertEquals($this->states[0]->getId(), $c1->getId());\n        self::assertEquals($this->states[0]->getName(), $c1->getName());\n        self::assertEquals($this->states[0]->getCountry()->getId(), $c1->getCountry()->getId());\n        self::assertEquals($this->states[0]->getCountry()->getName(), $c1->getCountry()->getName());\n\n        self::assertEquals($this->states[1]->getId(), $c2->getId());\n        self::assertEquals($this->states[1]->getName(), $c2->getName());\n        self::assertEquals($this->states[1]->getCountry()->getId(), $c2->getCountry()->getId());\n        self::assertEquals($this->states[1]->getCountry()->getName(), $c2->getCountry()->getName());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $c3 = $this->_em->find(State::class, $this->states[0]->getId());\n        $c4 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        $this->assertQueryCount(0);\n\n        //trigger lazy load from cache\n        self::assertNotNull($c3->getCountry()->getName());\n        self::assertNotNull($c4->getCountry()->getName());\n\n        self::assertInstanceOf(State::class, $c3);\n        self::assertInstanceOf(State::class, $c4);\n        self::assertInstanceOf(Country::class, $c3->getCountry());\n        self::assertInstanceOf(Country::class, $c4->getCountry());\n\n        self::assertEquals($c1->getId(), $c3->getId());\n        self::assertEquals($c1->getName(), $c3->getName());\n\n        self::assertEquals($c2->getId(), $c4->getId());\n        self::assertEquals($c2->getName(), $c4->getName());\n\n        self::assertEquals($this->states[0]->getCountry()->getId(), $c3->getCountry()->getId());\n        self::assertEquals($this->states[0]->getCountry()->getName(), $c3->getCountry()->getName());\n\n        self::assertEquals($this->states[1]->getCountry()->getId(), $c4->getCountry()->getId());\n        self::assertEquals($this->states[1]->getCountry()->getName(), $c4->getCountry()->getName());\n    }\n\n    public function testInverseSidePutShouldEvictCollection(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictEntityRegion(Country::class);\n\n        //evict collection on add\n        $c3    = $this->_em->find(State::class, $this->states[0]->getId());\n        $prev  = $c3->getCities();\n        $count = $prev->count();\n        $city  = new City('Buenos Aires', $c3);\n\n        $c3->addCity($city);\n\n        $this->_em->persist($city);\n        $this->_em->persist($c3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $state = $this->_em->find(State::class, $c3->getId());\n        $this->getQueryLog()->reset()->enable();\n\n        // Association was cleared from EM\n        self::assertNotEquals($prev, $state->getCities());\n\n        // New association has one more item (cache was evicted)\n        self::assertEquals($count + 1, $state->getCities()->count());\n        $this->assertQueryCount(0);\n    }\n\n    public function testShouldNotReloadWhenAssociationIsMissing(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->_em->clear();\n\n        $stateId1 = $this->states[0]->getId();\n        $stateId2 = $this->states[3]->getId();\n\n        $countryId1 = $this->states[0]->getCountry()->getId();\n        $countryId2 = $this->states[3]->getCountry()->getId();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $countryId1));\n        self::assertTrue($this->cache->containsEntity(Country::class, $countryId2));\n        self::assertTrue($this->cache->containsEntity(State::class, $stateId1));\n        self::assertTrue($this->cache->containsEntity(State::class, $stateId2));\n\n        $this->cache->evictEntityRegion(Country::class);\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $countryId1));\n        self::assertFalse($this->cache->containsEntity(Country::class, $countryId2));\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $state1 = $this->_em->find(State::class, $stateId1);\n        $state2 = $this->_em->find(State::class, $stateId2);\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(State::class, $state1);\n        self::assertInstanceOf(State::class, $state2);\n        self::assertInstanceOf(Country::class, $state1->getCountry());\n        self::assertInstanceOf(Country::class, $state2->getCountry());\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertNotNull($state1->getCountry()->getName());\n        self::assertNotNull($state2->getCountry()->getName());\n        self::assertEquals($countryId1, $state1->getCountry()->getId());\n        self::assertEquals($countryId2, $state2->getCountry()->getId());\n\n        $this->assertQueryCount(2);\n    }\n\n    public function testPutAndLoadNonCacheableManyToOne(): void\n    {\n        self::assertNull($this->cache->getEntityCacheRegion(Action::class));\n        self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(Token::class));\n\n        $token  = new Token('token-hash');\n        $action = new Action('exec');\n        $action->addToken($token);\n\n        $this->_em->persist($token);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Token::class, $token->token));\n        self::assertFalse($this->cache->containsEntity(Token::class, $action->name));\n\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(Token::class, $token->token);\n\n        self::assertInstanceOf(Token::class, $entity);\n        self::assertEquals('token-hash', $entity->token);\n\n        self::assertInstanceOf(Action::class, $entity->getAction());\n        self::assertEquals('exec', $entity->getAction()->name);\n\n        $this->assertQueryCount(0);\n    }\n\n    public function testPutAndLoadNonCacheableCompositeManyToOne(): void\n    {\n        self::assertNull($this->cache->getEntityCacheRegion(Action::class));\n        self::assertNull($this->cache->getEntityCacheRegion(ComplexAction::class));\n        self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(Token::class));\n\n        $token = new Token('token-hash');\n\n        $action1 = new Action('login');\n        $action2 = new Action('logout');\n        $action3 = new Action('rememberme');\n\n        $complexAction = new ComplexAction($action1, $action3, 'login,rememberme');\n\n        $complexAction->addToken($token);\n\n        $token->action = $action2;\n\n        $this->_em->persist($token);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Token::class, $token->token));\n        self::assertFalse($this->cache->containsEntity(Action::class, $action1->name));\n        self::assertFalse($this->cache->containsEntity(Action::class, $action2->name));\n        self::assertFalse($this->cache->containsEntity(Action::class, $action3->name));\n\n        $this->getQueryLog()->reset()->enable();\n\n        $entity = $this->_em->find(Token::class, $token->token);\n\n        self::assertInstanceOf(Token::class, $entity);\n        self::assertEquals('token-hash', $entity->token);\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Action::class, $entity->getAction());\n        self::assertInstanceOf(ComplexAction::class, $entity->getComplexAction());\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Action::class, $entity->getComplexAction()->getAction1());\n        self::assertInstanceOf(Action::class, $entity->getComplexAction()->getAction2());\n        $expectedQueryCount = $entity->getAction() instanceof CommonProxy ? 1 : 0;\n        $this->assertQueryCount($expectedQueryCount);\n\n        self::assertEquals('login', $entity->getComplexAction()->getAction1()->name);\n        $this->assertQueryCount($expectedQueryCount);\n        self::assertEquals('rememberme', $entity->getComplexAction()->getAction2()->name);\n        $this->assertQueryCount($expectedQueryCount);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Login;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Token;\nuse Doctrine\\Tests\\Models\\Cache\\Travel;\nuse Doctrine\\Tests\\Models\\Cache\\Traveler;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function sprintf;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheOneToManyTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testShouldPutCollectionInverseSideOnPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $this->states[1]->getId()));\n    }\n\n    public function testPutAndLoadOneToManyRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictCollectionRegion(State::class, 'cities');\n\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $this->states[1]->getId()));\n\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(0)->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(1)->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[1]->getCities()->get(0)->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[1]->getCities()->get(1)->getId()));\n\n        $s1 = $this->_em->find(State::class, $this->states[0]->getId());\n        $s2 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getEntityRegion(State::class)));\n\n        //trigger lazy load\n        self::assertCount(2, $s1->getCities());\n        self::assertCount(2, $s2->getCities());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(4, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getCollectionRegion(State::class, 'cities')));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getCollectionRegion(State::class, 'cities')));\n\n        self::assertInstanceOf(City::class, $s1->getCities()->get(0));\n        self::assertInstanceOf(City::class, $s1->getCities()->get(1));\n\n        self::assertInstanceOf(City::class, $s2->getCities()->get(0));\n        self::assertInstanceOf(City::class, $s2->getCities()->get(1));\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $this->states[1]->getId()));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(0)->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(1)->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[1]->getCities()->get(0)->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[1]->getCities()->get(1)->getId()));\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $s3 = $this->_em->find(State::class, $this->states[0]->getId());\n        $s4 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        //trigger lazy load from cache\n        self::assertCount(2, $s3->getCities());\n        self::assertCount(2, $s4->getCities());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::class, 'cities')));\n\n        self::assertInstanceOf(City::class, $s3->getCities()->get(0));\n        self::assertInstanceOf(City::class, $s3->getCities()->get(1));\n        self::assertInstanceOf(City::class, $s4->getCities()->get(0));\n        self::assertInstanceOf(City::class, $s4->getCities()->get(1));\n\n        self::assertNotSame($s1->getCities()->get(0), $s3->getCities()->get(0));\n        self::assertEquals($s1->getCities()->get(0)->getId(), $s3->getCities()->get(0)->getId());\n        self::assertEquals($s1->getCities()->get(0)->getName(), $s3->getCities()->get(0)->getName());\n\n        self::assertNotSame($s1->getCities()->get(1), $s3->getCities()->get(1));\n        self::assertEquals($s1->getCities()->get(1)->getId(), $s3->getCities()->get(1)->getId());\n        self::assertEquals($s1->getCities()->get(1)->getName(), $s3->getCities()->get(1)->getName());\n\n        self::assertNotSame($s2->getCities()->get(0), $s4->getCities()->get(0));\n        self::assertEquals($s2->getCities()->get(0)->getId(), $s4->getCities()->get(0)->getId());\n        self::assertEquals($s2->getCities()->get(0)->getName(), $s4->getCities()->get(0)->getName());\n\n        self::assertNotSame($s2->getCities()->get(1), $s4->getCities()->get(1));\n        self::assertEquals($s2->getCities()->get(1)->getId(), $s4->getCities()->get(1)->getId());\n        self::assertEquals($s2->getCities()->get(1)->getName(), $s4->getCities()->get(1)->getName());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getHitCount());\n        $this->assertQueryCount(0);\n    }\n\n    public function testLoadOneToManyCollectionFromDatabaseWhenEntityMissing(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->_em->clear();\n\n        //trigger lazy load from database\n        self::assertCount(2, $this->_em->find(State::class, $this->states[0]->getId())->getCities());\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(0)->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(1)->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $stateId = $this->states[0]->getId();\n        $state   = $this->_em->find(State::class, $stateId);\n        $cityId  = $this->states[0]->getCities()->get(1)->getId();\n\n        //trigger lazy load from cache\n        self::assertCount(2, $state->getCities());\n        $this->assertQueryCount(0);\n        self::assertTrue($this->cache->containsEntity(City::class, $cityId));\n\n        $this->cache->evictEntity(City::class, $cityId);\n\n        self::assertFalse($this->cache->containsEntity(City::class, $cityId));\n        self::assertTrue($this->cache->containsEntity(State::class, $stateId));\n        self::assertTrue($this->cache->containsCollection(State::class, 'cities', $stateId));\n\n        $this->_em->clear();\n\n        $state = $this->_em->find(State::class, $stateId);\n\n        //trigger lazy load from database\n        self::assertCount(2, $state->getCities());\n        $this->assertQueryCount(1);\n    }\n\n    public function testShoudNotPutOneToManyRelationOnPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n\n        $state = new State('State Foo', $this->countries[0]);\n\n        $this->_em->persist($state);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(State::class, $state->getId()));\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $state->getId()));\n    }\n\n    public function testOneToManyRemove(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictCollectionRegion(State::class, 'cities');\n\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsCollection(State::class, 'cities', $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(0)->getId()));\n        self::assertFalse($this->cache->containsEntity(City::class, $this->states[0]->getCities()->get(1)->getId()));\n\n        $entity = $this->_em->find(State::class, $this->states[0]->getId());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getEntityRegion(State::class)));\n\n        //trigger lazy load\n        self::assertCount(2, $entity->getCities());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getCollectionRegion(State::class, 'cities')));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getCollectionRegion(State::class, 'cities')));\n\n        self::assertInstanceOf(City::class, $entity->getCities()->get(0));\n        self::assertInstanceOf(City::class, $entity->getCities()->get(1));\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n        $state = $this->_em->find(State::class, $this->states[0]->getId());\n\n        //trigger lazy load from cache\n        self::assertCount(2, $state->getCities());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::class, 'cities')));\n\n        $city0 = $state->getCities()->get(0);\n        $city1 = $state->getCities()->get(1);\n\n        self::assertInstanceOf(City::class, $city0);\n        self::assertInstanceOf(City::class, $city1);\n\n        self::assertEquals($entity->getCities()->get(0)->getName(), $city0->getName());\n        self::assertEquals($entity->getCities()->get(1)->getName(), $city1->getName());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        $this->assertQueryCount(0);\n\n        $state->getCities()->removeElement($city0);\n\n        $this->_em->remove($city0);\n        $this->_em->persist($state);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n        $state = $this->_em->find(State::class, $this->states[0]->getId());\n\n        //trigger lazy load from cache\n        self::assertCount(1, $state->getCities());\n\n        $city1 = $state->getCities()->get(0);\n        self::assertInstanceOf(City::class, $city1);\n        self::assertEquals($entity->getCities()->get(1)->getName(), $city1->getName());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::class, 'cities')));\n\n        $this->assertQueryCount(0);\n\n        $state->getCities()->remove(0);\n\n        $this->_em->remove($city1);\n        $this->_em->persist($state);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n        $state = $this->_em->find(State::class, $this->states[0]->getId());\n\n        self::assertCount(0, $state->getCities());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::class, 'cities')));\n    }\n\n    public function testOneToManyWithEmptyRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictCollectionRegion(State::class, 'cities');\n        $this->_em->clear();\n\n        $entitiId = $this->states[2]->getId(); // bavaria (cities count = 0)\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(State::class, $entitiId);\n\n        self::assertEquals(0, $entity->getCities()->count());\n        $this->assertQueryCount(2);\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(State::class, $entitiId);\n\n        self::assertEquals(0, $entity->getCities()->count());\n        $this->assertQueryCount(0);\n    }\n\n    public function testOneToManyCount(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictEntityRegion(State::class);\n        $this->cache->evictCollectionRegion(State::class, 'cities');\n        $this->_em->clear();\n\n        $entityId = $this->states[0]->getId();\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(State::class, $entityId);\n\n        self::assertEquals(2, $entity->getCities()->count());\n        $this->assertQueryCount(2);\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(State::class, $entityId);\n\n        self::assertEquals(2, $entity->getCities()->count());\n        $this->assertQueryCount(0);\n    }\n\n    public function testCacheInitializeCollectionWithNewObjects(): void\n    {\n        $this->_em->clear();\n\n        $this->evictRegions();\n\n        $traveler = new Traveler('Doctrine Bot');\n\n        for ($i = 0; $i < 3; ++$i) {\n            $traveler->getTravels()->add(new Travel($traveler));\n        }\n\n        $this->_em->persist($traveler);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertCount(3, $traveler->getTravels());\n\n        $travelerId = $traveler->getId();\n        $this->getQueryLog()->reset()->enable();\n        $entity = $this->_em->find(Traveler::class, $travelerId);\n\n        $this->assertQueryCount(0);\n        self::assertFalse($entity->getTravels()->isInitialized());\n\n        $newItem = new Travel($entity);\n        $entity->getTravels()->add($newItem);\n\n        self::assertFalse($entity->getTravels()->isInitialized());\n        self::assertCount(4, $entity->getTravels());\n        self::assertTrue($entity->getTravels()->isInitialized());\n        $this->assertQueryCount(0);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = sprintf(\n            'SELECT t, tt FROM Doctrine\\Tests\\Models\\Cache\\Traveler t JOIN t.travels tt WHERE t.id = %s',\n            $travelerId,\n        );\n        $result = $this->_em->createQuery($query)->getSingleResult();\n\n        self::assertEquals(4, $result->getTravels()->count());\n    }\n\n    public function testPutAndLoadNonCacheableOneToMany(): void\n    {\n        self::assertNull($this->cache->getEntityCacheRegion(Login::class));\n        self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(Token::class));\n\n        $l1    = new Login('session1');\n        $l2    = new Login('session2');\n        $token = new Token('token-hash');\n        $token->addLogin($l1);\n        $token->addLogin($l2);\n\n        $this->_em->persist($token);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Token::class, $token->token));\n\n        $this->getQueryLog()->reset()->enable();\n\n        $entity = $this->_em->find(Token::class, $token->token);\n\n        self::assertInstanceOf(Token::class, $entity);\n        self::assertEquals('token-hash', $entity->token);\n        $this->assertQueryCount(0);\n\n        self::assertCount(2, $entity->logins);\n        $this->assertQueryCount(1);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Cache\\Region;\nuse Doctrine\\Tests\\Models\\Cache\\Address;\nuse Doctrine\\Tests\\Models\\Cache\\Client;\nuse Doctrine\\Tests\\Models\\Cache\\Person;\nuse Doctrine\\Tests\\Models\\Cache\\Token;\nuse Doctrine\\Tests\\Models\\Cache\\Traveler;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfile;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfileInfo;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheOneToOneTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testPutOneToOneOnUnidirectionalPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTravelersWithProfile();\n\n        $this->_em->clear();\n\n        $entity1 = $this->travelersWithProfile[0];\n        $entity2 = $this->travelersWithProfile[1];\n\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity1->getId()));\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity2->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity2->getProfile()->getId()));\n    }\n\n    public function testPutOneToOneOnBidirectionalPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTravelersWithProfile();\n        $this->loadFixturesTravelersProfileInfo();\n\n        $this->_em->clear();\n\n        $entity1 = $this->travelersWithProfile[0];\n        $entity2 = $this->travelersWithProfile[1];\n\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity1->getId()));\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity2->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity2->getProfile()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfileInfo::class, $entity1->getProfile()->getInfo()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfileInfo::class, $entity2->getProfile()->getInfo()->getId()));\n    }\n\n    public function testPutAndLoadOneToOneUnidirectionalRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTravelersWithProfile();\n        $this->loadFixturesTravelersProfileInfo();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(Traveler::class);\n        $this->cache->evictEntityRegion(TravelerProfile::class);\n\n        $entity1 = $this->travelersWithProfile[0];\n        $entity2 = $this->travelersWithProfile[1];\n\n        self::assertFalse($this->cache->containsEntity(Traveler::class, $entity1->getId()));\n        self::assertFalse($this->cache->containsEntity(Traveler::class, $entity2->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity2->getProfile()->getId()));\n\n        $t1 = $this->_em->find(Traveler::class, $entity1->getId());\n        $t2 = $this->_em->find(Traveler::class, $entity2->getId());\n\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity1->getId()));\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity2->getId()));\n        // The inverse side its not cached\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity2->getProfile()->getId()));\n\n        self::assertInstanceOf(Traveler::class, $t1);\n        self::assertInstanceOf(Traveler::class, $t2);\n        self::assertInstanceOf(TravelerProfile::class, $t1->getProfile());\n        self::assertInstanceOf(TravelerProfile::class, $t2->getProfile());\n\n        self::assertEquals($entity1->getId(), $t1->getId());\n        self::assertEquals($entity1->getName(), $t1->getName());\n        self::assertEquals($entity1->getProfile()->getId(), $t1->getProfile()->getId());\n        self::assertEquals($entity1->getProfile()->getName(), $t1->getProfile()->getName());\n\n        self::assertEquals($entity2->getId(), $t2->getId());\n        self::assertEquals($entity2->getName(), $t2->getName());\n        self::assertEquals($entity2->getProfile()->getId(), $t2->getProfile()->getId());\n        self::assertEquals($entity2->getProfile()->getName(), $t2->getProfile()->getName());\n\n        // its all cached now\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity1->getId()));\n        self::assertTrue($this->cache->containsEntity(Traveler::class, $entity2->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity1->getProfile()->getId()));\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        // load from cache\n        $t3 = $this->_em->find(Traveler::class, $entity1->getId());\n        $t4 = $this->_em->find(Traveler::class, $entity2->getId());\n\n        self::assertInstanceOf(Traveler::class, $t3);\n        self::assertInstanceOf(Traveler::class, $t4);\n        self::assertInstanceOf(TravelerProfile::class, $t3->getProfile());\n        self::assertInstanceOf(TravelerProfile::class, $t4->getProfile());\n\n        self::assertEquals($entity1->getProfile()->getId(), $t3->getProfile()->getId());\n        self::assertEquals($entity2->getProfile()->getId(), $t4->getProfile()->getId());\n\n        self::assertEquals($entity1->getProfile()->getName(), $t3->getProfile()->getName());\n        self::assertEquals($entity2->getProfile()->getName(), $t4->getProfile()->getName());\n\n        $this->assertQueryCount(0);\n    }\n\n    public function testPutAndLoadOneToOneBidirectionalRelation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesTravelersWithProfile();\n        $this->loadFixturesTravelersProfileInfo();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(Traveler::class);\n        $this->cache->evictEntityRegion(TravelerProfile::class);\n        $this->cache->evictEntityRegion(TravelerProfileInfo::class);\n\n        $entity1 = $this->travelersWithProfile[0]->getProfile();\n        $entity2 = $this->travelersWithProfile[1]->getProfile();\n\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity1->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfile::class, $entity2->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfileInfo::class, $entity1->getInfo()->getId()));\n        self::assertFalse($this->cache->containsEntity(TravelerProfileInfo::class, $entity2->getInfo()->getId()));\n\n        $p1 = $this->_em->find(TravelerProfile::class, $entity1->getId());\n        $p2 = $this->_em->find(TravelerProfile::class, $entity2->getId());\n\n        self::assertEquals($entity1->getId(), $p1->getId());\n        self::assertEquals($entity1->getName(), $p1->getName());\n        self::assertEquals($entity1->getInfo()->getId(), $p1->getInfo()->getId());\n        self::assertEquals($entity1->getInfo()->getDescription(), $p1->getInfo()->getDescription());\n\n        self::assertEquals($entity2->getId(), $p2->getId());\n        self::assertEquals($entity2->getName(), $p2->getName());\n        self::assertEquals($entity2->getInfo()->getId(), $p2->getInfo()->getId());\n        self::assertEquals($entity2->getInfo()->getDescription(), $p2->getInfo()->getDescription());\n\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity1->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfile::class, $entity2->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfileInfo::class, $entity1->getInfo()->getId()));\n        self::assertTrue($this->cache->containsEntity(TravelerProfileInfo::class, $entity2->getInfo()->getId()));\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $p3 = $this->_em->find(TravelerProfile::class, $entity1->getId());\n        $p4 = $this->_em->find(TravelerProfile::class, $entity2->getId());\n\n        self::assertInstanceOf(TravelerProfile::class, $p3);\n        self::assertInstanceOf(TravelerProfile::class, $p4);\n        self::assertInstanceOf(TravelerProfileInfo::class, $p3->getInfo());\n        self::assertInstanceOf(TravelerProfileInfo::class, $p4->getInfo());\n\n        self::assertEquals($entity1->getId(), $p3->getId());\n        self::assertEquals($entity1->getName(), $p3->getName());\n        self::assertEquals($entity1->getInfo()->getId(), $p3->getInfo()->getId());\n        self::assertEquals($entity1->getInfo()->getDescription(), $p3->getInfo()->getDescription());\n\n        self::assertEquals($entity2->getId(), $p4->getId());\n        self::assertEquals($entity2->getName(), $p4->getName());\n        self::assertEquals($entity2->getInfo()->getId(), $p4->getInfo()->getId());\n        self::assertEquals($entity2->getInfo()->getDescription(), $p4->getInfo()->getDescription());\n\n        $this->assertQueryCount(0);\n    }\n\n    public function testInverseSidePutAndLoadOneToOneBidirectionalRelation(): void\n    {\n        $this->loadFixturesPersonWithAddress();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(Person::class);\n        $this->cache->evictEntityRegion(Address::class);\n\n        $entity1 = $this->addresses[0]->person;\n        $entity2 = $this->addresses[1]->person;\n\n        self::assertFalse($this->cache->containsEntity(Person::class, $entity1->id));\n        self::assertFalse($this->cache->containsEntity(Person::class, $entity2->id));\n        self::assertFalse($this->cache->containsEntity(Address::class, $entity1->address->id));\n        self::assertFalse($this->cache->containsEntity(Address::class, $entity2->address->id));\n\n        $p1 = $this->_em->find(Person::class, $entity1->id);\n        $p2 = $this->_em->find(Person::class, $entity2->id);\n\n        self::assertEquals($entity1->id, $p1->id);\n        self::assertEquals($entity1->name, $p1->name);\n        self::assertEquals($entity1->address->id, $p1->address->id);\n        self::assertEquals($entity1->address->location, $p1->address->location);\n\n        self::assertEquals($entity2->id, $p2->id);\n        self::assertEquals($entity2->name, $p2->name);\n        self::assertEquals($entity2->address->id, $p2->address->id);\n        self::assertEquals($entity2->address->location, $p2->address->location);\n\n        self::assertTrue($this->cache->containsEntity(Person::class, $entity1->id));\n        self::assertTrue($this->cache->containsEntity(Person::class, $entity2->id));\n        // The inverse side its not cached\n        self::assertFalse($this->cache->containsEntity(Address::class, $entity1->address->id));\n        self::assertFalse($this->cache->containsEntity(Address::class, $entity2->address->id));\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $p3 = $this->_em->find(Person::class, $entity1->id);\n        $p4 = $this->_em->find(Person::class, $entity2->id);\n\n        self::assertInstanceOf(Person::class, $p3);\n        self::assertInstanceOf(Person::class, $p4);\n        self::assertInstanceOf(Address::class, $p3->address);\n        self::assertInstanceOf(Address::class, $p4->address);\n\n        self::assertEquals($entity1->id, $p3->id);\n        self::assertEquals($entity1->name, $p3->name);\n        self::assertEquals($entity1->address->id, $p3->address->id);\n        self::assertEquals($entity1->address->location, $p3->address->location);\n\n        self::assertEquals($entity2->id, $p4->id);\n        self::assertEquals($entity2->name, $p4->name);\n        self::assertEquals($entity2->address->id, $p4->address->id);\n        self::assertEquals($entity2->address->location, $p4->address->location);\n\n        $this->assertQueryCount(2);\n    }\n\n    public function testPutAndLoadNonCacheableOneToOne(): void\n    {\n        self::assertNull($this->cache->getEntityCacheRegion(Client::class));\n        self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(Token::class));\n\n        $client = new Client('FabioBatSilva');\n        $token  = new Token('token-hash', $client);\n\n        $this->_em->persist($client);\n        $this->_em->persist($token);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($this->cache->containsEntity(Token::class, $token->token));\n        self::assertFalse($this->cache->containsEntity(Client::class, $client->id));\n\n        $entity = $this->_em->find(Token::class, $token->token);\n\n        self::assertInstanceOf(Token::class, $entity);\n        self::assertInstanceOf(Client::class, $entity->getClient());\n        self::assertEquals('token-hash', $entity->token);\n        $this->assertQueryCount(0);\n\n        self::assertEquals('FabioBatSilva', $entity->getClient()->name);\n        $this->assertQueryCount(1);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Cache\\EntityCacheEntry;\nuse Doctrine\\ORM\\Cache\\EntityCacheKey;\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\nuse Doctrine\\ORM\\Cache\\QueryCacheEntry;\nuse Doctrine\\ORM\\Cache\\QueryCacheKey;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionMethod;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheQueryCacheTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testBasicQueryCache(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result1 = $this->_em->createQuery($dql)->setCacheable(true)->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[1]->getId(), $result1[1]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n        self::assertEquals($this->countries[1]->getName(), $result1[1]->getName());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertCount(2, $result2);\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n        self::assertInstanceOf(Country::class, $result2[1]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[1]->getId(), $result2[1]->getId());\n\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n        self::assertEquals($result1[1]->getName(), $result2[1]->getName());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n    }\n\n    public function testQueryCacheModeGet(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $dql      = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $queryGet = $this->_em->createQuery($dql)\n            ->setCacheMode(Cache::MODE_GET)\n            ->setCacheable(true);\n\n        // MODE_GET should never add items to the cache.\n        self::assertCount(2, $queryGet->getResult());\n        $this->assertQueryCount(1);\n\n        self::assertCount(2, $queryGet->getResult());\n        $this->assertQueryCount(2);\n\n        $result = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(2, $result);\n        $this->assertQueryCount(3);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        // MODE_GET should read items if exists.\n        self::assertCount(2, $queryGet->getResult());\n        $this->assertQueryCount(3);\n    }\n\n    public function testQueryCacheModePut(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $dql    = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertCount(2, $result);\n        $this->assertQueryCount(1);\n\n        $queryPut = $this->_em->createQuery($dql)\n            ->setCacheMode(Cache::MODE_PUT)\n            ->setCacheable(true);\n\n        // MODE_PUT should never read itens from cache.\n        self::assertCount(2, $queryPut->getResult());\n        $this->assertQueryCount(2);\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertCount(2, $queryPut->getResult());\n        $this->assertQueryCount(3);\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n    }\n\n    public function testQueryCacheModeRefresh(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $region = $this->cache->getEntityCacheRegion(Country::class);\n        $this->getQueryLog()->reset()->enable();\n        $dql    = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertCount(2, $result);\n        $this->assertQueryCount(1);\n\n        $countryId1   = $this->countries[0]->getId();\n        $countryId2   = $this->countries[1]->getId();\n        $countryName1 = $this->countries[0]->getName();\n        $countryName2 = $this->countries[1]->getName();\n\n        $key1   = new EntityCacheKey(Country::class, ['id' => $countryId1]);\n        $key2   = new EntityCacheKey(Country::class, ['id' => $countryId2]);\n        $entry1 = new EntityCacheEntry(Country::class, ['id' => $countryId1, 'name' => 'outdated']);\n        $entry2 = new EntityCacheEntry(Country::class, ['id' => $countryId2, 'name' => 'outdated']);\n\n        $region->put($key1, $entry1);\n        $region->put($key2, $entry2);\n        $this->_em->clear();\n\n        $queryRefresh = $this->_em->createQuery($dql)\n            ->setCacheMode(Cache::MODE_REFRESH)\n            ->setCacheable(true);\n\n        // MODE_REFRESH should never read itens from cache.\n        $result1 = $queryRefresh->getResult();\n        self::assertCount(2, $result1);\n        self::assertEquals($countryName1, $result1[0]->getName());\n        self::assertEquals($countryName2, $result1[1]->getName());\n        $this->assertQueryCount(2);\n\n        $this->_em->clear();\n\n        $result2 = $queryRefresh->getResult();\n        self::assertCount(2, $result2);\n        self::assertEquals($countryName1, $result2[0]->getName());\n        self::assertEquals($countryName2, $result2[1]->getName());\n        $this->assertQueryCount(3);\n    }\n\n    public function testBasicQueryCachePutEntityCache(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result1 = $this->_em->createQuery($dql)->setCacheable(true)->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[1]->getId(), $result1[1]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n        self::assertEquals($this->countries[1]->getName(), $result1[1]->getName());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertEquals(3, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertCount(2, $result2);\n\n        self::assertEquals(3, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n        self::assertInstanceOf(Country::class, $result2[1]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[1]->getId(), $result2[1]->getId());\n\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n        self::assertEquals($result1[1]->getName(), $result2[1]->getName());\n\n        self::assertEquals(3, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n    }\n\n    #[Group('5854')]\n    public function testMultipleNestedDQLAliases(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $queryRegionName      = $this->getDefaultQueryRegionName();\n        $cityRegionName       = $this->getEntityRegion(City::class);\n        $stateRegionName      = $this->getEntityRegion(State::class);\n        $attractionRegionName = $this->getEntityRegion(Attraction::class);\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT s, c, a FROM Doctrine\\Tests\\Models\\Cache\\State s JOIN s.cities c JOIN c.attractions a';\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[2]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $this->cities[3]->getId()));\n\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $this->attractions[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $this->attractions[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $this->attractions[2]->getId()));\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $this->attractions[3]->getId()));\n\n        self::assertInstanceOf(State::class, $result1[0]);\n        self::assertInstanceOf(State::class, $result1[1]);\n\n        self::assertCount(2, $result1[0]->getCities());\n        self::assertCount(2, $result1[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(1));\n\n        self::assertCount(2, $result1[0]->getCities()->get(0)->getAttractions());\n        self::assertCount(2, $result1[0]->getCities()->get(1)->getAttractions());\n        self::assertCount(2, $result1[1]->getCities()->get(0)->getAttractions());\n        self::assertCount(1, $result1[1]->getCities()->get(1)->getAttractions());\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(2, $result2);\n        $this->assertQueryCount(1);\n\n        self::assertInstanceOf(State::class, $result2[0]);\n        self::assertInstanceOf(State::class, $result2[1]);\n\n        self::assertCount(2, $result2[0]->getCities());\n        self::assertCount(2, $result2[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(1));\n\n        self::assertCount(2, $result2[0]->getCities()->get(0)->getAttractions());\n        self::assertCount(2, $result2[0]->getCities()->get(1)->getAttractions());\n        self::assertCount(2, $result2[1]->getCities()->get(0)->getAttractions());\n        self::assertCount(1, $result2[1]->getCities()->get(1)->getAttractions());\n    }\n\n    public function testBasicQueryParams(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $name    = $this->countries[0]->getName();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c WHERE c.name = :name';\n        $result1 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->setParameter('name', $name)\n                ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)->setCacheable(true)\n                ->setParameter('name', $name)\n                ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertCount(1, $result2);\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n    }\n\n    public function testLoadFromDatabaseWhenEntityMissing(): void\n    {\n        $this->evictRegions();\n\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result1 = $this->_em->createQuery($dql)->setCacheable(true)->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[1]->getId(), $result1[1]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n        self::assertEquals($this->countries[1]->getName(), $result1[1]->getName());\n\n        self::assertEquals(3, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        $this->cache->evictEntity(Country::class, $result1[0]->getId());\n        self::assertFalse($this->cache->containsEntity(Country::class, $result1[0]->getId()));\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        $this->assertQueryCount(2);\n        self::assertCount(2, $result2);\n\n        self::assertEquals(5, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n        self::assertInstanceOf(Country::class, $result2[1]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[1]->getId(), $result2[1]->getId());\n\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n        self::assertEquals($result1[1]->getName(), $result2[1]->getName());\n\n        $this->assertQueryCount(2);\n    }\n\n    public function testBasicQueryFetchJoinsOneToMany(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT s, c FROM Doctrine\\Tests\\Models\\Cache\\State s JOIN s.cities c';\n        $result1 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(State::class, $result1[0]);\n        self::assertInstanceOf(State::class, $result1[1]);\n        self::assertCount(2, $result1[0]->getCities());\n        self::assertCount(2, $result1[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(1));\n\n        self::assertNotNull($result1[0]->getCities()->get(0)->getId());\n        self::assertNotNull($result1[0]->getCities()->get(1)->getId());\n        self::assertNotNull($result1[1]->getCities()->get(0)->getId());\n        self::assertNotNull($result1[1]->getCities()->get(1)->getId());\n\n        self::assertNotNull($result1[0]->getCities()->get(0)->getName());\n        self::assertNotNull($result1[0]->getCities()->get(1)->getName());\n        self::assertNotNull($result1[1]->getCities()->get(0)->getName());\n        self::assertNotNull($result1[1]->getCities()->get(1)->getName());\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertInstanceOf(State::class, $result2[0]);\n        self::assertInstanceOf(State::class, $result2[1]);\n        self::assertCount(2, $result2[0]->getCities());\n        self::assertCount(2, $result2[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(1));\n\n        self::assertNotNull($result2[0]->getCities()->get(0)->getId());\n        self::assertNotNull($result2[0]->getCities()->get(1)->getId());\n        self::assertNotNull($result2[1]->getCities()->get(0)->getId());\n        self::assertNotNull($result2[1]->getCities()->get(1)->getId());\n\n        self::assertNotNull($result2[0]->getCities()->get(0)->getName());\n        self::assertNotNull($result2[0]->getCities()->get(1)->getName());\n        self::assertNotNull($result2[1]->getCities()->get(0)->getName());\n        self::assertNotNull($result2[1]->getCities()->get(1)->getName());\n\n        $this->assertQueryCount(1);\n    }\n\n    public function testBasicQueryFetchJoinsManyToOne(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->_em->clear();\n\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c, s FROM Doctrine\\Tests\\Models\\Cache\\City c JOIN c.state s';\n        $result1 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertCount(4, $result1);\n        self::assertInstanceOf(City::class, $result1[0]);\n        self::assertInstanceOf(City::class, $result1[1]);\n        self::assertInstanceOf(State::class, $result1[0]->getState());\n        self::assertInstanceOf(State::class, $result1[1]->getState());\n\n        self::assertTrue($this->cache->containsEntity(City::class, $result1[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $result1[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $result1[0]->getState()->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $result1[1]->getState()->getId()));\n\n        self::assertEquals(7, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n        self::assertEquals(4, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(City::class)));\n\n        $this->assertQueryCount(1);\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $result2 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertCount(4, $result1);\n        self::assertInstanceOf(City::class, $result2[0]);\n        self::assertInstanceOf(City::class, $result2[1]);\n        self::assertInstanceOf(State::class, $result2[0]->getState());\n        self::assertInstanceOf(State::class, $result2[1]->getState());\n\n        self::assertNotNull($result2[0]->getId());\n        self::assertNotNull($result2[0]->getId());\n        self::assertNotNull($result2[1]->getState()->getId());\n        self::assertNotNull($result2[1]->getState()->getId());\n\n        self::assertNotNull($result2[0]->getName());\n        self::assertNotNull($result2[0]->getName());\n        self::assertNotNull($result2[1]->getState()->getName());\n        self::assertNotNull($result2[1]->getState()->getName());\n\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n        self::assertEquals($result1[1]->getName(), $result2[1]->getName());\n        self::assertEquals($result1[0]->getState()->getName(), $result2[0]->getState()->getName());\n        self::assertEquals($result1[1]->getState()->getName(), $result2[1]->getState()->getName());\n\n        $this->assertQueryCount(1);\n    }\n\n    public function testReloadQueryIfToOneIsNotFound(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->_em->clear();\n\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c, s FROM Doctrine\\Tests\\Models\\Cache\\City c JOIN c.state s';\n        $result1 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertCount(4, $result1);\n        self::assertInstanceOf(City::class, $result1[0]);\n        self::assertInstanceOf(City::class, $result1[1]);\n        self::assertInstanceOf(State::class, $result1[0]->getState());\n        self::assertInstanceOf(State::class, $result1[1]->getState());\n\n        self::assertTrue($this->cache->containsEntity(City::class, $result1[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(City::class, $result1[1]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $result1[0]->getState()->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $result1[1]->getState()->getId()));\n        $this->assertQueryCount(1);\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(State::class);\n\n        $result2 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertCount(4, $result1);\n        self::assertInstanceOf(City::class, $result2[0]);\n        self::assertInstanceOf(City::class, $result2[1]);\n        self::assertInstanceOf(State::class, $result2[0]->getState());\n        self::assertInstanceOf(State::class, $result2[1]->getState());\n\n        $this->assertQueryCount(2);\n    }\n\n    public function testReloadQueryIfToManyAssociationItemIsNotFound(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n\n        $this->evictRegions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT s, c FROM Doctrine\\Tests\\Models\\Cache\\State s JOIN s.cities c';\n        $result1 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(State::class, $result1[0]);\n        self::assertInstanceOf(State::class, $result1[1]);\n        self::assertCount(2, $result1[0]->getCities());\n        self::assertCount(2, $result1[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result1[1]->getCities()->get(1));\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(City::class);\n\n        $result2 = $this->_em->createQuery($dql)\n                ->setCacheable(true)\n                ->getResult();\n\n        self::assertInstanceOf(State::class, $result2[0]);\n        self::assertInstanceOf(State::class, $result2[1]);\n        self::assertCount(2, $result2[0]->getCities());\n        self::assertCount(2, $result2[1]->getCities());\n\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[0]->getCities()->get(1));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(0));\n        self::assertInstanceOf(City::class, $result2[1]->getCities()->get(1));\n\n        $this->assertQueryCount(2);\n    }\n\n    public function testBasicNativeQueryCache(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(Country::class, 'c');\n        $rsm->addFieldResult('c', 'name', 'name');\n        $rsm->addFieldResult('c', 'id', 'id');\n\n        $this->getQueryLog()->reset()->enable();\n        $sql     = 'SELECT id, name FROM cache_country';\n        $result1 = $this->_em->createNativeQuery($sql, $rsm)->setCacheable(true)->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n        self::assertEquals($this->countries[0]->getId(), $result1[0]->getId());\n        self::assertEquals($this->countries[1]->getId(), $result1[1]->getId());\n        self::assertEquals($this->countries[0]->getName(), $result1[0]->getName());\n        self::assertEquals($this->countries[1]->getName(), $result1[1]->getName());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createNativeQuery($sql, $rsm)\n            ->setCacheable(true)\n            ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertCount(2, $result2);\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n\n        self::assertInstanceOf(Country::class, $result2[0]);\n        self::assertInstanceOf(Country::class, $result2[1]);\n\n        self::assertEquals($result1[0]->getId(), $result2[0]->getId());\n        self::assertEquals($result1[1]->getId(), $result2[1]->getId());\n\n        self::assertEquals($result1[0]->getName(), $result2[0]->getName());\n        self::assertEquals($result1[1]->getName(), $result2[1]->getName());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getDefaultQueryRegionName()));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));\n    }\n\n    public function testQueryDependsOnFirstAndMaxResultResult(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->setFirstResult(1)\n            ->setMaxResults(1)\n            ->getResult();\n\n        $this->assertQueryCount(1);\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->setFirstResult(2)\n            ->setMaxResults(1)\n            ->getResult();\n\n        $this->assertQueryCount(2);\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n\n        $this->_em->clear();\n\n        $result3 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        $this->assertQueryCount(3);\n        self::assertEquals(3, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(3, $this->secondLevelCacheLogger->getMissCount());\n    }\n\n    public function testQueryCacheLifetime(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        $getHash = static function (AbstractQuery $query) {\n            $method = new ReflectionMethod($query, 'getHash');\n\n            return $method->invoke($query);\n        };\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $query   = $this->_em->createQuery($dql);\n        $result1 = $query->setCacheable(true)\n            ->setLifetime(3600)\n            ->getResult();\n\n        self::assertNotEmpty($result1);\n        $this->assertQueryCount(1);\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        $this->_em->clear();\n\n        $key   = new QueryCacheKey($getHash($query), 3600);\n        $entry = $this->cache->getQueryCache()\n            ->getRegion()\n            ->get($key);\n\n        self::assertInstanceOf(QueryCacheEntry::class, $entry);\n        $entry = new QueryCacheEntry($entry->result, $entry->time / 2);\n\n        $this->cache->getQueryCache()\n            ->getRegion()\n            ->put($key, $entry);\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->setLifetime(3600)\n            ->getResult();\n\n        self::assertNotEmpty($result2);\n        $this->assertQueryCount(2);\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n    }\n\n    public function testQueryCacheRegion(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql   = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $query = $this->_em->createQuery($dql);\n\n        $query1  = clone $query;\n        $result1 = $query1->setCacheable(true)\n            ->setCacheRegion('foo_region')\n            ->getResult();\n\n        self::assertNotEmpty($result1);\n        $this->assertQueryCount(1);\n        self::assertEquals(0, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount('foo_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('foo_region'));\n\n        $query2  = clone $query;\n        $result2 = $query2->setCacheable(true)\n            ->setCacheRegion('bar_region')\n            ->getResult();\n\n        self::assertNotEmpty($result2);\n        $this->assertQueryCount(2);\n        self::assertEquals(0, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount('bar_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('bar_region'));\n\n        $query3  = clone $query;\n        $result3 = $query3->setCacheable(true)\n            ->setCacheRegion('foo_region')\n            ->getResult();\n\n        self::assertNotEmpty($result3);\n        $this->assertQueryCount(2);\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount('foo_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount('foo_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('foo_region'));\n\n        $query4  = clone $query;\n        $result4 = $query4->setCacheable(true)\n            ->setCacheRegion('bar_region')\n            ->getResult();\n\n        self::assertNotEmpty($result3);\n        $this->assertQueryCount(2);\n        self::assertEquals(6, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount('bar_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount('bar_region'));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('bar_region'));\n    }\n\n    public function testResolveAssociationCacheEntry(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n\n        $this->_em->clear();\n\n        $stateId     = $this->states[0]->getId();\n        $countryName = $this->states[0]->getCountry()->getName();\n        $dql         = 'SELECT s FROM Doctrine\\Tests\\Models\\Cache\\State s WHERE s.id = :id';\n        $query       = $this->_em->createQuery($dql);\n        $this->getQueryLog()->reset()->enable();\n\n        $query1 = clone $query;\n        $state1 = $query1\n            ->setParameter('id', $stateId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        self::assertNotNull($state1);\n        self::assertNotNull($state1->getCountry());\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(State::class, $state1);\n        self::assertEquals($countryName, $state1->getCountry()->getName());\n        self::assertEquals($stateId, $state1->getId());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $query2 = clone $query;\n        $state2 = $query2\n            ->setParameter('id', $stateId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        self::assertNotNull($state2);\n        self::assertNotNull($state2->getCountry());\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(State::class, $state2);\n        self::assertEquals($countryName, $state2->getCountry()->getName());\n        self::assertEquals($stateId, $state2->getId());\n    }\n\n    public function testResolveToOneAssociationCacheEntry(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->evictRegions();\n\n        $this->_em->clear();\n\n        $cityId = $this->cities[0]->getId();\n        $dql    = 'SELECT c, s FROM Doctrine\\Tests\\Models\\Cache\\City c JOIN c.state s WHERE c.id = :id';\n        $query  = $this->_em->createQuery($dql);\n        $this->getQueryLog()->reset()->enable();\n\n        $query1 = clone $query;\n        $city1  = $query1\n            ->setParameter('id', $cityId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(City::class, $city1);\n        self::assertInstanceOf(State::class, $city1->getState());\n        self::assertInstanceOf(City::class, $city1->getState()->getCities()->get(0));\n        self::assertInstanceOf(State::class, $city1->getState()->getCities()->get(0)->getState());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $query2 = clone $query;\n        $city2  = $query2\n            ->setParameter('id', $cityId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(City::class, $city2);\n        self::assertInstanceOf(State::class, $city2->getState());\n        self::assertInstanceOf(City::class, $city2->getState()->getCities()->get(0));\n        self::assertInstanceOf(State::class, $city2->getState()->getCities()->get(0)->getState());\n    }\n\n    public function testResolveToManyAssociationCacheEntry(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->evictRegions();\n\n        $this->_em->clear();\n\n        $stateId = $this->states[0]->getId();\n        $dql     = 'SELECT s, c FROM Doctrine\\Tests\\Models\\Cache\\State s JOIN s.cities c WHERE s.id = :id';\n        $query   = $this->_em->createQuery($dql);\n        $this->getQueryLog()->reset()->enable();\n\n        $query1 = clone $query;\n        $state1 = $query1\n            ->setParameter('id', $stateId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        $this->assertQueryCount(1);\n        self::assertInstanceOf(State::class, $state1);\n        self::assertInstanceOf(City::class, $state1->getCities()->get(0));\n        self::assertInstanceOf(State::class, $state1->getCities()->get(0)->getState());\n        self::assertSame($state1, $state1->getCities()->get(0)->getState());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $query2 = clone $query;\n        $state2 = $query2\n            ->setParameter('id', $stateId)\n            ->setCacheable(true)\n            ->setMaxResults(1)\n            ->getSingleResult();\n\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(State::class, $state2);\n        self::assertInstanceOf(City::class, $state2->getCities()->get(0));\n        self::assertInstanceOf(State::class, $state2->getCities()->get(0)->getState());\n        self::assertSame($state2, $state2->getCities()->get(0)->getState());\n    }\n\n    public function testHintClearEntityRegionUpdateStatement(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->_em->createQuery('DELETE Doctrine\\Tests\\Models\\Cache\\Country u WHERE u.id = 4')\n            ->setHint(Query::HINT_CACHE_EVICT, true)\n            ->execute();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n    }\n\n    public function testHintClearEntityRegionDeleteStatement(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->_em->createQuery(\"UPDATE Doctrine\\Tests\\Models\\Cache\\Country u SET u.name = 'foo' WHERE u.id = 1\")\n            ->setHint(Query::HINT_CACHE_EVICT, true)\n            ->execute();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n    }\n\n    public function testCacheablePartialQueryException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second level cache does not support partial entities.');\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->_em->createQuery('SELECT PARTIAL c.{id} FROM Doctrine\\Tests\\Models\\Cache\\Country c')\n            ->setCacheable(true)\n            ->getResult();\n    }\n\n    public function testCacheableForcePartialLoadHintQueryException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second level cache does not support partial entities.');\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c')\n            ->setCacheable(true)\n            ->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)\n            ->getResult();\n    }\n\n    public function testNonCacheableQueryDeleteStatementException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second-level cache query supports only select statements.');\n        $this->_em->createQuery('DELETE Doctrine\\Tests\\Models\\Cache\\Country u WHERE u.id = 4')\n            ->setCacheable(true)\n            ->getResult();\n    }\n\n    public function testNonCacheableQueryUpdateStatementException(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Second-level cache query supports only select statements.');\n        $this->_em->createQuery(\"UPDATE Doctrine\\Tests\\Models\\Cache\\Country u SET u.name = 'foo' WHERE u.id = 4\")\n            ->setCacheable(true)\n            ->getResult();\n    }\n\n    public function testQueryCacheShouldBeEvictedOnTimestampUpdate(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql = 'SELECT country FROM Doctrine\\Tests\\Models\\Cache\\Country country';\n\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(2, $result1);\n        $this->assertQueryCount(1);\n\n        $this->_em->persist(new Country('France'));\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(3, $result2);\n        $this->assertQueryCount(1);\n\n        foreach ($result2 as $entity) {\n            self::assertInstanceOf(Country::class, $entity);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheRepositoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheRepositoryTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testRepositoryCacheFind(): void\n    {\n        $this->evictRegions();\n        $this->loadFixturesCountries();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $this->getQueryLog()->reset()->enable();\n        $repository = $this->_em->getRepository(Country::class);\n        $country1   = $repository->find($this->countries[0]->getId());\n        $country2   = $repository->find($this->countries[1]->getId());\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Country::class, $country1);\n        self::assertInstanceOf(Country::class, $country2);\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(0, $this->secondLevelCacheLogger->getMissCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(Country::class)));\n    }\n\n    public function testRepositoryCacheFindAll(): void\n    {\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertCount(2, $repository->findAll());\n        $this->assertQueryCount(1);\n\n        $this->getQueryLog()->reset()->enable();\n        $countries = $repository->findAll();\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Country::class, $countries[0]);\n        self::assertInstanceOf(Country::class, $countries[1]);\n\n        self::assertEquals(3, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n    }\n\n    public function testRepositoryCacheFindAllInvalidation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertCount(2, $repository->findAll());\n        $this->assertQueryCount(1);\n\n        $this->getQueryLog()->reset()->enable();\n        $countries = $repository->findAll();\n\n        $this->assertQueryCount(0);\n\n        self::assertCount(2, $countries);\n        self::assertInstanceOf(Country::class, $countries[0]);\n        self::assertInstanceOf(Country::class, $countries[1]);\n\n        $country = new Country('foo');\n\n        $this->_em->persist($country);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertCount(3, $repository->findAll());\n        $this->assertQueryCount(1);\n\n        $country = $repository->find($country->getId());\n\n        $this->_em->remove($country);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertCount(2, $repository->findAll());\n        $this->assertQueryCount(1);\n    }\n\n    public function testRepositoryCacheFindBy(): void\n    {\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n\n        $criteria   = ['name' => $this->countries[0]->getName()];\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertCount(1, $repository->findBy($criteria));\n        $this->assertQueryCount(1);\n\n        $this->getQueryLog()->reset()->enable();\n        $countries = $repository->findBy($criteria);\n\n        $this->assertQueryCount(0);\n\n        self::assertCount(1, $countries);\n        self::assertInstanceOf(Country::class, $countries[0]);\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n    }\n\n    public function testRepositoryCacheFindOneBy(): void\n    {\n        $this->loadFixturesCountries();\n        $this->evictRegions();\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n\n        $criteria   = ['name' => $this->countries[0]->getName()];\n        $repository = $this->_em->getRepository(Country::class);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertNotNull($repository->findOneBy($criteria));\n        $this->assertQueryCount(1);\n\n        $this->getQueryLog()->reset()->enable();\n        $country = $repository->findOneBy($criteria);\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Country::class, $country);\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(1, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n    }\n\n    public function testRepositoryCacheFindAllToOneAssociation(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n\n        $this->evictRegions();\n\n        $this->secondLevelCacheLogger->clearStats();\n        $this->_em->clear();\n\n        // load from database\n        $repository = $this->_em->getRepository(State::class);\n        $this->getQueryLog()->reset()->enable();\n        $entities = $repository->findAll();\n\n        self::assertCount(4, $entities);\n        $this->assertQueryCount(1);\n\n        self::assertInstanceOf(State::class, $entities[0]);\n        self::assertInstanceOf(State::class, $entities[1]);\n        self::assertInstanceOf(Country::class, $entities[0]->getCountry());\n        self::assertInstanceOf(Country::class, $entities[0]->getCountry());\n\n        // load from cache\n        $this->getQueryLog()->reset()->enable();\n        $entities = $repository->findAll();\n\n        self::assertCount(4, $entities);\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(State::class, $entities[0]);\n        self::assertInstanceOf(State::class, $entities[1]);\n        self::assertInstanceOf(Country::class, $entities[0]->getCountry());\n        self::assertInstanceOf(Country::class, $entities[1]->getCountry());\n\n        // invalidate cache\n        $this->_em->persist(new State('foo', $this->_em->find(Country::class, $this->countries[0]->getId())));\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // load from database\n        $this->getQueryLog()->reset()->enable();\n        $entities = $repository->findAll();\n\n        self::assertCount(5, $entities);\n        $this->assertQueryCount(1);\n\n        self::assertInstanceOf(State::class, $entities[0]);\n        self::assertInstanceOf(State::class, $entities[1]);\n        self::assertInstanceOf(Country::class, $entities[0]->getCountry());\n        self::assertInstanceOf(Country::class, $entities[1]->getCountry());\n\n        // load from cache\n        $this->getQueryLog()->reset()->enable();\n        $entities = $repository->findAll();\n\n        self::assertCount(5, $entities);\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(State::class, $entities[0]);\n        self::assertInstanceOf(State::class, $entities[1]);\n        self::assertInstanceOf(Country::class, $entities[0]->getCountry());\n        self::assertInstanceOf(Country::class, $entities[1]->getCountry());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheSingleTableInheritanceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\Bar;\nuse Doctrine\\Tests\\Models\\Cache\\Beach;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Restaurant;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheSingleTableInheritanceTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testUseSameRegion(): void\n    {\n        $attractionRegion = $this->cache->getEntityCacheRegion(Attraction::class);\n        $restaurantRegion = $this->cache->getEntityCacheRegion(Restaurant::class);\n        $beachRegion      = $this->cache->getEntityCacheRegion(Beach::class);\n        $barRegion        = $this->cache->getEntityCacheRegion(Bar::class);\n\n        self::assertEquals($attractionRegion->getName(), $restaurantRegion->getName());\n        self::assertEquals($attractionRegion->getName(), $beachRegion->getName());\n        self::assertEquals($attractionRegion->getName(), $barRegion->getName());\n    }\n\n    public function testPutOnPersistSingleTableInheritance(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Bar::class, $this->attractions[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Bar::class, $this->attractions[1]->getId()));\n    }\n\n    public function testCountaisRootClass(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->_em->clear();\n\n        foreach ($this->attractions as $attraction) {\n            self::assertTrue($this->cache->containsEntity(Attraction::class, $attraction->getId()));\n            self::assertTrue($this->cache->containsEntity($attraction::class, $attraction->getId()));\n        }\n    }\n\n    public function testPutAndLoadEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->_em->clear();\n\n        $this->cache->evictEntityRegion(Attraction::class);\n\n        $entityId1 = $this->attractions[0]->getId();\n        $entityId2 = $this->attractions[1]->getId();\n\n        self::assertFalse($this->cache->containsEntity(Attraction::class, $entityId1));\n        self::assertFalse($this->cache->containsEntity(Attraction::class, $entityId2));\n        self::assertFalse($this->cache->containsEntity(Bar::class, $entityId1));\n        self::assertFalse($this->cache->containsEntity(Bar::class, $entityId2));\n\n        $entity1 = $this->_em->find(Attraction::class, $entityId1);\n        $entity2 = $this->_em->find(Attraction::class, $entityId2);\n\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $entityId1));\n        self::assertTrue($this->cache->containsEntity(Attraction::class, $entityId2));\n        self::assertTrue($this->cache->containsEntity(Bar::class, $entityId1));\n        self::assertTrue($this->cache->containsEntity(Bar::class, $entityId2));\n\n        self::assertInstanceOf(Attraction::class, $entity1);\n        self::assertInstanceOf(Attraction::class, $entity2);\n        self::assertInstanceOf(Bar::class, $entity1);\n        self::assertInstanceOf(Bar::class, $entity2);\n\n        self::assertEquals($this->attractions[0]->getId(), $entity1->getId());\n        self::assertEquals($this->attractions[0]->getName(), $entity1->getName());\n\n        self::assertEquals($this->attractions[1]->getId(), $entity2->getId());\n        self::assertEquals($this->attractions[1]->getName(), $entity2->getName());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $entity3 = $this->_em->find(Attraction::class, $entityId1);\n        $entity4 = $this->_em->find(Attraction::class, $entityId2);\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Attraction::class, $entity3);\n        self::assertInstanceOf(Attraction::class, $entity4);\n        self::assertInstanceOf(Bar::class, $entity3);\n        self::assertInstanceOf(Bar::class, $entity4);\n\n        self::assertNotSame($entity1, $entity3);\n        self::assertEquals($entity1->getId(), $entity3->getId());\n        self::assertEquals($entity1->getName(), $entity3->getName());\n\n        self::assertNotSame($entity2, $entity4);\n        self::assertEquals($entity2->getId(), $entity4->getId());\n        self::assertEquals($entity2->getName(), $entity4->getName());\n    }\n\n    public function testQueryCacheFindAll(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql     = 'SELECT a FROM Doctrine\\Tests\\Models\\Cache\\Attraction a';\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractions), $result1);\n        $this->assertQueryCount(1);\n\n        $this->_em->clear();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractions), $result2);\n        $this->assertQueryCount(1);\n\n        foreach ($result2 as $entity) {\n            self::assertInstanceOf(Attraction::class, $entity);\n        }\n    }\n\n    public function testShouldNotPutOneToManyRelationOnPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->_em->clear();\n\n        foreach ($this->cities as $city) {\n            self::assertTrue($this->cache->containsEntity(City::class, $city->getId()));\n            self::assertFalse($this->cache->containsCollection(City::class, 'attractions', $city->getId()));\n        }\n\n        foreach ($this->attractions as $attraction) {\n            self::assertTrue($this->cache->containsEntity(Attraction::class, $attraction->getId()));\n        }\n    }\n\n    public function testOneToManyRelationSingleTable(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        $this->cache->evictEntityRegion(City::class);\n        $this->cache->evictEntityRegion(Attraction::class);\n        $this->cache->evictCollectionRegion(City::class, 'attractions');\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(City::class, $this->cities[0]->getId());\n\n        self::assertInstanceOf(City::class, $entity);\n        self::assertInstanceOf(PersistentCollection::class, $entity->getAttractions());\n        self::assertCount(2, $entity->getAttractions());\n\n        $ownerId = $this->cities[0]->getId();\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($this->cache->containsEntity(City::class, $ownerId));\n        self::assertTrue($this->cache->containsCollection(City::class, 'attractions', $ownerId));\n\n        self::assertInstanceOf(Bar::class, $entity->getAttractions()->get(0));\n        self::assertInstanceOf(Bar::class, $entity->getAttractions()->get(1));\n        self::assertEquals($this->attractions[0]->getName(), $entity->getAttractions()->get(0)->getName());\n        self::assertEquals($this->attractions[1]->getName(), $entity->getAttractions()->get(1)->getName());\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(City::class, $ownerId);\n\n        self::assertInstanceOf(City::class, $entity);\n        self::assertInstanceOf(PersistentCollection::class, $entity->getAttractions());\n        self::assertCount(2, $entity->getAttractions());\n\n        $this->assertQueryCount(0);\n\n        self::assertInstanceOf(Bar::class, $entity->getAttractions()->get(0));\n        self::assertInstanceOf(Bar::class, $entity->getAttractions()->get(1));\n        self::assertEquals($this->attractions[0]->getName(), $entity->getAttractions()->get(0)->getName());\n        self::assertEquals($this->attractions[1]->getName(), $entity->getAttractions()->get(1)->getName());\n    }\n\n    public function testQueryCacheShouldBeEvictedOnTimestampUpdate(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $dql = 'SELECT attraction FROM Doctrine\\Tests\\Models\\Cache\\Attraction attraction';\n\n        $result1 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractions), $result1);\n        $this->assertQueryCount(1);\n\n        $contact = new Beach(\n            'Botafogo',\n            $this->_em->find(City::class, $this->cities[1]->getId()),\n        );\n\n        $this->_em->persist($contact);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $result2 = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->getResult();\n\n        self::assertCount(count($this->attractions) + 1, $result2);\n        $this->assertQueryCount(1);\n\n        foreach ($result2 as $entity) {\n            self::assertInstanceOf(Attraction::class, $entity);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SecondLevelCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\EventArgs;\nuse Doctrine\\ORM\\Event\\PostFlushEventArgs;\nuse Doctrine\\ORM\\Event\\PostRemoveEventArgs;\nuse Doctrine\\ORM\\Event\\PostUpdateEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse RuntimeException;\n\nuse function uniqid;\n\n#[Group('DDC-2183')]\nclass SecondLevelCacheTest extends SecondLevelCacheFunctionalTestCase\n{\n    public function testPutOnPersist(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n    }\n\n    public function testPutAndLoadEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n\n        $this->cache->evictEntityRegion(Country::class);\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $c1 = $this->_em->find(Country::class, $this->countries[0]->getId());\n        $c2 = $this->_em->find(Country::class, $this->countries[1]->getId());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertInstanceOf(Country::class, $c1);\n        self::assertInstanceOf(Country::class, $c2);\n\n        self::assertEquals($this->countries[0]->getId(), $c1->getId());\n        self::assertEquals($this->countries[0]->getName(), $c1->getName());\n\n        self::assertEquals($this->countries[1]->getId(), $c2->getId());\n        self::assertEquals($this->countries[1]->getName(), $c2->getName());\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $c3 = $this->_em->find(Country::class, $this->countries[0]->getId());\n        $c4 = $this->_em->find(Country::class, $this->countries[1]->getId());\n\n        $this->assertQueryCount(0);\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(Country::class)));\n\n        self::assertInstanceOf(Country::class, $c3);\n        self::assertInstanceOf(Country::class, $c4);\n\n        self::assertEquals($c1->getId(), $c3->getId());\n        self::assertEquals($c1->getName(), $c3->getName());\n\n        self::assertEquals($c2->getId(), $c4->getId());\n        self::assertEquals($c2->getName(), $c4->getName());\n    }\n\n    public function testRemoveEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n\n        $this->cache->evictEntityRegion(Country::class);\n        $this->secondLevelCacheLogger->clearRegionStats($this->getEntityRegion(Country::class));\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        $c1 = $this->_em->find(Country::class, $this->countries[0]->getId());\n        $c2 = $this->_em->find(Country::class, $this->countries[1]->getId());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertInstanceOf(Country::class, $c1);\n        self::assertInstanceOf(Country::class, $c2);\n\n        self::assertEquals($this->countries[0]->getId(), $c1->getId());\n        self::assertEquals($this->countries[0]->getName(), $c1->getName());\n\n        self::assertEquals($this->countries[1]->getId(), $c2->getId());\n        self::assertEquals($this->countries[1]->getName(), $c2->getName());\n\n        $this->_em->remove($c1);\n        $this->_em->remove($c2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId()));\n\n        self::assertNull($this->_em->find(Country::class, $this->countries[0]->getId()));\n        self::assertNull($this->_em->find(Country::class, $this->countries[1]->getId()));\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getMissCount());\n    }\n\n    public function testUpdateEntities(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->_em->clear();\n\n        self::assertEquals(6, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n        self::assertEquals(4, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n\n        $this->cache->evictEntityRegion(State::class);\n        $this->secondLevelCacheLogger->clearRegionStats($this->getEntityRegion(State::class));\n\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertFalse($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        $s1 = $this->_em->find(State::class, $this->states[0]->getId());\n        $s2 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        self::assertEquals(4, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertInstanceOf(State::class, $s1);\n        self::assertInstanceOf(State::class, $s2);\n\n        self::assertEquals($this->states[0]->getId(), $s1->getId());\n        self::assertEquals($this->states[0]->getName(), $s1->getName());\n\n        self::assertEquals($this->states[1]->getId(), $s2->getId());\n        self::assertEquals($this->states[1]->getName(), $s2->getName());\n\n        $s1->setName('NEW NAME 1');\n        $s2->setName('NEW NAME 2');\n\n        $this->_em->persist($s1);\n        $this->_em->persist($s2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertEquals(6, $this->secondLevelCacheLogger->getPutCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(Country::class)));\n        self::assertEquals(4, $this->secondLevelCacheLogger->getRegionPutCount($this->getEntityRegion(State::class)));\n\n        $this->getQueryLog()->reset()->enable();\n\n        $c3 = $this->_em->find(State::class, $this->states[0]->getId());\n        $c4 = $this->_em->find(State::class, $this->states[1]->getId());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n\n        $this->assertQueryCount(0);\n\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[0]->getId()));\n        self::assertTrue($this->cache->containsEntity(State::class, $this->states[1]->getId()));\n\n        self::assertInstanceOf(State::class, $c3);\n        self::assertInstanceOf(State::class, $c4);\n\n        self::assertEquals($s1->getId(), $c3->getId());\n        self::assertEquals('NEW NAME 1', $c3->getName());\n\n        self::assertEquals($s2->getId(), $c4->getId());\n        self::assertEquals('NEW NAME 2', $c4->getName());\n\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n        self::assertEquals(2, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::class)));\n    }\n\n    public function testPostFlushFailure(): void\n    {\n        $listener = new ListenerSecondLevelCacheTest(\n            [\n                Events::postFlush => static function (): void {\n                    throw new RuntimeException('post flush failure');\n                },\n            ],\n        );\n\n        $this->_em->getEventManager()\n            ->addEventListener(Events::postFlush, $listener);\n\n        $country = new Country('Brazil');\n\n        $this->cache->evictEntityRegion(Country::class);\n\n        try {\n            $this->_em->persist($country);\n            $this->_em->flush();\n            self::fail('Should throw exception');\n        } catch (RuntimeException $exc) {\n            self::assertNotNull($country->getId());\n            self::assertEquals('post flush failure', $exc->getMessage());\n            self::assertTrue($this->cache->containsEntity(Country::class, $country->getId()));\n        }\n    }\n\n    public function testPostUpdateFailure(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->_em->clear();\n\n        $listener = new ListenerSecondLevelCacheTest(\n            [\n                Events::postUpdate => static function (): void {\n                    throw new RuntimeException('post update failure');\n                },\n            ],\n        );\n\n        $this->_em->getEventManager()\n            ->addEventListener(Events::postUpdate, $listener);\n\n        $this->cache->evictEntityRegion(State::class);\n\n        $stateId   = $this->states[0]->getId();\n        $stateName = $this->states[0]->getName();\n        $state     = $this->_em->find(State::class, $stateId);\n\n        self::assertTrue($this->cache->containsEntity(State::class, $stateId));\n        self::assertInstanceOf(State::class, $state);\n        self::assertEquals($stateName, $state->getName());\n\n        $state->setName($stateName . uniqid());\n\n        $this->_em->persist($state);\n\n        try {\n            $this->_em->flush();\n            self::fail('Should throw exception');\n        } catch (Exception $exc) {\n            self::assertEquals('post update failure', $exc->getMessage());\n        }\n\n        $this->_em->clear();\n\n        self::assertTrue($this->cache->containsEntity(State::class, $stateId));\n\n        $state = $this->_em->find(State::class, $stateId);\n\n        self::assertInstanceOf(State::class, $state);\n        self::assertEquals($stateName, $state->getName());\n    }\n\n    public function testPostRemoveFailure(): void\n    {\n        $this->loadFixturesCountries();\n        $this->_em->clear();\n\n        $listener = new ListenerSecondLevelCacheTest(\n            [\n                Events::postRemove => static function (): void {\n                    throw new RuntimeException('post remove failure');\n                },\n            ],\n        );\n\n        $this->_em->getEventManager()\n            ->addEventListener(Events::postRemove, $listener);\n\n        $this->cache->evictEntityRegion(Country::class);\n\n        $countryId = $this->countries[0]->getId();\n        $country   = $this->_em->find(Country::class, $countryId);\n\n        self::assertTrue($this->cache->containsEntity(Country::class, $countryId));\n        self::assertInstanceOf(Country::class, $country);\n\n        $this->_em->remove($country);\n\n        try {\n            $this->_em->flush();\n            self::fail('Should throw exception');\n        } catch (Exception $exc) {\n            self::assertEquals('post remove failure', $exc->getMessage());\n        }\n\n        $this->_em->clear();\n\n        self::assertFalse(\n            $this->cache->containsEntity(Country::class, $countryId),\n            'Removal attempts should clear the cache entry corresponding to the entity',\n        );\n\n        self::assertInstanceOf(Country::class, $this->_em->find(Country::class, $countryId));\n    }\n\n    public function testCachedNewEntityExists(): void\n    {\n        $this->loadFixturesCountries();\n\n        $persister = $this->_em->getUnitOfWork()->getEntityPersister(Country::class);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($persister->exists($this->countries[0]));\n\n        $this->assertQueryCount(0);\n\n        self::assertFalse($persister->exists(new Country('Foo')));\n    }\n}\n\n\nclass ListenerSecondLevelCacheTest\n{\n    /**\n     * @param array<string, callable> $callbacks\n     * @phpstan-param array<string, callable> $callbacks\n     */\n    public function __construct(public array $callbacks = [])\n    {\n    }\n\n    private function dispatch(string $eventName, EventArgs $args): void\n    {\n        if (isset($this->callbacks[$eventName])) {\n            ($this->callbacks[$eventName])($args);\n        }\n    }\n\n    public function postFlush(PostFlushEventArgs $args): void\n    {\n        $this->dispatch(__FUNCTION__, $args);\n    }\n\n    public function postUpdate(PostUpdateEventArgs $args): void\n    {\n        $this->dispatch(__FUNCTION__, $args);\n    }\n\n    public function postRemove(PostRemoveEventArgs $args): void\n    {\n        $this->dispatch(__FUNCTION__, $args);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SequenceGeneratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\SequenceGenerator;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Description of SequenceGeneratorTest\n */\nclass SequenceGeneratorTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! $this->_em->getConnection()->getDatabasePlatform()->supportsSequences()) {\n            self::markTestSkipped('Only working for Databases that support sequences.');\n        }\n\n        $this->createSchemaForModels(SequenceEntity::class);\n    }\n\n    public function testHighAllocationSizeSequence(): void\n    {\n        for ($i = 0; $i < 11; ++$i) {\n            $this->_em->persist(new SequenceEntity());\n        }\n\n        $this->_em->flush();\n\n        self::assertCount(11, $this->_em->getRepository(SequenceEntity::class)->findAll());\n    }\n}\n\n#[Entity]\nclass SequenceEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'SEQUENCE')]\n    #[SequenceGenerator(allocationSize: 5, sequenceName: 'person_id_seq')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SingleTableCompositeKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\SingleChildClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\SingleRootClass;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass SingleTableCompositeKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('compositekeyinheritance');\n\n        parent::setUp();\n    }\n\n    public function testInsertWithCompositeKey(): void\n    {\n        $childEntity = new SingleChildClass();\n        $this->_em->persist($childEntity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity = $this->findEntity();\n        self::assertEquals($childEntity, $entity);\n    }\n\n    #[Group('non-cacheable')]\n    public function testUpdateWithCompositeKey(): void\n    {\n        $childEntity = new SingleChildClass();\n        $this->_em->persist($childEntity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity            = $this->findEntity();\n        $entity->extension = 'ext-new';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $persistedEntity = $this->findEntity();\n        self::assertEquals($entity, $persistedEntity);\n    }\n\n    private function findEntity(): SingleChildClass\n    {\n        return $this->_em->find(SingleRootClass::class, ['keyPart1' => 'part-1', 'keyPart2' => 'part-2']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/SingleTableInheritanceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\MatchingAssociationFieldRequiresObject;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_map;\nuse function sort;\n\nclass SingleTableInheritanceTest extends OrmFunctionalTestCase\n{\n    private CompanyEmployee|null $salesPerson = null;\n\n    /** @var list<CompanyEmployee> */\n    private array $engineers = [];\n\n    private CompanyFixContract|null $fix = null;\n\n    private CompanyFlexContract|null $flex = null;\n\n    private CompanyFlexUltraContract|null $ultra = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function persistRelatedEmployees(): void\n    {\n        $this->salesPerson = new CompanyEmployee();\n        $this->salesPerson->setName('Poor Sales Guy');\n        $this->salesPerson->setDepartment('Sales');\n        $this->salesPerson->setSalary(100);\n\n        $engineer1 = new CompanyEmployee();\n        $engineer1->setName('Roman B.');\n        $engineer1->setDepartment('IT');\n        $engineer1->setSalary(100);\n        $this->engineers[] = $engineer1;\n\n        $engineer2 = new CompanyEmployee();\n        $engineer2->setName('Jonathan W.');\n        $engineer2->setDepartment('IT');\n        $engineer2->setSalary(100);\n        $this->engineers[] = $engineer2;\n\n        $engineer3 = new CompanyEmployee();\n        $engineer3->setName('Benjamin E.');\n        $engineer3->setDepartment('IT');\n        $engineer3->setSalary(100);\n        $this->engineers[] = $engineer3;\n\n        $engineer4 = new CompanyEmployee();\n        $engineer4->setName('Guilherme B.');\n        $engineer4->setDepartment('IT');\n        $engineer4->setSalary(100);\n        $this->engineers[] = $engineer4;\n\n        $this->_em->persist($this->salesPerson);\n        $this->_em->persist($engineer1);\n        $this->_em->persist($engineer2);\n        $this->_em->persist($engineer3);\n        $this->_em->persist($engineer4);\n    }\n\n    public function loadFullFixture(): void\n    {\n        $this->persistRelatedEmployees();\n\n        $this->fix = new CompanyFixContract();\n        $this->fix->setFixPrice(1000);\n        $this->fix->setSalesPerson($this->salesPerson);\n        $this->fix->addEngineer($this->engineers[0]);\n        $this->fix->addEngineer($this->engineers[1]);\n        $this->fix->markCompleted();\n\n        $this->flex = new CompanyFlexContract();\n        $this->flex->setSalesPerson($this->salesPerson);\n        $this->flex->setHoursWorked(100);\n        $this->flex->setPricePerHour(100);\n        $this->flex->addEngineer($this->engineers[2]);\n        $this->flex->addEngineer($this->engineers[1]);\n        $this->flex->addEngineer($this->engineers[3]);\n        $this->flex->markCompleted();\n\n        $this->ultra = new CompanyFlexUltraContract();\n        $this->ultra->setSalesPerson($this->salesPerson);\n        $this->ultra->setHoursWorked(150);\n        $this->ultra->setPricePerHour(150);\n        $this->ultra->setMaxPrice(7000);\n        $this->ultra->addEngineer($this->engineers[3]);\n        $this->ultra->addEngineer($this->engineers[0]);\n\n        $this->_em->persist($this->fix);\n        $this->_em->persist($this->flex);\n        $this->_em->persist($this->ultra);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testPersistChildOfBaseClass(): void\n    {\n        $this->persistRelatedEmployees();\n\n        $fixContract = new CompanyFixContract();\n        $fixContract->setFixPrice(1000);\n        $fixContract->setSalesPerson($this->salesPerson);\n\n        $this->_em->persist($fixContract);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $contract = $this->_em->find(CompanyFixContract::class, $fixContract->getId());\n\n        self::assertInstanceOf(CompanyFixContract::class, $contract);\n        self::assertEquals(1000, $contract->getFixPrice());\n        self::assertEquals($this->salesPerson->getId(), $contract->getSalesPerson()->getId());\n    }\n\n    public function testPersistDeepChildOfBaseClass(): void\n    {\n        $this->persistRelatedEmployees();\n\n        $ultraContract = new CompanyFlexUltraContract();\n        $ultraContract->setSalesPerson($this->salesPerson);\n        $ultraContract->setHoursWorked(100);\n        $ultraContract->setPricePerHour(50);\n        $ultraContract->setMaxPrice(7000);\n\n        $this->_em->persist($ultraContract);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $contract = $this->_em->find(CompanyFlexUltraContract::class, $ultraContract->getId());\n\n        self::assertInstanceOf(CompanyFlexUltraContract::class, $contract);\n        self::assertEquals(7000, $contract->getMaxPrice());\n        self::assertEquals(100, $contract->getHoursWorked());\n        self::assertEquals(50, $contract->getPricePerHour());\n    }\n\n    public function testChildClassLifecycleUpdate(): void\n    {\n        $this->loadFullFixture();\n\n        $fix = $this->_em->find(CompanyContract::class, $this->fix->getId());\n        $fix->setFixPrice(2500);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $newFix = $this->_em->find(CompanyContract::class, $this->fix->getId());\n        self::assertEquals(2500, $newFix->getFixPrice());\n    }\n\n    public function testChildClassLifecycleRemove(): void\n    {\n        $this->loadFullFixture();\n\n        $fix = $this->_em->find(CompanyContract::class, $this->fix->getId());\n        $this->_em->remove($fix);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(CompanyContract::class, $this->fix->getId()));\n    }\n\n    public function testFindAllForAbstractBaseClass(): void\n    {\n        $this->loadFullFixture();\n        $contracts = $this->_em->getRepository(CompanyContract::class)->findAll();\n\n        self::assertCount(3, $contracts);\n        self::assertContainsOnly(CompanyContract::class, $contracts);\n    }\n\n    public function testFindAllForChildClass(): void\n    {\n        $this->loadFullFixture();\n\n        self::assertCount(1, $this->_em->getRepository(CompanyFixContract::class)->findAll());\n        self::assertCount(2, $this->_em->getRepository(CompanyFlexContract::class)->findAll());\n        self::assertCount(1, $this->_em->getRepository(CompanyFlexUltraContract::class)->findAll());\n    }\n\n    public function testFindForAbstractBaseClass(): void\n    {\n        $this->loadFullFixture();\n\n        $contract = $this->_em->find(CompanyContract::class, $this->fix->getId());\n\n        self::assertInstanceOf(CompanyFixContract::class, $contract);\n        self::assertEquals(1000, $contract->getFixPrice());\n    }\n\n    public function testQueryForAbstractBaseClass(): void\n    {\n        $this->loadFullFixture();\n\n        $contracts = $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c')->getResult();\n\n        self::assertCount(3, $contracts);\n        self::assertContainsOnly(CompanyContract::class, $contracts);\n    }\n\n    public function testQueryForChildClass(): void\n    {\n        $this->loadFullFixture();\n\n        self::assertCount(1, $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract c')->getResult());\n        self::assertCount(2, $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexContract c')->getResult());\n        self::assertCount(1, $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract c')->getResult());\n    }\n\n    public function testQueryBaseClassWithJoin(): void\n    {\n        $this->loadFullFixture();\n\n        $contracts = $this->_em->createQuery('SELECT c, p FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c JOIN c.salesPerson p')->getResult();\n        self::assertCount(3, $contracts);\n        self::assertContainsOnly(CompanyContract::class, $contracts);\n    }\n\n    public function testQueryScalarWithDiscriminatorValue(): void\n    {\n        $this->loadFullFixture();\n\n        $contracts = $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c ORDER BY c.id')->getScalarResult();\n\n        $discrValues = array_map(static fn ($a) => $a['c_discr'], $contracts);\n\n        sort($discrValues);\n\n        self::assertEquals(['fix', 'flexible', 'flexultra'], $discrValues);\n    }\n\n    public function testQueryChildClassWithCondition(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract c WHERE c.fixPrice = ?1';\n        $contract = $this->_em->createQuery($dql)->setParameter(1, 1000)->getSingleResult();\n\n        self::assertInstanceOf(CompanyFixContract::class, $contract);\n        self::assertEquals(1000, $contract->getFixPrice());\n    }\n\n    #[Group('non-cacheable')]\n    public function testUpdateChildClassWithCondition(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'UPDATE Doctrine\\Tests\\Models\\Company\\CompanyFlexContract c SET c.hoursWorked = c.hoursWorked * 2 WHERE c.hoursWorked = 150';\n        $affected = $this->_em->createQuery($dql)->execute();\n\n        self::assertEquals(1, $affected);\n\n        $flexContract  = $this->_em->find(CompanyContract::class, $this->flex->getId());\n        $ultraContract = $this->_em->find(CompanyContract::class, $this->ultra->getId());\n\n        self::assertEquals(300, $ultraContract->getHoursWorked());\n        self::assertEquals(100, $flexContract->getHoursWorked());\n    }\n\n    public function testUpdateBaseClassWithCondition(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'UPDATE Doctrine\\Tests\\Models\\Company\\CompanyContract c SET c.completed = true WHERE c.completed = false';\n        $affected = $this->_em->createQuery($dql)->execute();\n\n        self::assertEquals(1, $affected);\n\n        $dql      = 'UPDATE Doctrine\\Tests\\Models\\Company\\CompanyContract c SET c.completed = false';\n        $affected = $this->_em->createQuery($dql)->execute();\n\n        self::assertEquals(3, $affected);\n    }\n\n    public function testDeleteByChildClassCondition(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'DELETE Doctrine\\Tests\\Models\\Company\\CompanyFlexContract c';\n        $affected = $this->_em->createQuery($dql)->execute();\n\n        self::assertEquals(2, $affected);\n    }\n\n    public function testDeleteByBaseClassCondition(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'DELETE Doctrine\\Tests\\Models\\Company\\CompanyContract c WHERE c.completed = true';\n        $affected = $this->_em->createQuery($dql)->execute();\n\n        self::assertEquals(2, $affected);\n\n        $contracts = $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c')->getResult();\n        self::assertCount(1, $contracts);\n\n        self::assertFalse($contracts[0]->isCompleted(), 'Only non completed contracts should be left.');\n    }\n\n    #[Group('DDC-130')]\n    public function testDeleteJoinTableRecords(): void\n    {\n        $this->loadFullFixture();\n\n        // remove managed copy of the fix contract\n        $this->_em->remove($this->_em->find($this->fix::class, $this->fix->getId()));\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find($this->fix::class, $this->fix->getId()), 'Contract should not be present in the database anymore.');\n    }\n\n    #[Group('DDC-817')]\n    public function testFindByAssociation(): void\n    {\n        $this->loadFullFixture();\n\n        $repos     = $this->_em->getRepository(CompanyContract::class);\n        $contracts = $repos->findBy(['salesPerson' => $this->salesPerson->getId()]);\n        self::assertCount(3, $contracts, 'There should be 3 entities related to ' . $this->salesPerson->getId() . \" for 'Doctrine\\Tests\\Models\\Company\\CompanyContract'\");\n\n        $repos     = $this->_em->getRepository(CompanyFixContract::class);\n        $contracts = $repos->findBy(['salesPerson' => $this->salesPerson->getId()]);\n        self::assertCount(1, $contracts, 'There should be 1 entities related to ' . $this->salesPerson->getId() . \" for 'Doctrine\\Tests\\Models\\Company\\CompanyFixContract'\");\n\n        $repos     = $this->_em->getRepository(CompanyFlexContract::class);\n        $contracts = $repos->findBy(['salesPerson' => $this->salesPerson->getId()]);\n        self::assertCount(2, $contracts, 'There should be 2 entities related to ' . $this->salesPerson->getId() . \" for 'Doctrine\\Tests\\Models\\Company\\CompanyFlexContract'\");\n\n        $repos     = $this->_em->getRepository(CompanyFlexUltraContract::class);\n        $contracts = $repos->findBy(['salesPerson' => $this->salesPerson->getId()]);\n        self::assertCount(1, $contracts, 'There should be 1 entities related to ' . $this->salesPerson->getId() . \" for 'Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract'\");\n    }\n\n    #[Group('DDC-1637')]\n    public function testInheritanceMatching(): void\n    {\n        $this->loadFullFixture();\n\n        $repository = $this->_em->getRepository(CompanyContract::class);\n        $contracts  = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('salesPerson', $this->salesPerson),\n        ));\n        self::assertCount(3, $contracts);\n\n        $repository = $this->_em->getRepository(CompanyFixContract::class);\n        $contracts  = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('salesPerson', $this->salesPerson),\n        ));\n        self::assertCount(1, $contracts);\n    }\n\n    #[Group('DDC-2430')]\n    public function testMatchingNonObjectOnAssocationThrowsException(): void\n    {\n        $this->loadFullFixture();\n\n        $repository = $this->_em->getRepository(CompanyContract::class);\n\n        $this->expectException(MatchingAssociationFieldRequiresObject::class);\n        $this->expectExceptionMessage('annot match on Doctrine\\Tests\\Models\\Company\\CompanyContract::salesPerson with a non-object value.');\n\n        $contracts = $repository->matching(Criteria::create(true)->where(\n            Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()),\n        ));\n\n        // Load the association because it's wrapped in a lazy collection\n        $contracts->toArray();\n    }\n\n    #[Group('DDC-834')]\n    public function testGetReferenceEntityWithSubclasses(): void\n    {\n        $this->loadFullFixture();\n\n        $ref = $this->_em->getReference(CompanyContract::class, $this->fix->getId());\n        self::assertFalse($this->isUninitializedObject($ref), 'Cannot Request a proxy from a class that has subclasses.');\n        self::assertInstanceOf(CompanyContract::class, $ref);\n        self::assertInstanceOf(CompanyFixContract::class, $ref, 'Direct fetch of the reference has to load the child class Employee directly.');\n        $this->_em->clear();\n\n        $ref = $this->_em->getReference(CompanyFixContract::class, $this->fix->getId());\n\n        self::assertTrue($this->isUninitializedObject($ref), 'A proxy can be generated only if no subclasses exists for the requested reference.');\n    }\n\n    #[Group('DDC-952')]\n    public function testEagerLoadInheritanceHierarchy(): void\n    {\n        $this->loadFullFixture();\n\n        $dql      = 'SELECT f FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract f WHERE f.id = ?1';\n        $contract = $this->_em->createQuery($dql)\n                              ->setFetchMode(CompanyFixContract::class, 'salesPerson', ClassMetadata::FETCH_EAGER)\n                              ->setParameter(1, $this->fix->getId())\n                              ->getSingleResult();\n\n        self::assertFalse($this->isUninitializedObject($contract->getSalesPerson()));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/StandardEntityPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Tests capabilities of the persister.\n */\nclass StandardEntityPersisterTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n    }\n\n    public function testAcceptsForeignKeysAsCriteria(): void\n    {\n        $customer = new ECommerceCustomer();\n        $customer->setName('John Doe');\n        $cart = new ECommerceCart();\n        $cart->setPayment('Credit card');\n        $customer->setCart($cart);\n        $this->_em->persist($customer);\n        $this->_em->flush();\n        $this->_em->clear();\n        $cardId = $cart->getId();\n        unset($cart);\n\n        $class = $this->_em->getClassMetadata(ECommerceCart::class);\n\n        $persister = $this->_em->getUnitOfWork()->getEntityPersister(ECommerceCart::class);\n        $newCart   = new ECommerceCart();\n        $this->_em->getUnitOfWork()->registerManaged($newCart, ['id' => $cardId], []);\n        $persister->load(['customer_id' => $customer->getId()], $newCart, $class->associationMappings['customer']);\n        self::assertEquals('Credit card', $newCart->getPayment());\n    }\n\n    /**\n     * Ticket #2478 from Damon Jones (dljones)\n     */\n    public function testAddPersistRetrieve(): void\n    {\n        $f1 = new ECommerceFeature();\n        $f1->setDescription('AC-3');\n\n        $f2 = new ECommerceFeature();\n        $f2->setDescription('DTS');\n\n        $p = new ECommerceProduct();\n        $p->addFeature($f1);\n        $p->addFeature($f2);\n        $this->_em->persist($p);\n\n        $this->_em->flush();\n\n        self::assertCount(2, $p->getFeatures());\n        self::assertInstanceOf(PersistentCollection::class, $p->getFeatures());\n\n        $q = $this->_em->createQuery(\n            'SELECT p, f\n               FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p\n               JOIN p.features f',\n        );\n\n        $res = $q->getResult();\n\n        self::assertCount(2, $p->getFeatures());\n        self::assertInstanceOf(PersistentCollection::class, $p->getFeatures());\n\n        // Check that the features are the same instances still\n        foreach ($p->getFeatures() as $feature) {\n            if ($feature->getDescription() === 'AC-3') {\n                self::assertSame($feature, $f1);\n            } else {\n                self::assertSame($feature, $f2);\n            }\n        }\n\n        // Now we test how Hydrator affects IdentityMap\n        // (change from ArrayCollection to PersistentCollection)\n        $f3 = new ECommerceFeature();\n        $f3->setDescription('XVID');\n        $p->addFeature($f3);\n\n        // Now we persist the Feature #3\n        $this->_em->persist($p);\n        $this->_em->flush();\n\n        $q = $this->_em->createQuery(\n            'SELECT p, f\n               FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p\n               JOIN p.features f',\n        );\n\n        $res = $q->getResult();\n\n        // Persisted Product now must have 3 Feature items\n        self::assertCount(3, $res[0]->getFeatures());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1040Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1040')]\nclass DDC1040Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testReuseNamedEntityParameter(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'John Galt';\n        $user->username = 'jgalt';\n        $user->status   = 'inactive';\n\n        $article        = new CmsArticle();\n        $article->topic = 'This is John Galt speaking!';\n        $article->text  = 'Yadda Yadda!';\n        $article->setAuthor($user);\n\n        $this->_em->persist($user);\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $dql = 'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = :author';\n        $this->_em->createQuery($dql)\n                  ->setParameter('author', $user)\n                  ->getResult();\n\n        $dql = 'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = :author AND a.user = :author';\n        $this->_em->createQuery($dql)\n                  ->setParameter('author', $user)\n                  ->getResult();\n\n        $dql      = 'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author';\n        $farticle = $this->_em->createQuery($dql)\n                  ->setParameter('author', $user)\n                  ->setParameter('topic', 'This is John Galt speaking!')\n                  ->getSingleResult();\n\n        self::assertSame($article, $farticle);\n    }\n\n    public function testUseMultiplePositionalParameters(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'John Galt';\n        $user->username = 'jgalt';\n        $user->status   = 'inactive';\n\n        $article        = new CmsArticle();\n        $article->topic = 'This is John Galt speaking!';\n        $article->text  = 'Yadda Yadda!';\n        $article->setAuthor($user);\n\n        $this->_em->persist($user);\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $dql      = 'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3';\n        $farticle = $this->_em->createQuery($dql)\n                  ->setParameter(1, 'This is John Galt speaking!')\n                  ->setParameter(2, $user)\n                  ->setParameter(3, $user)\n                  ->getSingleResult();\n\n        self::assertSame($article, $farticle);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1041Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1041')]\nclass DDC1041Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function testGrabWrongSubtypeReturnsNull(): void\n    {\n        $fix = new CompanyFixContract();\n        $fix->setFixPrice(2000);\n\n        $this->_em->persist($fix);\n        $this->_em->flush();\n\n        $id = $fix->getId();\n\n        self::assertNull($this->_em->find(CompanyFlexContract::class, $id));\n        self::assertNull($this->_em->getReference(CompanyFlexContract::class, $id));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1043Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1043')]\nclass DDC1043Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testChangeSetPlusWeirdPHPCastingIntCastingRule(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'John Galt';\n        $user->username = 'jgalt';\n        $user->status   = '+44';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $user->status = '44';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(CmsUser::class, $user->id);\n        self::assertSame('44', $user->status);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1080Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1080')]\nclass DDC1080Test extends OrmFunctionalTestCase\n{\n    public function testHydration(): void\n    {\n        $this->createSchemaForModels(\n            DDC1080Foo::class,\n            DDC1080Bar::class,\n            DDC1080FooBar::class,\n        );\n\n        $foo1 = new DDC1080Foo();\n        $foo1->setFooTitle('foo title 1');\n        $foo2 = new DDC1080Foo();\n        $foo2->setFooTitle('foo title 2');\n\n        $bar1 = new DDC1080Bar();\n        $bar1->setBarTitle('bar title 1');\n        $bar2 = new DDC1080Bar();\n        $bar2->setBarTitle('bar title 2');\n        $bar3 = new DDC1080Bar();\n        $bar3->setBarTitle('bar title 3');\n\n        $foobar1 = new DDC1080FooBar();\n        $foobar1->setFoo($foo1);\n        $foobar1->setBar($bar1);\n        $foobar1->setOrderNr(0);\n\n        $foobar2 = new DDC1080FooBar();\n        $foobar2->setFoo($foo1);\n        $foobar2->setBar($bar2);\n        $foobar2->setOrderNr(0);\n\n        $foobar3 = new DDC1080FooBar();\n        $foobar3->setFoo($foo1);\n        $foobar3->setBar($bar3);\n        $foobar3->setOrderNr(0);\n\n        $this->_em->persist($foo1);\n        $this->_em->persist($foo2);\n        $this->_em->persist($bar1);\n        $this->_em->persist($bar2);\n        $this->_em->persist($bar3);\n        $this->_em->flush();\n\n        $this->_em->persist($foobar1);\n        $this->_em->persist($foobar2);\n        $this->_em->persist($foobar3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $foo     = $this->_em->find(DDC1080Foo::class, $foo1->getFooID());\n        $fooBars = $foo->getFooBars();\n\n        self::assertCount(3, $fooBars, 'Should return three foobars.');\n    }\n}\n\n\n#[Table(name: 'foo')]\n#[Entity]\nclass DDC1080Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'fooID', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $fooID;\n\n    /** @var string */\n    #[Column(name: 'fooTitle', type: 'string', length: 255)]\n    protected $fooTitle;\n\n    /** @phpstan-var Collection<DDC1080FooBar> */\n    #[OneToMany(targetEntity: 'DDC1080FooBar', mappedBy: 'foo', cascade: ['persist'])]\n    #[OrderBy(['orderNr' => 'ASC'])]\n    protected $fooBars;\n\n    public function __construct()\n    {\n        $this->fooBars = new ArrayCollection();\n    }\n\n    public function getFooID(): int\n    {\n        return $this->fooID;\n    }\n\n    public function getFooTitle(): string\n    {\n        return $this->fooTitle;\n    }\n\n    /** @phpstan-return Collection<DDC1080FooBar> */\n    public function getFooBars(): Collection\n    {\n        return $this->fooBars;\n    }\n\n    public function setFooID(int $fooID): void\n    {\n        $this->fooID = $fooID;\n    }\n\n    public function setFooTitle(string $fooTitle): void\n    {\n        $this->fooTitle = $fooTitle;\n    }\n\n    public function setFooBars(array $fooBars): void\n    {\n        $this->fooBars = $fooBars;\n    }\n}\n#[Table(name: 'bar')]\n#[Entity]\nclass DDC1080Bar\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'barID', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $barID;\n\n    /** @var string */\n    #[Column(name: 'barTitle', type: 'string', length: 255)]\n    protected $barTitle;\n\n    /** @phpstan-var Collection<DDC1080FooBar> */\n    #[OneToMany(targetEntity: 'DDC1080FooBar', mappedBy: 'bar', cascade: ['persist'])]\n    #[OrderBy(['orderNr' => 'ASC'])]\n    protected $fooBars;\n\n    public function __construct()\n    {\n        $this->fooBars = new ArrayCollection();\n    }\n\n    public function getBarID(): int\n    {\n        return $this->barID;\n    }\n\n    public function getBarTitle(): string\n    {\n        return $this->barTitle;\n    }\n\n    /** @phpstan-return Collection<DDC1080FooBar> */\n    public function getFooBars(): Collection\n    {\n        return $this->fooBars;\n    }\n\n    public function setBarID(int $barID): void\n    {\n        $this->barID = $barID;\n    }\n\n    public function setBarTitle(string $barTitle): void\n    {\n        $this->barTitle = $barTitle;\n    }\n\n    public function setFooBars(array $fooBars): void\n    {\n        $this->fooBars = $fooBars;\n    }\n}\n\n#[Table(name: 'fooBar')]\n#[Entity]\nclass DDC1080FooBar\n{\n    /** @var DDC1080Foo */\n    #[ManyToOne(targetEntity: 'DDC1080Foo')]\n    #[JoinColumn(name: 'fooID', referencedColumnName: 'fooID')]\n    #[Id]\n    protected $foo = null;\n\n    /** @var DDC1080Bar */\n    #[ManyToOne(targetEntity: 'DDC1080Bar')]\n    #[JoinColumn(name: 'barID', referencedColumnName: 'barID')]\n    #[Id]\n    protected $bar = null;\n\n    /** @var int orderNr */\n    #[Column(name: 'orderNr', type: 'integer', nullable: false)]\n    protected $orderNr = null;\n\n    public function getFoo(): DDC1080Foo\n    {\n        return $this->foo;\n    }\n\n    public function setFoo(DDC1080Foo $foo): DDC1080FooBar\n    {\n        $this->foo = $foo;\n\n        return $this;\n    }\n\n    public function getBar(): DDC1080Bar\n    {\n        return $this->bar;\n    }\n\n    public function setBar(DDC1080Bar $bar): DDC1080FooBar\n    {\n        $this->bar = $bar;\n\n        return $this;\n    }\n\n    public function getOrderNr(): int|null\n    {\n        return $this->orderNr;\n    }\n\n    public function setOrderNr(int|null $orderNr): DDC1080FooBar\n    {\n        $this->orderNr = $orderNr;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1113Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1113')]\n#[Group('DDC-1306')]\nclass DDC1113Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1113Engine::class,\n            DDC1113Vehicle::class,\n            DDC1113Car::class,\n            DDC1113Bus::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $car         = new DDC1113Car();\n        $car->engine = new DDC1113Engine();\n\n        $bus         = new DDC1113Bus();\n        $bus->engine = new DDC1113Engine();\n\n        $this->_em->persist($car);\n        $this->_em->flush();\n\n        $this->_em->persist($bus);\n        $this->_em->flush();\n\n        $this->_em->remove($bus);\n        $this->_em->remove($car);\n        $this->_em->flush();\n\n        self::assertEmpty($this->_em->getRepository(DDC1113Car::class)->findAll());\n        self::assertEmpty($this->_em->getRepository(DDC1113Bus::class)->findAll());\n        self::assertEmpty($this->_em->getRepository(DDC1113Engine::class)->findAll());\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['vehicle' => 'DDC1113Vehicle', 'car' => 'DDC1113Car', 'bus' => 'DDC1113Bus'])]\nclass DDC1113Vehicle\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC1113Vehicle */\n    #[ManyToOne(targetEntity: 'DDC1113Vehicle')]\n    public $parent;\n\n    /** @var DDC1113Engine */\n    #[OneToOne(targetEntity: 'DDC1113Engine', cascade: ['persist', 'remove'])]\n    public $engine;\n}\n\n#[Entity]\nclass DDC1113Car extends DDC1113Vehicle\n{\n}\n\n#[Entity]\nclass DDC1113Bus extends DDC1113Vehicle\n{\n}\n\n#[Entity]\nclass DDC1113Engine\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1129Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1129')]\nclass DDC1129Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testVersionFieldIgnoredInChangesetComputation(): void\n    {\n        $article        = new CmsArticle();\n        $article->text  = \"I don't know.\";\n        $article->topic = 'Who is John Galt?';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        self::assertEquals(1, $article->version);\n\n        $class = $this->_em->getClassMetadata(CmsArticle::class);\n        $uow   = $this->_em->getUnitOfWork();\n\n        $uow->computeChangeSet($class, $article);\n        $changeSet = $uow->getEntityChangeSet($article);\n        self::assertCount(0, $changeSet, 'No changesets should be computed.');\n\n        $article->text = 'This is John Galt speaking.';\n        $this->_em->flush();\n\n        self::assertEquals(2, $article->version);\n\n        $uow->computeChangeSet($class, $article);\n        $changeSet = $uow->getEntityChangeSet($article);\n        self::assertCount(0, $changeSet, 'No changesets should be computed.');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1163Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('DDC-1163')]\nclass DDC1163Test extends OrmFunctionalTestCase\n{\n    private int|null $productId = null;\n\n    private int|null $proxyHolderId = null;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1163Product::class,\n            DDC1163SpecialProduct::class,\n            DDC1163ProxyHolder::class,\n            DDC1163Tag::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $this->createSpecialProductAndProxyHolderReferencingIt();\n        $this->_em->clear();\n\n        $this->createProxyForSpecialProduct();\n\n        $this->setPropertyAndAssignTagToSpecialProduct();\n\n        // fails\n        $this->_em->flush();\n    }\n\n    private function createSpecialProductAndProxyHolderReferencingIt(): void\n    {\n        $specialProduct = new DDC1163SpecialProduct();\n        $this->_em->persist($specialProduct);\n\n        $proxyHolder = new DDC1163ProxyHolder();\n        $this->_em->persist($proxyHolder);\n\n        $proxyHolder->setSpecialProduct($specialProduct);\n\n        $this->_em->flush();\n\n        $this->productId     = $specialProduct->getId();\n        $this->proxyHolderId = $proxyHolder->getId();\n    }\n\n    /**\n     * We want Doctrine to instantiate a lazy-load proxy for the previously created\n     * 'SpecialProduct' and register it.\n     *\n     * When Doctrine loads the 'ProxyHolder', it will do just that because the 'ProxyHolder'\n     * references the 'SpecialProduct'.\n     */\n    private function createProxyForSpecialProduct(): void\n    {\n        $proxyHolder = $this->_em->find(DDC1163ProxyHolder::class, $this->proxyHolderId);\n        assert($proxyHolder instanceof DDC1163ProxyHolder);\n\n        self::assertInstanceOf(DDC1163SpecialProduct::class, $proxyHolder->getSpecialProduct());\n    }\n\n    private function setPropertyAndAssignTagToSpecialProduct(): void\n    {\n        $specialProduct = $this->_em->find(DDC1163SpecialProduct::class, $this->productId);\n        assert($specialProduct instanceof DDC1163SpecialProduct);\n\n        self::assertInstanceOf(DDC1163SpecialProduct::class, $specialProduct);\n        self::assertTrue($this->isUninitializedObject($specialProduct));\n\n        $specialProduct->setSubclassProperty('foobar');\n\n        // this screams violation of law of demeter ;)\n        self::assertEquals(\n            DDC1163SpecialProduct::class,\n            $this->_em->getUnitOfWork()->getEntityPersister($specialProduct::class)->getClassMetadata()->name,\n        );\n\n        $tag = new DDC1163Tag('Foo');\n        $this->_em->persist($tag);\n        $tag->setProduct($specialProduct);\n    }\n}\n\n#[Entity]\nclass DDC1163ProxyHolder\n{\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'DDC1163SpecialProduct')]\n    private DDC1163SpecialProduct|null $specialProduct = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setSpecialProduct(DDC1163SpecialProduct $specialProduct): void\n    {\n        $this->specialProduct = $specialProduct;\n    }\n\n    public function getSpecialProduct(): DDC1163SpecialProduct\n    {\n        return $this->specialProduct;\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['special' => 'DDC1163SpecialProduct'])]\nabstract class DDC1163Product\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Entity]\nclass DDC1163SpecialProduct extends DDC1163Product\n{\n    #[Column(name: 'subclass_property', type: 'string', nullable: true)]\n    private string|null $subclassProperty = null;\n\n    public function setSubclassProperty(string $value): void\n    {\n        $this->subclassProperty = $value;\n    }\n}\n\n#[Entity]\nclass DDC1163Tag\n{\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n    /** @var Product */\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[ManyToOne(targetEntity: 'DDC1163Product', inversedBy: 'tags')]\n    private $product;\n\n    public function __construct(\n        #[Column(name: 'name', type: 'string', length: 255)]\n        private string $name,\n    ) {\n    }\n\n    public function setProduct(DDC1163Product $product): void\n    {\n        $this->product = $product;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC117Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ApproveChanges;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Editor;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Link;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Reference;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Translation;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function count;\n\n#[Group('DDC-117')]\nclass DDC117Test extends OrmFunctionalTestCase\n{\n    private DDC117Article|null $article1;\n\n    private DDC117Article|null $article2;\n\n    private DDC117Reference $reference;\n\n    private DDC117Translation|null $translation;\n\n    private DDC117ArticleDetails $articleDetails;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ddc117');\n\n        parent::setUp();\n\n        $this->article1 = new DDC117Article('Foo');\n        $this->article2 = new DDC117Article('Bar');\n\n        $this->_em->persist($this->article1);\n        $this->_em->persist($this->article2);\n        $this->_em->flush();\n\n        $link = new DDC117Link($this->article1, $this->article2, 'Link-Description');\n        $this->_em->persist($link);\n\n        $this->reference = new DDC117Reference($this->article1, $this->article2, 'Test-Description');\n        $this->_em->persist($this->reference);\n\n        $this->translation = new DDC117Translation($this->article1, 'en', 'Bar');\n        $this->_em->persist($this->translation);\n\n        $this->articleDetails = new DDC117ArticleDetails($this->article1, 'Very long text');\n        $this->_em->persist($this->articleDetails);\n        $this->_em->flush();\n\n        $this->_em->clear();\n    }\n\n    #[Group('DDC-117')]\n    public function testAssociationOnlyCompositeKey(): void\n    {\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n\n        $mapRef = $this->_em->find(DDC117Reference::class, $idCriteria);\n        self::assertInstanceOf(DDC117Reference::class, $mapRef);\n        self::assertInstanceOf(DDC117Article::class, $mapRef->target());\n        self::assertInstanceOf(DDC117Article::class, $mapRef->source());\n        self::assertSame($mapRef, $this->_em->find(DDC117Reference::class, $idCriteria));\n\n        $this->_em->clear();\n\n        $dql    = 'SELECT r, s FROM ' . DDC117Reference::class . ' r JOIN r.source s WHERE r.source = ?1';\n        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 1)->getSingleResult();\n\n        self::assertInstanceOf(DDC117Reference::class, $mapRef);\n        self::assertInstanceOf(DDC117Article::class, $mapRef->target());\n        self::assertInstanceOf(DDC117Article::class, $mapRef->source());\n        self::assertSame($dqlRef, $this->_em->find(DDC117Reference::class, $idCriteria));\n\n        $this->_em->clear();\n\n        $dql    = 'SELECT r, s FROM ' . DDC117Reference::class . ' r JOIN r.source s WHERE s.title = ?1';\n        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getSingleResult();\n\n        self::assertInstanceOf(DDC117Reference::class, $dqlRef);\n        self::assertInstanceOf(DDC117Article::class, $dqlRef->target());\n        self::assertInstanceOf(DDC117Article::class, $dqlRef->source());\n        self::assertSame($dqlRef, $this->_em->find(DDC117Reference::class, $idCriteria));\n\n        $dql    = 'SELECT r, s FROM ' . DDC117Reference::class . ' r JOIN r.source s WHERE s.title = ?1';\n        $dqlRef = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getSingleResult();\n\n        $this->_em->contains($dqlRef);\n    }\n\n    #[Group('DDC-117')]\n    public function testUpdateAssociationEntity(): void\n    {\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n\n        $mapRef = $this->_em->find(DDC117Reference::class, $idCriteria);\n        self::assertNotNull($mapRef);\n        $mapRef->setDescription('New Description!!');\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $mapRef = $this->_em->find(DDC117Reference::class, $idCriteria);\n\n        self::assertEquals('New Description!!', $mapRef->getDescription());\n    }\n\n    #[Group('DDC-117')]\n    public function testFetchDql(): void\n    {\n        $dql  = 'SELECT r, s FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Reference r JOIN r.source s WHERE s.title = ?1';\n        $refs = $this->_em->createQuery($dql)->setParameter(1, 'Foo')->getResult();\n\n        self::assertGreaterThan(0, $refs, 'Has to contain at least one Reference.');\n\n        foreach ($refs as $ref) {\n            self::assertInstanceOf(DDC117Reference::class, $ref, 'Contains only Reference instances.');\n            self::assertTrue($this->_em->contains($ref), 'Contains Reference in the IdentityMap.');\n        }\n    }\n\n    #[Group('DDC-117')]\n    public function testRemoveCompositeElement(): void\n    {\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n\n        $refRep = $this->_em->find(DDC117Reference::class, $idCriteria);\n\n        $this->_em->remove($refRep);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(DDC117Reference::class, $idCriteria));\n    }\n\n    #[Group('DDC-117')]\n    #[Group('non-cacheable')]\n    public function testDqlRemoveCompositeElement(): void\n    {\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n\n        $dql = 'DELETE Doctrine\\Tests\\Models\\DDC117\\DDC117Reference r WHERE r.source = ?1 AND r.target = ?2';\n        $this->_em->createQuery($dql)\n                  ->setParameter(1, $this->article1->id())\n                  ->setParameter(2, $this->article2->id())\n                  ->execute();\n\n        self::assertNull($this->_em->find(DDC117Reference::class, $idCriteria));\n    }\n\n    #[Group('DDC-117')]\n    public function testInverseSideAccess(): void\n    {\n        $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id());\n\n        self::assertCount(1, $this->article1->references());\n\n        foreach ($this->article1->references() as $this->reference) {\n            self::assertInstanceOf(DDC117Reference::class, $this->reference);\n            self::assertSame($this->article1, $this->reference->source());\n        }\n\n        $this->_em->clear();\n\n        $dql        = 'SELECT a, r FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Article a INNER JOIN a.references r WHERE a.id = ?1';\n        $articleDql = $this->_em->createQuery($dql)\n                                ->setParameter(1, $this->article1->id())\n                                ->getSingleResult();\n\n        self::assertCount(1, $this->article1->references());\n\n        foreach ($this->article1->references() as $this->reference) {\n            self::assertInstanceOf(DDC117Reference::class, $this->reference);\n            self::assertSame($this->article1, $this->reference->source());\n        }\n    }\n\n    #[Group('DDC-117')]\n    public function testMixedCompositeKey(): void\n    {\n        $idCriteria = ['article' => $this->article1->id(), 'language' => 'en'];\n\n        $this->translation = $this->_em->find(DDC117Translation::class, $idCriteria);\n        self::assertInstanceOf(DDC117Translation::class, $this->translation);\n\n        self::assertSame($this->translation, $this->_em->find(DDC117Translation::class, $idCriteria));\n\n        $this->_em->clear();\n\n        $dql      = 'SELECT t, a FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Translation t JOIN t.article a WHERE t.article = ?1 AND t.language = ?2';\n        $dqlTrans = $this->_em->createQuery($dql)\n                              ->setParameter(1, $this->article1->id())\n                              ->setParameter(2, 'en')\n                              ->getSingleResult();\n\n        self::assertInstanceOf(DDC117Translation::class, $this->translation);\n    }\n\n    #[Group('DDC-117')]\n    public function testMixedCompositeKeyViolateUniqueness(): void\n    {\n        $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id());\n        $this->article1->addTranslation('en', 'Bar');\n        $this->article1->addTranslation('en', 'Baz');\n\n        // exception depending on the underlying Database Driver\n        $this->expectException(Exception::class);\n        $this->_em->flush();\n    }\n\n    #[Group('DDC-117')]\n    public function testOneToOneForeignObjectId(): void\n    {\n        $this->article1 = new DDC117Article('Foo');\n        $this->_em->persist($this->article1);\n        $this->_em->flush();\n\n        $this->articleDetails = new DDC117ArticleDetails($this->article1, 'Very long text');\n        $this->_em->persist($this->articleDetails);\n        $this->_em->flush();\n\n        $this->articleDetails->update('not so very long text!');\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em->find($this->article1::class, $this->article1->id());\n        assert($article instanceof DDC117Article);\n        self::assertEquals('not so very long text!', $article->getText());\n    }\n\n    #[Group('DDC-117')]\n    public function testOneToOneCascadeRemove(): void\n    {\n        $article = $this->_em->find($this->article1::class, $this->article1->id());\n        $this->_em->remove($article);\n        $this->_em->flush();\n\n        self::assertFalse($this->_em->contains($article->getDetails()));\n    }\n\n    #[Group('DDC-117')]\n    public function testOneToOneCascadePersist(): void\n    {\n        $this->article1       = new DDC117Article('Foo');\n        $this->articleDetails = new DDC117ArticleDetails($this->article1, 'Very long text');\n\n        $this->_em->persist($this->article1);\n        $this->_em->flush();\n\n        self::assertSame($this->articleDetails, $this->_em->find(DDC117ArticleDetails::class, $this->article1));\n    }\n\n    #[Group('DDC-117')]\n    public function testReferencesToForeignKeyEntities(): void\n    {\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n        $reference  = $this->_em->find(DDC117Reference::class, $idCriteria);\n\n        $idCriteria  = ['article' => $this->article1->id(), 'language' => 'en'];\n        $translation = $this->_em->find(DDC117Translation::class, $idCriteria);\n\n        $approveChanges = new DDC117ApproveChanges($reference->source()->getDetails(), $reference, $translation);\n        $this->_em->persist($approveChanges);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $approveChanges = $this->_em->find(DDC117ApproveChanges::class, $approveChanges->getId());\n\n        self::assertInstanceOf(DDC117ArticleDetails::class, $approveChanges->getArticleDetails());\n        self::assertInstanceOf(DDC117Reference::class, $approveChanges->getReference());\n        self::assertInstanceOf(DDC117Translation::class, $approveChanges->getTranslation());\n    }\n\n    #[Group('DDC-117')]\n    public function testLoadOneToManyCollectionOfForeignKeyEntities(): void\n    {\n        $article = $this->_em->find($this->article1::class, $this->article1->id());\n        assert($article instanceof DDC117Article);\n\n        $translations = $article->getTranslations();\n        self::assertFalse($translations->isInitialized());\n        self::assertContainsOnly(DDC117Translation::class, $translations);\n        self::assertTrue($translations->isInitialized());\n    }\n\n    #[Group('DDC-117')]\n    public function testLoadManyToManyCollectionOfForeignKeyEntities(): void\n    {\n        $editor = $this->loadEditorFixture();\n\n        self::assertFalse($editor->reviewingTranslations->isInitialized());\n        self::assertContainsOnly(DDC117Translation::class, $editor->reviewingTranslations);\n        self::assertTrue($editor->reviewingTranslations->isInitialized());\n\n        $this->_em->clear();\n\n        $dql    = 'SELECT e, t FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Editor e JOIN e.reviewingTranslations t WHERE e.id = ?1';\n        $editor = $this->_em->createQuery($dql)->setParameter(1, $editor->id)->getSingleResult();\n        self::assertTrue($editor->reviewingTranslations->isInitialized());\n        self::assertContainsOnly(DDC117Translation::class, $editor->reviewingTranslations);\n    }\n\n    #[Group('DDC-117')]\n    public function testClearManyToManyCollectionOfForeignKeyEntities(): void\n    {\n        $editor = $this->loadEditorFixture();\n        self::assertCount(3, $editor->reviewingTranslations);\n\n        $editor->reviewingTranslations->clear();\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $editor = $this->_em->find($editor::class, $editor->id);\n        self::assertCount(0, $editor->reviewingTranslations);\n    }\n\n    #[Group('DDC-117')]\n    public function testLoadInverseManyToManyCollection(): void\n    {\n        $editor = $this->loadEditorFixture();\n\n        self::assertInstanceOf(DDC117Translation::class, $editor->reviewingTranslations[0]);\n\n        $reviewedBy = $editor->reviewingTranslations[0]->getReviewedByEditors();\n        self::assertCount(1, $reviewedBy);\n        self::assertSame($editor, $reviewedBy[0]);\n\n        $this->_em->clear();\n\n        $dql   = 'SELECT t, e FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Translation t ' .\n               'JOIN t.reviewedByEditors e WHERE t.article = ?1 AND t.language = ?2';\n        $trans = $this->_em->createQuery($dql)\n                           ->setParameter(1, $this->translation->getArticleId())\n                           ->setParameter(2, $this->translation->getLanguage())\n                           ->getSingleResult();\n\n        self::assertInstanceOf(DDC117Translation::class, $trans);\n        self::assertContainsOnly(DDC117Editor::class, $trans->reviewedByEditors);\n        self::assertCount(1, $trans->reviewedByEditors);\n    }\n\n    #[Group('DDC-117')]\n    public function testLoadOneToManyOfSourceEntityWithAssociationIdentifier(): void\n    {\n        $editor = $this->loadEditorFixture();\n\n        $editor->addLastTranslation($editor->reviewingTranslations[0]);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $editor           = $this->_em->find($editor::class, $editor->id);\n        $lastTranslatedBy = $editor->reviewingTranslations[0]->getLastTranslatedBy();\n        $lastTranslatedBy->count();\n\n        self::assertCount(1, $lastTranslatedBy);\n    }\n\n    private function loadEditorFixture(): DDC117Editor\n    {\n        $editor = new DDC117Editor('beberlei');\n\n        $article1 = $this->_em->find($this->article1::class, $this->article1->id());\n        assert($article1 instanceof DDC117Article);\n        foreach ($article1->getTranslations() as $translation) {\n            $editor->reviewingTranslations[] = $translation;\n        }\n\n        $article2 = $this->_em->find($this->article2::class, $this->article2->id());\n        assert($article2 instanceof DDC117Article);\n        $article2->addTranslation('de', 'Vanille-Krapferl'); // omnomnom\n        $article2->addTranslation('fr', \"Sorry can't speak french!\");\n\n        foreach ($article2->getTranslations() as $translation) {\n            $this->_em->persist($translation); // otherwise persisting the editor won't work, reachability!\n            $editor->reviewingTranslations[] = $translation;\n        }\n\n        $this->_em->persist($editor);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return $this->_em->find($editor::class, $editor->id);\n    }\n\n    #[Group('DDC-1652')]\n    public function testArrayHydrationWithCompositeKey(): void\n    {\n        $dql    = 'SELECT r,s,t FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t';\n        $before = count($this->_em->createQuery($dql)->getResult());\n\n        $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id());\n        $this->article2 = $this->_em->find(DDC117Article::class, $this->article2->id());\n\n        $this->reference = new DDC117Reference($this->article2, $this->article1, 'Test-Description');\n        $this->_em->persist($this->reference);\n\n        $this->reference = new DDC117Reference($this->article1, $this->article1, 'Test-Description');\n        $this->_em->persist($this->reference);\n\n        $this->reference = new DDC117Reference($this->article2, $this->article2, 'Test-Description');\n        $this->_em->persist($this->reference);\n\n        $this->_em->flush();\n\n        $dql  = 'SELECT r,s,t FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t';\n        $data = $this->_em->createQuery($dql)->getArrayResult();\n\n        self::assertCount($before + 3, $data);\n    }\n\n    #[Group('DDC-2246')]\n    public function testGetEntityState(): void\n    {\n        if ($this->isSecondLevelCacheEnabled) {\n            self::markTestIncomplete('Second level cache - not supported yet');\n        }\n\n        $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id());\n        $this->article2 = $this->_em->find(DDC117Article::class, $this->article2->id());\n\n        $this->reference = new DDC117Reference($this->article2, $this->article1, 'Test-Description');\n\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($this->reference));\n\n        $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()];\n        $reference  = $this->_em->find(DDC117Reference::class, $idCriteria);\n\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($reference));\n    }\n\n    #[Group('DDC-117')]\n    public function testIndexByOnCompositeKeyField(): void\n    {\n        $article = $this->_em->find(DDC117Article::class, $this->article1->id());\n\n        self::assertInstanceOf(DDC117Article::class, $article);\n        self::assertCount(1, $article->getLinks());\n        self::assertTrue($article->getLinks()->offsetExists($this->article2->id()));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1181Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC1181Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1181Hotel::class,\n            DDC1181Booking::class,\n            DDC1181Room::class,\n        );\n    }\n\n    #[Group('DDC-1181')]\n    public function testIssue(): void\n    {\n        $hotel = new DDC1181Hotel();\n        $room1 = new DDC1181Room();\n        $room2 = new DDC1181Room();\n\n        $this->_em->persist($hotel);\n        $this->_em->persist($room1);\n        $this->_em->persist($room2);\n        $this->_em->flush();\n\n        $booking1          = new DDC1181Booking();\n        $booking1->hotel   = $hotel;\n        $booking1->room    = $room1;\n        $booking2          = new DDC1181Booking();\n        $booking2->hotel   = $hotel;\n        $booking2->room    = $room2;\n        $hotel->bookings[] = $booking1;\n        $hotel->bookings[] = $booking2;\n\n        $this->_em->persist($booking1);\n        $this->_em->persist($booking2);\n        $this->_em->flush();\n\n        $this->_em->remove($hotel);\n        $this->_em->flush();\n\n        self::assertEmpty($this->_em->getRepository(DDC1181Booking::class)->findAll());\n    }\n}\n\n#[Entity]\nclass DDC1181Hotel\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Booking[] */\n    #[OneToMany(targetEntity: 'DDC1181Booking', mappedBy: 'hotel', cascade: ['remove'])]\n    public $bookings;\n}\n\n#[Entity]\nclass DDC1181Booking\n{\n    /** @var Hotel */\n    #[JoinColumn(name: 'hotel_id', referencedColumnName: 'id')]\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1181Hotel', inversedBy: 'bookings')]\n    public $hotel;\n    /** @var Room */\n    #[JoinColumn(name: 'room_id', referencedColumnName: 'id')]\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1181Room')]\n    public $room;\n}\n\n#[Entity]\nclass DDC1181Room\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1193Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC1193Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1193Company::class,\n            DDC1193Person::class,\n            DDC1193Account::class,\n        );\n    }\n\n    #[Group('DDC-1193')]\n    public function testIssue(): void\n    {\n        $company = new DDC1193Company();\n        $person  = new DDC1193Person();\n        $account = new DDC1193Account();\n\n        $person->account = $account;\n        $company->member = $person;\n\n        $this->_em->persist($company);\n        $this->_em->flush();\n\n        $companyId = $company->id;\n        $this->_em->clear();\n\n        $company = $this->_em->find($company::class, $companyId);\n\n        self::assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($company), 'Company is in identity map.');\n        self::assertTrue($this->isUninitializedObject($company->member), 'Pre-Condition');\n        self::assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($company->member), 'Member is in identity map.');\n\n        $this->_em->remove($company);\n        $this->_em->flush();\n\n        self::assertCount(0, $this->_em->getRepository($account::class)->findAll());\n    }\n}\n\n#[Entity]\nclass DDC1193Company\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC1193Person */\n    #[OneToOne(targetEntity: 'DDC1193Person', cascade: ['persist', 'remove'])]\n    public $member;\n}\n\n#[Entity]\nclass DDC1193Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC1193Account */\n    #[OneToOne(targetEntity: 'DDC1193Account', cascade: ['persist', 'remove'])]\n    public $account;\n}\n\n#[Entity]\nclass DDC1193Account\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1209Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\nclass DDC1209Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1209One::class,\n            DDC1209Two::class,\n            DDC1209Three::class,\n        );\n    }\n\n    #[Group('DDC-1209')]\n    public function testIdentifierCanHaveCustomType(): void\n    {\n        $entity = new DDC1209Three();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertSame($entity, $this->_em->find(DDC1209Three::class, $entity->date));\n    }\n\n    #[Group('DDC-1209')]\n    public function testCompositeIdentifierCanHaveCustomType(): void\n    {\n        $future1 = new DDC1209One();\n\n        $this->_em->persist($future1);\n        $this->_em->flush();\n\n        $future2 = new DDC1209Two($future1);\n\n        $this->_em->persist($future2);\n        $this->_em->flush();\n\n        self::assertSame(\n            $future2,\n            $this->_em->find(\n                DDC1209Two::class,\n                [\n                    'future1'           => $future1,\n                    'startingDatetime' => $future2->startingDatetime,\n                    'duringDatetime'   => $future2->duringDatetime,\n                    'endingDatetime'   => $future2->endingDatetime,\n                ],\n            ),\n        );\n    }\n}\n\n#[Entity]\nclass DDC1209One\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Entity]\nclass DDC1209Two\n{\n    /** @var DateTime2 */\n    #[Id]\n    #[Column(type: 'datetime', nullable: false)]\n    public $startingDatetime;\n\n    /** @var DateTime2 */\n    #[Id]\n    #[Column(type: 'datetime', nullable: false)]\n    public $duringDatetime;\n\n    /** @var DateTime2 */\n    #[Id]\n    #[Column(type: 'datetime', nullable: false)]\n    public $endingDatetime;\n\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC1209One')]\n        #[JoinColumn(referencedColumnName: 'id')]\n        private DDC1209One $future1,\n    ) {\n        $this->startingDatetime = new DateTime2();\n        $this->duringDatetime   = new DateTime2();\n        $this->endingDatetime   = new DateTime2();\n    }\n}\n\n#[Entity]\nclass DDC1209Three\n{\n    /** @var DateTime2 */\n    #[Id]\n    #[Column(type: 'datetime', name: 'somedate')]\n    public $date;\n\n    public function __construct()\n    {\n        $this->date = new DateTime2();\n    }\n}\n\nclass DateTime2 extends DateTime implements Stringable\n{\n    public function __toString(): string\n    {\n        return $this->format('Y');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1225Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function strtolower;\n\n#[Group('DDC-1225')]\nclass DDC1225Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1225TestEntity1::class,\n            DDC1225TestEntity2::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n        $qb->from(DDC1225TestEntity1::class, 'te1')\n           ->select('te1')\n           ->where('te1.testEntity2 = ?1')\n           ->setParameter(1, 0);\n\n        self::assertEquals(\n            strtolower('SELECT t0_.test_entity2_id AS test_entity2_id_0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?'),\n            strtolower($qb->getQuery()->getSQL()),\n        );\n    }\n}\n\n#[Table(name: 'te1')]\n#[Entity]\nclass DDC1225TestEntity1\n{\n    #[Id]\n    #[ManyToOne(targetEntity: 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC1225TestEntity2')]\n    #[JoinColumn(name: 'test_entity2_id', referencedColumnName: 'id')]\n    private DDC1225TestEntity2|null $testEntity2 = null;\n\n    public function setTestEntity2(DDC1225TestEntity2 $testEntity2): void\n    {\n        $this->testEntity2 = $testEntity2;\n    }\n\n    public function getTestEntity2(): DDC1225TestEntity2\n    {\n        return $this->testEntity2;\n    }\n}\n\n#[Table(name: 'te2')]\n#[Entity]\nclass DDC1225TestEntity2\n{\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    private int $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1228Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1228')]\n#[Group('DDC-1226')]\nclass DDC1228Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1228User::class, DDC1228Profile::class);\n    }\n\n    public function testOneToOnePersist(): void\n    {\n        $user          = new DDC1228User();\n        $profile       = new DDC1228Profile();\n        $profile->name = 'Foo';\n        $user->profile = $profile;\n\n        $this->_em->persist($user);\n        $this->_em->persist($profile);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(DDC1228User::class, $user->id);\n\n        self::assertTrue($this->isUninitializedObject($user->getProfile()), 'Proxy is not initialized');\n        $user->getProfile()->setName('Bar');\n        self::assertFalse($this->isUninitializedObject($user->getProfile()), 'Proxy is not initialized');\n\n        self::assertEquals('Bar', $user->getProfile()->getName());\n        self::assertEquals(['id' => 1, 'name' => 'Foo'], $this->_em->getUnitOfWork()->getOriginalEntityData($user->getProfile()));\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(DDC1228User::class, $user->id);\n        self::assertEquals('Bar', $user->getProfile()->getName());\n    }\n\n    public function testRefresh(): void\n    {\n        $user          = new DDC1228User();\n        $profile       = new DDC1228Profile();\n        $profile->name = 'Foo';\n        $user->profile = $profile;\n\n        $this->_em->persist($user);\n        $this->_em->persist($profile);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(DDC1228User::class, $user->id);\n\n        $this->_em->refresh($user);\n        $user->name = 'Baz';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(DDC1228User::class, $user->id);\n        self::assertEquals('Baz', $user->name);\n    }\n}\n\n#[Entity]\nclass DDC1228User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name = 'Bar';\n\n    /** @var DDC1228Profile */\n    #[OneToOne(targetEntity: 'DDC1228Profile')]\n    public $profile;\n\n    public function getProfile(): DDC1228Profile\n    {\n        return $this->profile;\n    }\n}\n\n#[Entity]\nclass DDC1228Profile\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1238Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1238')]\nclass DDC1238Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1238User::class);\n    }\n\n    public function testIssue(): void\n    {\n        $user = new DDC1238User();\n        $user->setName('test');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userId = $user->getId();\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(DDC1238User::class, $userId);\n        $this->_em->clear();\n\n        $userId2 = $user->getId();\n        self::assertEquals($userId, $userId2, 'This proxy can still be initialized.');\n    }\n\n    public function testIssueProxyClear(): void\n    {\n        $user = new DDC1238User();\n        $user->setName('test');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $userId = $user->getId();\n        $this->_em->clear();\n\n        $user = $this->_em->getReference(DDC1238User::class, $userId);\n        $this->_em->clear();\n\n        $user2 = $this->_em->getReference(DDC1238User::class, $userId);\n\n        //$this->initializeObject($user);\n\n        self::assertIsInt($user->getId(), 'Even if a proxy is detached, it should still have an identifier');\n\n        $this->initializeObject($user2);\n\n        self::assertIsInt($user2->getId(), 'The managed instance still has an identifier');\n    }\n}\n\n#[Entity]\nclass DDC1238User\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int|null $id = null;\n\n    #[Column]\n    private string|null $name = null;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1250Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1250')]\nclass DDC1250Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1250ClientHistory::class);\n    }\n\n    public function testIssue(): void\n    {\n        $c1                         = new DDC1250ClientHistory();\n        $c2                         = new DDC1250ClientHistory();\n        $c1->declinedClientsHistory = $c2;\n        $c1->declinedBy             = $c2;\n        $c2->declinedBy             = $c1;\n        $c2->declinedClientsHistory = $c1;\n\n        $this->_em->persist($c1);\n        $this->_em->persist($c2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $history = $this->_em->createQuery('SELECT h FROM ' . __NAMESPACE__ . '\\\\DDC1250ClientHistory h WHERE h.id = ?1')\n                  ->setParameter(1, $c2->id)->getSingleResult();\n\n        self::assertInstanceOf(DDC1250ClientHistory::class, $history);\n    }\n}\n\n#[Entity]\nclass DDC1250ClientHistory\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC1250ClientHistory */\n    #[OneToOne(targetEntity: 'DDC1250ClientHistory', inversedBy: 'declinedBy')]\n    #[JoinColumn(name: 'declined_clients_history_id', referencedColumnName: 'id')]\n    public $declinedClientsHistory;\n\n    /** @var DDC1250ClientHistory */\n    #[OneToOne(targetEntity: 'DDC1250ClientHistory', mappedBy: 'declinedClientsHistory')]\n    public $declinedBy;\n}\n\n/*\n *\nEntities\\ClientsHistory:\ntype: entity\ntable: clients_history\nfields:\nid:\nid: true\ntype: integer\nunsigned: false\nnullable: false\ngenerator:\nstrategy: IDENTITY\n[...skiped...]\noneToOne:\ndeclinedClientsHistory:\ntargetEntity: Entities\\ClientsHistory\njoinColumn:\nname: declined_clients_history_id\nreferencedColumnName: id\ninversedBy: declinedBy\ndeclinedBy:\ntargetEntity: Entities\\ClientsHistory\nmappedBy: declinedClientsHistory\nlifecycleCallbacks: { }\nrepositoryClass: Entities\\ClientsHistoryRepository\n\n\n */\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1300Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1300')]\nclass DDC1300Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1300Foo::class,\n            DDC1300FooLocale::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $foo               = new DDC1300Foo();\n        $foo->fooReference = 'foo';\n\n        $this->_em->persist($foo);\n        $this->_em->flush();\n\n        $locale         = new DDC1300FooLocale();\n        $locale->foo    = $foo;\n        $locale->locale = 'en';\n        $locale->title  = 'blub';\n\n        $this->_em->persist($locale);\n        $this->_em->flush();\n\n        $query  = $this->_em->createQuery('SELECT f, fl FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC1300Foo f JOIN f.fooLocaleRefFoo fl');\n        $result =  $query->getResult();\n\n        self::assertCount(1, $result);\n    }\n}\n\n#[Entity]\nclass DDC1300Foo\n{\n    /** @var int fooID */\n    #[Column(name: 'fooID', type: 'integer', nullable: false)]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Id]\n    public $fooID = null;\n\n    /** @var string fooReference */\n    #[Column(name: 'fooReference', type: 'string', nullable: true, length: 45)]\n    public $fooReference = null;\n\n    /** @phpstan-var Collection<int, DDC1300FooLocale> */\n    #[OneToMany(targetEntity: 'DDC1300FooLocale', mappedBy: 'foo', cascade: ['persist'])]\n    public $fooLocaleRefFoo = null;\n\n    /** @param mixed[]|null $options */\n    public function __construct(array|null $options = null)\n    {\n        $this->fooLocaleRefFoo = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC1300FooLocale\n{\n    /** @var DDC1300Foo */\n    #[ManyToOne(targetEntity: 'DDC1300Foo')]\n    #[JoinColumn(name: 'fooID', referencedColumnName: 'fooID')]\n    #[Id]\n    public $foo = null;\n\n    /** @var string locale */\n    #[Column(name: 'locale', type: 'string', nullable: false, length: 5)]\n    #[Id]\n    public $locale = null;\n\n    /** @var string title */\n    #[Column(name: 'title', type: 'string', nullable: true, length: 150)]\n    public $title = null;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1301Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyArticle;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyCar;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUser;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('non-cacheable')]\n#[Group('DDC-1301')]\nclass DDC1301Test extends OrmFunctionalTestCase\n{\n    private int|null $userId = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('legacy');\n\n        parent::setUp();\n\n        $class                                           = $this->_em->getClassMetadata(LegacyUser::class);\n        $class->associationMappings['articles']->fetch   = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['references']->fetch = ClassMetadata::FETCH_EXTRA_LAZY;\n        $class->associationMappings['cars']->fetch       = ClassMetadata::FETCH_EXTRA_LAZY;\n\n        $this->loadFixture();\n    }\n\n    public function tearDown(): void\n    {\n        parent::tearDown();\n\n        $class                                           = $this->_em->getClassMetadata(LegacyUser::class);\n        $class->associationMappings['articles']->fetch   = ClassMetadata::FETCH_LAZY;\n        $class->associationMappings['references']->fetch = ClassMetadata::FETCH_LAZY;\n        $class->associationMappings['cars']->fetch       = ClassMetadata::FETCH_LAZY;\n    }\n\n    public function testCountNotInitializesLegacyCollection(): void\n    {\n        $user = $this->_em->find(LegacyUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->articles->isInitialized());\n        self::assertCount(2, $user->articles);\n        self::assertFalse($user->articles->isInitialized());\n\n        foreach ($user->articles as $article) {\n        }\n\n        $this->assertQueryCount(2, 'Expecting two queries to be fired for count, then iteration.');\n    }\n\n    public function testCountNotInitializesLegacyCollectionWithForeignIdentifier(): void\n    {\n        $user = $this->_em->find(LegacyUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->references->isInitialized());\n        self::assertCount(2, $user->references);\n        self::assertFalse($user->references->isInitialized());\n\n        foreach ($user->references as $reference) {\n        }\n\n        $this->assertQueryCount(2, 'Expecting two queries to be fired for count, then iteration.');\n    }\n\n    public function testCountNotInitializesLegacyManyToManyCollection(): void\n    {\n        $user = $this->_em->find(LegacyUser::class, $this->userId);\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertFalse($user->cars->isInitialized());\n        self::assertCount(3, $user->cars);\n        self::assertFalse($user->cars->isInitialized());\n\n        foreach ($user->cars as $reference) {\n        }\n\n        $this->assertQueryCount(2, 'Expecting two queries to be fired for count, then iteration.');\n    }\n\n    public function loadFixture(): void\n    {\n        $user1           = new LegacyUser();\n        $user1->username = 'beberlei';\n        $user1->name     = 'Benjamin';\n\n        $user2           = new LegacyUser();\n        $user2->username = 'jwage';\n        $user2->name     = 'Jonathan';\n\n        $user3           = new LegacyUser();\n        $user3->username = 'romanb';\n        $user3->name     = 'Roman';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n\n        $article1        = new LegacyArticle();\n        $article1->topic = 'Test';\n        $article1->text  = 'Test';\n        $article1->setAuthor($user1);\n\n        $article2        = new LegacyArticle();\n        $article2->topic = 'Test';\n        $article2->text  = 'Test';\n        $article2->setAuthor($user1);\n\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n\n        $car1              = new LegacyCar();\n        $car1->description = 'Test1';\n\n        $car2              = new LegacyCar();\n        $car2->description = 'Test2';\n\n        $car3              = new LegacyCar();\n        $car3->description = 'Test3';\n\n        $user1->addCar($car1);\n        $user1->addCar($car2);\n        $user1->addCar($car3);\n\n        $user2->addCar($car1);\n        $user3->addCar($car1);\n\n        $this->_em->persist($car1);\n        $this->_em->persist($car2);\n        $this->_em->persist($car3);\n\n        $this->_em->flush();\n\n        $detail1 = new LegacyUserReference($user1, $user2, 'foo');\n        $detail2 = new LegacyUserReference($user1, $user3, 'bar');\n\n        $this->_em->persist($detail1);\n        $this->_em->persist($detail2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->userId = $user1->getId();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1306Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1306')]\nclass DDC1306Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testIssue(): void\n    {\n        $phone              = new CmsPhonenumber();\n        $phone->phonenumber = '1234';\n\n        // puts user and phone into commit order calculator\n        $this->_em->persist($phone);\n        $this->_em->flush();\n\n        $address          = new CmsAddress();\n        $address->city    = 'bonn';\n        $address->country = 'Germany';\n        $address->street  = 'somestreet!';\n        $address->zip     = 12345;\n\n        $this->_em->persist($address);\n\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'benjamin';\n        $user->status   = 'active';\n        $user->setAddress($address);\n\n        // puts user and address into commit order calculator, but does not calculate user dependencies new\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->remove($user->getAddress());\n        $this->_em->remove($user);\n        $this->_em->flush();\n\n        self::assertEmpty($this->_em->getRepository(CmsAddress::class)->findAll());\n        self::assertEmpty($this->_em->getRepository(CmsUser::class)->findAll());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1335Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1335')]\nclass DDC1335Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1335User::class, DDC1335Phone::class);\n        try {\n            $this->loadFixture();\n        } catch (UniqueConstraintViolationException) {\n        }\n    }\n\n    public function testDql(): void\n    {\n        $dql    = 'SELECT u FROM ' . __NAMESPACE__ . '\\DDC1335User u INDEX BY u.id';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey(1, $result);\n        self::assertArrayHasKey(2, $result);\n        self::assertArrayHasKey(3, $result);\n\n        $dql    = 'SELECT u, p FROM ' . __NAMESPACE__ . '\\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey('foo@foo.com', $result);\n        self::assertArrayHasKey('bar@bar.com', $result);\n        self::assertArrayHasKey('foobar@foobar.com', $result);\n\n        self::assertCount(3, $result['foo@foo.com']->phones);\n        self::assertCount(3, $result['bar@bar.com']->phones);\n        self::assertCount(3, $result['foobar@foobar.com']->phones);\n\n        $foo    = $result['foo@foo.com']->phones->toArray();\n        $bar    = $result['bar@bar.com']->phones->toArray();\n        $foobar = $result['foobar@foobar.com']->phones->toArray();\n\n        self::assertArrayHasKey(1, $foo);\n        self::assertArrayHasKey(2, $foo);\n        self::assertArrayHasKey(3, $foo);\n\n        self::assertArrayHasKey(4, $bar);\n        self::assertArrayHasKey(5, $bar);\n        self::assertArrayHasKey(6, $bar);\n\n        self::assertArrayHasKey(7, $foobar);\n        self::assertArrayHasKey(8, $foobar);\n        self::assertArrayHasKey(9, $foobar);\n    }\n\n    public function testTicket(): void\n    {\n        $builder = $this->_em->createQueryBuilder();\n        $builder->select('u')->from(DDC1335User::class, 'u', 'u.id');\n\n        $dql    = $builder->getQuery()->getDQL();\n        $result = $builder->getQuery()->getResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey(1, $result);\n        self::assertArrayHasKey(2, $result);\n        self::assertArrayHasKey(3, $result);\n        self::assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\\DDC1335User u INDEX BY u.id', $dql);\n    }\n\n    public function testIndexByUnique(): void\n    {\n        $builder = $this->_em->createQueryBuilder();\n        $builder->select('u')->from(DDC1335User::class, 'u', 'u.email');\n\n        $dql    = $builder->getQuery()->getDQL();\n        $result = $builder->getQuery()->getResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey('foo@foo.com', $result);\n        self::assertArrayHasKey('bar@bar.com', $result);\n        self::assertArrayHasKey('foobar@foobar.com', $result);\n        self::assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\\DDC1335User u INDEX BY u.email', $dql);\n    }\n\n    public function testIndexWithJoin(): void\n    {\n        $builder = $this->_em->createQueryBuilder();\n        $builder->select('u', 'p')\n                ->from(DDC1335User::class, 'u', 'u.email')\n                ->join('u.phones', 'p', null, null, 'p.id');\n\n        $dql    = $builder->getQuery()->getDQL();\n        $result = $builder->getQuery()->getResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey('foo@foo.com', $result);\n        self::assertArrayHasKey('bar@bar.com', $result);\n        self::assertArrayHasKey('foobar@foobar.com', $result);\n\n        self::assertCount(3, $result['foo@foo.com']->phones);\n        self::assertCount(3, $result['bar@bar.com']->phones);\n        self::assertCount(3, $result['foobar@foobar.com']->phones);\n\n        self::assertArrayHasKey(1, $result['foo@foo.com']->phones->toArray());\n        self::assertArrayHasKey(2, $result['foo@foo.com']->phones->toArray());\n        self::assertArrayHasKey(3, $result['foo@foo.com']->phones->toArray());\n\n        self::assertArrayHasKey(4, $result['bar@bar.com']->phones->toArray());\n        self::assertArrayHasKey(5, $result['bar@bar.com']->phones->toArray());\n        self::assertArrayHasKey(6, $result['bar@bar.com']->phones->toArray());\n\n        self::assertArrayHasKey(7, $result['foobar@foobar.com']->phones->toArray());\n        self::assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());\n        self::assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());\n\n        self::assertEquals('SELECT u, p FROM ' . __NAMESPACE__ . '\\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);\n    }\n\n    private function loadFixture(): void\n    {\n        $p1 = ['11 xxxx-xxxx', '11 yyyy-yyyy', '11 zzzz-zzzz'];\n        $p2 = ['22 xxxx-xxxx', '22 yyyy-yyyy', '22 zzzz-zzzz'];\n        $p3 = ['33 xxxx-xxxx', '33 yyyy-yyyy', '33 zzzz-zzzz'];\n\n        $u1 = new DDC1335User('foo@foo.com', 'Foo', $p1);\n        $u2 = new DDC1335User('bar@bar.com', 'Bar', $p2);\n        $u3 = new DDC1335User('foobar@foobar.com', 'Foo Bar', $p3);\n\n        $this->_em->persist($u1);\n        $this->_em->persist($u2);\n        $this->_em->persist($u3);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n\n#[Entity]\nclass DDC1335User\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public int|null $id = null;\n\n    /** @phpstan-var Collection<int, DDC1335Phone> */\n    #[OneToMany(targetEntity: 'DDC1335Phone', mappedBy: 'user', cascade: ['persist', 'remove'])]\n    public Collection $phones;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255, unique: true)]\n        public string $email,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n        array $numbers = [],\n    ) {\n        $this->phones = new ArrayCollection();\n\n        foreach ($numbers as $number) {\n            $this->phones->add(new DDC1335Phone($this, $number));\n        }\n    }\n}\n\n#[Entity]\nclass DDC1335Phone\n{\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue]\n    public int|null $id = null;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC1335User', inversedBy: 'phones')]\n        #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)]\n        public DDC1335User $user,\n        #[Column(name: 'numericalValue', type: 'string', nullable: false)]\n        public string $numericalValue,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1400Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1400')]\nclass DDC1400Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1400Article::class,\n            DDC1400User::class,\n            DDC1400UserState::class,\n        );\n    }\n\n    public function testFailingCase(): void\n    {\n        $article = new DDC1400Article();\n        $user1   = new DDC1400User();\n        $user2   = new DDC1400User();\n\n        $this->_em->persist($article);\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n\n        $userState1            = new DDC1400UserState();\n        $userState1->article   = $article;\n        $userState1->articleId = $article->id;\n        $userState1->user      = $user1;\n        $userState1->userId    = $user1->id;\n\n        $userState2            = new DDC1400UserState();\n        $userState2->article   = $article;\n        $userState2->articleId = $article->id;\n        $userState2->user      = $user2;\n        $userState2->userId    = $user2->id;\n\n        $this->_em->persist($userState1);\n        $this->_em->persist($userState2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user1 = $this->_em->getReference(DDC1400User::class, $user1->id);\n\n        $this->_em->createQuery('SELECT a, s FROM ' . DDC1400Article::class . ' a JOIN a.userStates s WITH s.user = :activeUser')\n                  ->setParameter('activeUser', $user1)\n                  ->getResult();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $this->_em->flush();\n\n        $this->assertQueryCount(0, 'No query should be executed during flush in this case');\n    }\n}\n\n#[Entity]\nclass DDC1400Article\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC1400UserState> */\n    #[OneToMany(targetEntity: 'DDC1400UserState', mappedBy: 'article', indexBy: 'userId', fetch: 'EXTRA_LAZY')]\n    public $userStates;\n}\n\n#[Entity]\nclass DDC1400User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC1400UserState> */\n    #[OneToMany(targetEntity: 'DDC1400UserState', mappedBy: 'user', indexBy: 'articleId', fetch: 'EXTRA_LAZY')]\n    public $userStates;\n}\n\n#[Entity]\nclass DDC1400UserState\n{\n    /** @var DDC1400Article */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1400Article', inversedBy: 'userStates')]\n    public $article;\n\n    /** @var DDC1400User */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1400User', inversedBy: 'userStates')]\n    public $user;\n\n    /** @var int */\n    #[Column(name: 'user_id', type: 'integer')]\n    public $userId;\n\n    /** @var int */\n    #[Column(name: 'article_id', type: 'integer')]\n    public $articleId;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC142Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Quote\\Address;\nuse Doctrine\\Tests\\Models\\Quote\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1845')]\n#[Group('DDC-142')]\nclass DDC142Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('quote');\n\n        parent::setUp();\n    }\n\n    public function testCreateRetrieveUpdateDelete(): void\n    {\n        $user       = new User();\n        $user->name = 'FabioBatSilva';\n        $this->_em->persist($user);\n\n        $address      = new Address();\n        $address->zip = '12345';\n        $this->_em->persist($address);\n\n        $this->_em->flush();\n\n        $addressRef = $this->_em->getReference(Address::class, $address->getId());\n\n        $user->setAddress($addressRef);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $id = $user->id;\n        self::assertNotNull($id);\n\n        $user    = $this->_em->find(User::class, $id);\n        $address = $user->getAddress();\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertInstanceOf(Address::class, $user->getAddress());\n\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals('12345', $address->zip);\n\n        $user->name    = 'FabioBatSilva1';\n        $user->address = null;\n\n        $this->_em->persist($user);\n        $this->_em->remove($address);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $id);\n        self::assertInstanceOf(User::class, $user);\n        self::assertNull($user->getAddress());\n\n        self::assertEquals('FabioBatSilva1', $user->name);\n\n        $this->_em->remove($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(User::class, $id));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1430Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1430')]\nclass DDC1430Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        try {\n            $this->_schemaTool->createSchema(\n                [\n                    $this->_em->getClassMetadata(DDC1430Order::class),\n                    $this->_em->getClassMetadata(DDC1430OrderProduct::class),\n                ],\n            );\n            $this->loadFixtures();\n        } catch (Exception) {\n        }\n    }\n\n    public function testOrderByFields(): void\n    {\n        $repository = $this->_em->getRepository(DDC1430Order::class);\n        $builder    = $repository->createQueryBuilder('o');\n        $query      = $builder->select('o.id, o.date, COUNT(p.id) AS p_count')\n                        ->leftJoin('o.products', 'p')\n                        ->groupBy('o.id, o.date')\n                        ->orderBy('o.id')\n                        ->getQuery();\n\n        $this->assertSQLEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL());\n        $this->assertSQLEquals('SELECT d0_.order_id AS order_id_0, d0_.created_at AS created_at_1, COUNT(d1_.id) AS sclr_2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());\n\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n\n        self::assertArrayHasKey('id', $result[0]);\n        self::assertArrayHasKey('id', $result[1]);\n\n        self::assertArrayHasKey('p_count', $result[0]);\n        self::assertArrayHasKey('p_count', $result[1]);\n\n        self::assertEquals(1, $result[0]['id']);\n        self::assertEquals(2, $result[1]['id']);\n\n        self::assertEquals(2, $result[0]['p_count']);\n        self::assertEquals(3, $result[1]['p_count']);\n    }\n\n    public function testOrderByAllObjectFields(): void\n    {\n        $repository = $this->_em->getRepository(DDC1430Order::class);\n        $builder    = $repository->createQueryBuilder('o');\n        $query      = $builder->select('o, COUNT(p.id) AS p_count')\n                        ->leftJoin('o.products', 'p')\n                        ->groupBy('o.id, o.date, o.status')\n                        ->orderBy('o.id')\n                        ->getQuery();\n\n        $this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL());\n        $this->assertSQLEquals('SELECT d0_.order_id AS order_id_0, d0_.created_at AS created_at_1, d0_.order_status AS order_status_2, COUNT(d1_.id) AS sclr_3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());\n\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n\n        self::assertInstanceOf(DDC1430Order::class, $result[0][0]);\n        self::assertInstanceOf(DDC1430Order::class, $result[1][0]);\n\n        self::assertEquals($result[0][0]->getId(), 1);\n        self::assertEquals($result[1][0]->getId(), 2);\n\n        self::assertEquals($result[0]['p_count'], 2);\n        self::assertEquals($result[1]['p_count'], 3);\n    }\n\n    public function testTicket(): void\n    {\n        $repository = $this->_em->getRepository(DDC1430Order::class);\n        $builder    = $repository->createQueryBuilder('o');\n        $query      = $builder->select('o, COUNT(p.id) AS p_count')\n                        ->leftJoin('o.products', 'p')\n                        ->groupBy('o')\n                        ->orderBy('o.id')\n                        ->getQuery();\n\n        $this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL());\n        $this->assertSQLEquals('SELECT d0_.order_id AS order_id_0, d0_.created_at AS created_at_1, d0_.order_status AS order_status_2, COUNT(d1_.id) AS sclr_3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());\n\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n\n        self::assertInstanceOf(DDC1430Order::class, $result[0][0]);\n        self::assertInstanceOf(DDC1430Order::class, $result[1][0]);\n\n        self::assertEquals($result[0][0]->getId(), 1);\n        self::assertEquals($result[1][0]->getId(), 2);\n\n        self::assertEquals($result[0]['p_count'], 2);\n        self::assertEquals($result[1]['p_count'], 3);\n    }\n\n    public function loadFixtures(): void\n    {\n        $o1 = new DDC1430Order('NEW');\n        $o2 = new DDC1430Order('OK');\n\n        $o1->addProduct(new DDC1430OrderProduct(1.1));\n        $o1->addProduct(new DDC1430OrderProduct(1.2));\n\n        $o2->addProduct(new DDC1430OrderProduct(2.1));\n        $o2->addProduct(new DDC1430OrderProduct(2.2));\n        $o2->addProduct(new DDC1430OrderProduct(2.3));\n\n        $this->_em->persist($o1);\n        $this->_em->persist($o2);\n\n        $this->_em->flush();\n    }\n}\n\n#[Entity]\nclass DDC1430Order\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'order_id', type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    #[Column(name: 'created_at', type: 'datetime')]\n    private DateTime $date;\n\n    #[OneToMany(targetEntity: 'DDC1430OrderProduct', mappedBy: 'order', cascade: ['persist', 'remove'])]\n    private Collection $products;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function __construct(\n        #[Column(name: 'order_status', type: 'string', length: 255)]\n        private string $status,\n    ) {\n        $this->date     = new DateTime();\n        $this->products = new ArrayCollection();\n    }\n\n    public function getDate(): DateTime\n    {\n        return $this->date;\n    }\n\n    public function getStatus(): string\n    {\n        return $this->status;\n    }\n\n    public function setStatus(string $status): void\n    {\n        $this->status = $status;\n    }\n\n    public function getProducts(): ArrayCollection\n    {\n        return $this->products;\n    }\n\n    public function addProduct(DDC1430OrderProduct $product): void\n    {\n        $product->setOrder($this);\n        $this->products->add($product);\n    }\n}\n\n#[Entity]\nclass DDC1430OrderProduct\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    #[ManyToOne(targetEntity: 'DDC1430Order', inversedBy: 'products')]\n    #[JoinColumn(name: 'order_id', referencedColumnName: 'order_id', nullable: false)]\n    private DDC1430Order|null $order = null;\n\n    public function __construct(\n        #[Column(type: 'float')]\n        private float $value,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getOrder(): DDC1430Order\n    {\n        return $this->order;\n    }\n\n    public function setOrder(DDC1430Order $order): void\n    {\n        $this->order = $order;\n    }\n\n    public function getValue(): float\n    {\n        return $this->value;\n    }\n\n    public function setValue(float $value): void\n    {\n        $this->value = $value;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1436Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1436')]\nclass DDC1436Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1436Page::class);\n    }\n\n    public function testIdentityMap(): void\n    {\n        // fixtures\n        $parent = null;\n        for ($i = 0; $i < 3; $i++) {\n            $page = new DDC1436Page();\n            $page->setParent($parent);\n            $this->_em->persist($page);\n            $parent = $page;\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $id = $parent->getId();\n\n        // step 1\n        $page = $this->_em\n                ->createQuery('SELECT p, parent FROM ' . __NAMESPACE__ . '\\DDC1436Page p LEFT JOIN p.parent parent WHERE p.id = :id')\n                ->setParameter('id', $id)\n                ->getOneOrNullResult();\n\n        self::assertInstanceOf(DDC1436Page::class, $page);\n\n        // step 2\n        $page = $this->_em->find(DDC1436Page::class, $id);\n        self::assertInstanceOf(DDC1436Page::class, $page);\n        self::assertInstanceOf(DDC1436Page::class, $page->getParent());\n        self::assertInstanceOf(DDC1436Page::class, $page->getParent()->getParent());\n    }\n}\n\n#[Entity]\nclass DDC1436Page\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: 'id')]\n    protected $id;\n\n    /** @var DDC1436Page */\n    #[ManyToOne(targetEntity: 'DDC1436Page')]\n    #[JoinColumn(name: 'pid', referencedColumnName: 'id')]\n    protected $parent;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getParent(): DDC1436Page\n    {\n        return $this->parent;\n    }\n\n    public function setParent($parent): void\n    {\n        $this->parent = $parent;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC144Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC144Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC144FlowElement::class,\n            DDC144Operand::class,\n        );\n    }\n\n    #[Group('DDC-144')]\n    public function testIssue(): void\n    {\n        $operand                  = new DDC144Operand();\n        $operand->property        = 'flowValue';\n        $operand->operandProperty = 'operandValue';\n\n        $this->_em->persist($operand);\n        $this->_em->flush();\n\n        self::assertSame($operand, $this->_em->find(DDC144Operand::class, $operand->id));\n    }\n}\n\n#[Table(name: 'ddc144_flowelements')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(type: 'string', name: 'discr')]\n#[DiscriminatorMap(['flowelement' => 'DDC144FlowElement', 'operand' => 'DDC144Operand'])]\nclass DDC144FlowElement\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $property;\n}\n\nabstract class DDC144Expression extends DDC144FlowElement\n{\n    abstract public function method(): void;\n}\n\n#[Table(name: 'ddc144_operands')]\n#[Entity]\nclass DDC144Operand extends DDC144Expression\n{\n    /** @var string */\n    #[Column]\n    public $operandProperty;\n\n    public function method(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1452Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1452')]\nclass DDC1452Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1452EntityA::class,\n            DDC1452EntityB::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $a1        = new DDC1452EntityA();\n        $a1->title = 'foo';\n\n        $a2        = new DDC1452EntityA();\n        $a2->title = 'bar';\n\n        $b              = new DDC1452EntityB();\n        $b->entityAFrom = $a1;\n        $b->entityATo   = $a2;\n\n        $this->_em->persist($a1);\n        $this->_em->persist($a2);\n        $this->_em->persist($b);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql     = 'SELECT a, b, ba FROM ' . __NAMESPACE__ . '\\DDC1452EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba ORDER BY a.id';\n        $results = $this->_em->createQuery($dql)->setMaxResults(1)->getResult();\n\n        self::assertSame($results[0], $results[0]->entitiesB[0]->entityAFrom);\n        self::assertFalse($this->isUninitializedObject($results[0]->entitiesB[0]->entityATo));\n        self::assertInstanceOf(Collection::class, $results[0]->entitiesB[0]->entityATo->getEntitiesB());\n    }\n\n    public function testFetchJoinOneToOneFromInverse(): void\n    {\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->country = 'Germany';\n        $address->street  = 'Somestreet';\n        $address->zip     = 12345;\n\n        $user           = new CmsUser();\n        $user->name     = 'beberlei';\n        $user->username = 'beberlei';\n        $user->status   = 'active';\n        $user->address  = $address;\n        $address->user  = $user;\n\n        $this->_em->persist($address);\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql  = 'SELECT a, u FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a INNER JOIN a.user u';\n        $data = $this->_em->createQuery($dql)->getResult();\n        $this->_em->clear();\n\n        self::assertFalse($this->isUninitializedObject($data[0]->user));\n\n        $dql  = 'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.address a';\n        $data = $this->_em->createQuery($dql)->getResult();\n\n        self::assertFalse($this->isUninitializedObject($data[0]->address));\n    }\n}\n\n#[Entity]\nclass DDC1452EntityA\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $title;\n\n    /** @phpstan-var Collection<int, DDC1452EntityB> */\n    #[OneToMany(targetEntity: 'DDC1452EntityB', mappedBy: 'entityAFrom')]\n    public $entitiesB;\n\n    public function __construct()\n    {\n        $this->entitiesB = new ArrayCollection();\n    }\n\n    /** @phpstan-return Collection<int, DDC1452EntityB> */\n    public function getEntitiesB(): Collection\n    {\n        return $this->entitiesB;\n    }\n}\n\n#[Entity]\nclass DDC1452EntityB\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC1452EntityA */\n    #[ManyToOne(targetEntity: 'DDC1452EntityA', inversedBy: 'entitiesB')]\n    public $entityAFrom;\n    /** @var DDC1452EntityA */\n    #[ManyToOne(targetEntity: 'DDC1452EntityA')]\n    public $entityATo;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1454Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function mt_getrandmax;\nuse function random_int;\n\n#[Group('DDC-1454')]\nclass DDC1454Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1454File::class, DDC1454Picture::class);\n    }\n\n    public function testFailingCase(): void\n    {\n        $pic = new DDC1454Picture();\n\n        self::assertSame(UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($pic));\n    }\n}\n\n#[Entity]\nclass DDC1454Picture extends DDC1454File\n{\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['file' => 'DDC1454File', 'picture' => 'DDC1454Picture'])]\nclass DDC1454File\n{\n    /** @var int */\n    #[Column(name: 'file_id', type: 'integer')]\n    #[Id]\n    public $fileId;\n\n    public function __construct()\n    {\n        $this->fileId = random_int(0, mt_getrandmax());\n    }\n\n    public function getFileId(): int\n    {\n        return $this->fileId;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1458Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC1458Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            TestEntity::class,\n            TestAdditionalEntity::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $testEntity = new TestEntity();\n        $testEntity->setValue(3);\n        $testEntity->setAdditional(new TestAdditionalEntity());\n        $this->_em->persist($testEntity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // So here the value is 3\n        self::assertEquals(3, $testEntity->getValue());\n\n        $test = $this->_em->getRepository(TestEntity::class)->find(1);\n\n        // New value is set\n        $test->setValue(5);\n\n        // So here the value is 5\n        self::assertEquals(5, $test->getValue());\n\n        // Get the additional entity\n        $additional = $test->getAdditional();\n\n        // Still 5..\n        self::assertEquals(5, $test->getValue());\n\n        // Force the proxy to load\n        $additional->getBool();\n\n        // The value should still be 5\n        self::assertEquals(5, $test->getValue());\n    }\n}\n\n\n#[Entity]\nclass TestEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    protected $value;\n\n    /** @var TestAdditionalEntity */\n    #[OneToOne(targetEntity: 'TestAdditionalEntity', inversedBy: 'entity', orphanRemoval: true, cascade: ['persist', 'remove'])]\n    protected $additional;\n\n    public function getValue(): int\n    {\n        return $this->value;\n    }\n\n    public function setValue(int $value): void\n    {\n        $this->value = $value;\n    }\n\n    public function getAdditional(): TestAdditionalEntity\n    {\n        return $this->additional;\n    }\n\n    public function setAdditional(TestAdditionalEntity $additional): void\n    {\n        $this->additional = $additional;\n    }\n}\n#[Entity]\nclass TestAdditionalEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n    /** @var TestEntity */\n    #[OneToOne(targetEntity: 'TestEntity', mappedBy: 'additional')]\n    protected $entity;\n\n    /** @var bool */\n    #[Column(type: 'boolean')]\n    protected $bool;\n\n    public function __construct()\n    {\n        $this->bool = false;\n    }\n\n    public function getBool(): bool\n    {\n        return $this->bool;\n    }\n\n    public function setBool(bool $bool): void\n    {\n        $this->bool = $bool;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1461Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1461')]\nclass DDC1461Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1461TwitterAccount::class,\n            DDC1461User::class,\n        );\n    }\n\n    public function testChangeDetectionDeferredExplicit(): void\n    {\n        $user = new DDC1461User();\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user, UnitOfWork::STATE_NEW), 'Entity should be managed.');\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user), 'Entity should be managed.');\n\n        $acc                  = new DDC1461TwitterAccount();\n        $user->twitterAccount = $acc;\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $user = $this->_em->find($user::class, $user->id);\n        self::assertNotNull($user->twitterAccount);\n    }\n}\n\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass DDC1461User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC1461TwitterAccount */\n    #[OneToOne(targetEntity: 'DDC1461TwitterAccount', orphanRemoval: true, fetch: 'EAGER', cascade: ['persist'], inversedBy: 'user')]\n    public $twitterAccount;\n}\n\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass DDC1461TwitterAccount\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC1461User */\n    #[OneToOne(targetEntity: 'DDC1461User', fetch: 'EAGER')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1514Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1514')]\nclass DDC1514Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1514EntityA::class,\n            DDC1514EntityB::class,\n            DDC1514EntityC::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $a1        = new DDC1514EntityA();\n        $a1->title = '1foo';\n\n        $a2        = new DDC1514EntityA();\n        $a2->title = '2bar';\n\n        $b1              = new DDC1514EntityB();\n        $b1->entityAFrom = $a1;\n        $b1->entityATo   = $a2;\n\n        $b2              = new DDC1514EntityB();\n        $b2->entityAFrom = $a2;\n        $b2->entityATo   = $a1;\n\n        $c           = new DDC1514EntityC();\n        $c->title    = 'baz';\n        $a2->entityC = $c;\n\n        $this->_em->persist($a1);\n        $this->_em->persist($a2);\n        $this->_em->persist($b1);\n        $this->_em->persist($b2);\n        $this->_em->persist($c);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql     = 'SELECT a, b, ba, c FROM ' . __NAMESPACE__ . '\\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c ORDER BY a.title';\n        $results = $this->_em->createQuery($dql)->getResult();\n\n        self::assertEquals($a1->id, $results[0]->id);\n        self::assertNull($results[0]->entityC);\n\n        self::assertEquals($a2->id, $results[1]->id);\n        self::assertEquals($c->title, $results[1]->entityC->title);\n    }\n}\n\n#[Entity]\nclass DDC1514EntityA\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $title;\n\n    /** @phpstan-var Collection<int, DDC1514EntityB> */\n    #[ManyToMany(targetEntity: 'DDC1514EntityB', mappedBy: 'entityAFrom')]\n    public $entitiesB;\n\n    /** @var DDC1514EntityC */\n    #[ManyToOne(targetEntity: 'DDC1514EntityC')]\n    public $entityC;\n\n    public function __construct()\n    {\n        $this->entitiesB = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC1514EntityB\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC1514EntityA */\n    #[ManyToOne(targetEntity: 'DDC1514EntityA', inversedBy: 'entitiesB')]\n    public $entityAFrom;\n    /** @var DDC1514EntityA */\n    #[ManyToOne(targetEntity: 'DDC1514EntityA')]\n    public $entityATo;\n}\n\n#[Entity]\nclass DDC1514EntityC\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $title;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1515Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1515')]\nclass DDC1515Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1515Foo::class,\n            DDC1515Bar::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $bar = new DDC1515Bar();\n        $this->_em->persist($bar);\n        $this->_em->flush();\n\n        $foo      = new DDC1515Foo();\n        $foo->bar = $bar;\n        $this->_em->persist($foo);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $bar = $this->_em->find(DDC1515Bar::class, $bar->id);\n        self::assertInstanceOf(DDC1515Foo::class, $bar->foo);\n    }\n}\n\n#[Entity]\nclass DDC1515Foo\n{\n    /** @var DDC1515Bar */\n    #[OneToOne(targetEntity: 'DDC1515Bar', inversedBy: 'foo')]\n    #[Id]\n    public $bar;\n}\n\n#[Entity]\nclass DDC1515Bar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC1515Foo */\n    #[OneToOne(targetEntity: 'DDC1515Foo', mappedBy: 'bar')]\n    public $foo;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1526Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1526')]\nclass DDC1526Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1526Menu::class);\n    }\n\n    public function testIssue(): void\n    {\n        $parents = [];\n        for ($i = 0; $i < 9; $i++) {\n            $entity = new DDC1526Menu();\n\n            if (isset($parents[$i % 3])) {\n                $entity->parent = $parents[$i % 3];\n            }\n\n            $this->_em->persist($entity);\n            $parents[$i] = $entity;\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql   = 'SELECT m, c\n            FROM ' . __NAMESPACE__ . '\\DDC1526Menu m\n            LEFT JOIN m.children c';\n        $menus = $this->_em->createQuery($dql)->getResult();\n\n        // All Children collection now have to be initialized\n        foreach ($menus as $menu) {\n            self::assertTrue($menu->children->isInitialized());\n        }\n    }\n}\n\n#[Entity]\nclass DDC1526Menu\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n    /** @var DDC1526Menu */\n    #[ManyToOne(targetEntity: 'DDC1526Menu', inversedBy: 'children')]\n    public $parent;\n\n    /** @phpstan-var Collection<int, DDC1526Menu> */\n    #[OneToMany(targetEntity: 'DDC1526Menu', mappedBy: 'parent')]\n    public $children;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1545Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1545')]\nclass DDC1545Test extends OrmFunctionalTestCase\n{\n    private int|null $articleId = null;\n\n    private int|null $userId = null;\n\n    private int|null $user2Id = null;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    private function initDb(bool $link): void\n    {\n        $article        = new CmsArticle();\n        $article->topic = 'foo';\n        $article->text  = 'foo';\n\n        $user           = new CmsUser();\n        $user->status   = 'foo';\n        $user->username = 'foo';\n        $user->name     = 'foo';\n\n        $user2           = new CmsUser();\n        $user2->status   = 'bar';\n        $user2->username = 'bar';\n        $user2->name     = 'bar';\n\n        if ($link) {\n            $article->user = $user;\n        }\n\n        $this->_em->persist($article);\n        $this->_em->persist($user);\n        $this->_em->persist($user2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->articleId = $article->id;\n        $this->userId    = $user->id;\n        $this->user2Id   = $user2->id;\n    }\n\n    public function testLinkObjects(): void\n    {\n        $this->initDb(false);\n\n        // don't join association\n        $article = $this->_em->find(CmsArticle::class, $this->articleId);\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $article->user = $user;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNotNull($article->user);\n        self::assertEquals($user->id, $article->user->id);\n    }\n\n    public function testLinkObjectsWithAssociationLoaded(): void\n    {\n        $this->initDb(false);\n\n        // join association\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        $user = $this->_em->find(CmsUser::class, $this->userId);\n\n        $article->user = $user;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNotNull($article->user);\n        self::assertEquals($user->id, $article->user->id);\n    }\n\n    public function testUnlinkObjects(): void\n    {\n        $this->initDb(true);\n\n        // don't join association\n        $article = $this->_em->find(CmsArticle::class, $this->articleId);\n\n        $article->user = null;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNull($article->user);\n    }\n\n    public function testUnlinkObjectsWithAssociationLoaded(): void\n    {\n        $this->initDb(true);\n\n        // join association\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        $article->user = null;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNull($article->user);\n    }\n\n    public function testChangeLink(): void\n    {\n        $this->initDb(false);\n\n        // don't join association\n        $article = $this->_em->find(CmsArticle::class, $this->articleId);\n\n        $user2 = $this->_em->find(CmsUser::class, $this->user2Id);\n\n        $article->user = $user2;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNotNull($article->user);\n        self::assertEquals($user2->id, $article->user->id);\n    }\n\n    public function testChangeLinkWithAssociationLoaded(): void\n    {\n        $this->initDb(false);\n\n        // join association\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        $user2 = $this->_em->find(CmsUser::class, $this->user2Id);\n\n        $article->user = $user2;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article = $this->_em\n            ->createQuery('SELECT a, u FROM Doctrine\\Tests\\Models\\Cms\\CmsArticle a LEFT JOIN a.user u WHERE a.id = :id')\n            ->setParameter('id', $this->articleId)\n            ->getOneOrNullResult();\n\n        self::assertNotNull($article->user);\n        self::assertEquals($user2->id, $article->user->id);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1548Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1548')]\nclass DDC1548Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1548E1::class,\n            DDC1548E2::class,\n            DDC1548Rel::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $rel = new DDC1548Rel();\n        $this->_em->persist($rel);\n        $this->_em->flush();\n\n        $e1      = new DDC1548E1();\n        $e1->rel = $rel;\n        $this->_em->persist($e1);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $obt = $this->_em->find(DDC1548Rel::class, $rel->id);\n\n        self::assertNull($obt->e2);\n    }\n}\n\n#[Entity]\nclass DDC1548E1\n{\n    /** @var DDC1548Rel */\n    #[Id]\n    #[OneToOne(targetEntity: 'DDC1548Rel', inversedBy: 'e1')]\n    public $rel;\n}\n\n#[Entity]\nclass DDC1548E2\n{\n    /** @var DDC1548Rel */\n    #[Id]\n    #[OneToOne(targetEntity: 'DDC1548Rel', inversedBy: 'e2')]\n    public $rel;\n}\n\n#[Entity]\nclass DDC1548Rel\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC1548E1 */\n    #[OneToOne(targetEntity: 'DDC1548E1', mappedBy: 'rel')]\n    public $e1;\n    /** @var DDC1548E2 */\n    #[OneToOne(targetEntity: 'DDC1548E2', mappedBy: 'rel')]\n    public $e2;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1595Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1595')]\n#[Group('DDC-1596')]\n#[Group('non-cacheable')]\nclass DDC1595Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1595BaseInheritance::class,\n            DDC1595InheritedEntity1::class,\n            DDC1595InheritedEntity2::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $e1 = new DDC1595InheritedEntity1();\n\n        $this->_em->persist($e1);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(DDC1595InheritedEntity1::class);\n\n        $entity1 = $repository->find($e1->id);\n\n        // DDC-1596\n        $this->assertSQLEquals(\n            \"SELECT t0.id AS id_1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')\",\n            $this->getLastLoggedQuery()['sql'],\n        );\n\n        $entities = $entity1->getEntities()->getValues();\n\n        self::assertEquals(\n            \"SELECT t0.id AS id_1, t0.type FROM base t0 INNER JOIN entity1_entity2 ON t0.id = entity1_entity2.item WHERE entity1_entity2.parent = ? AND t0.type IN ('Entity2')\",\n            $this->getLastLoggedQuery()['sql'],\n        );\n\n        $this->_em->clear();\n\n        $entity1  = $repository->find($e1->id);\n        $entities = $entity1->getEntities()->count();\n\n        $this->assertSQLEquals(\n            'SELECT COUNT(*) FROM entity1_entity2 t WHERE t.parent = ?',\n            $this->getLastLoggedQuery()['sql'],\n        );\n    }\n}\n\n#[Table(name: 'base')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['Entity1' => 'DDC1595InheritedEntity1', 'Entity2' => 'DDC1595InheritedEntity2'])]\nabstract class DDC1595BaseInheritance\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n}\n\n#[Table(name: 'entity1')]\n#[Entity]\nclass DDC1595InheritedEntity1 extends DDC1595BaseInheritance\n{\n    /** @phpstan-var Collection<int, DDC1595InheritedEntity2> */\n    #[JoinTable(name: 'entity1_entity2')]\n    #[JoinColumn(name: 'parent', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'item', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC1595InheritedEntity2', fetch: 'EXTRA_LAZY')]\n    protected $entities;\n\n    /** @phpstan-return Collection<int, DDC1595InheritedEntity2> */\n    public function getEntities(): Collection\n    {\n        return $this->entities;\n    }\n}\n\n#[Table(name: 'entity2')]\n#[Entity]\nclass DDC1595InheritedEntity2 extends DDC1595BaseInheritance\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC163Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC163Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-163')]\n    public function testQueryWithOrConditionUsingTwoRelationOnSameEntity(): void\n    {\n        $p1 = new CompanyPerson();\n        $p1->setName('p1');\n\n        $p2 = new CompanyPerson();\n        $p2->setName('p2');\n\n        $p3 = new CompanyPerson();\n        $p3->setName('p3');\n\n        $p4 = new CompanyPerson();\n        $p4->setName('p4');\n\n        $p1->setSpouse($p3);\n        $p1->addFriend($p2);\n        $p2->addFriend($p3);\n\n        $p3->addFriend($p4);\n\n        $this->_em->persist($p1);\n        $this->_em->persist($p2);\n        $this->_em->persist($p3);\n        $this->_em->persist($p4);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql = 'SELECT PARTIAL person.{id,name}, PARTIAL spouse.{id,name}, PARTIAL friend.{id,name}\n            FROM  Doctrine\\Tests\\Models\\Company\\CompanyPerson person\n            LEFT JOIN person.spouse spouse\n            LEFT JOIN person.friends friend\n            LEFT JOIN spouse.friends spouse_friend\n            LEFT JOIN friend.friends friend_friend\n            WHERE person.name=:name AND (spouse_friend.name=:name2 OR friend_friend.name=:name2)';\n\n        $q = $this->_em->createQuery($dql);\n        $q->setParameter('name', 'p1');\n        $q->setParameter('name2', 'p4');\n        $result = $q->getScalarResult();\n\n        self::assertEquals('p3', $result[0]['spouse_name']);\n        self::assertEquals('p1', $result[0]['person_name']);\n        self::assertEquals('p2', $result[0]['friend_name']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1643Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1643')]\nclass DDC1643Test extends OrmFunctionalTestCase\n{\n    private CmsUser|null $user1;\n\n    private CmsUser|null $user2;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $user1           = new CmsUser();\n        $user1->username = 'beberlei';\n        $user1->name     = 'Benjamin';\n        $user1->status   = 'active';\n        $group1          = new CmsGroup();\n        $group1->name    = 'test';\n        $group2          = new CmsGroup();\n        $group2->name    = 'test';\n        $user1->addGroup($group1);\n        $user1->addGroup($group2);\n        $user2           = new CmsUser();\n        $user2->username = 'romanb';\n        $user2->name     = 'Roman';\n        $user2->status   = 'active';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($group1);\n        $this->_em->persist($group2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user1 = $this->_em->find($user1::class, $user1->id);\n        $this->user2 = $this->_em->find($user1::class, $user2->id);\n    }\n\n    public function testClonePersistentCollectionAndReuse(): void\n    {\n        $user1 = $this->user1;\n\n        $user1->groups = clone $user1->groups;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user1 = $this->_em->find($user1::class, $user1->id);\n\n        self::assertCount(2, $user1->groups);\n    }\n\n    public function testClonePersistentCollectionAndShare(): void\n    {\n        $user1 = $this->user1;\n        $user2 = $this->user2;\n\n        $user2->groups = clone $user1->groups;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user1 = $this->_em->find($user1::class, $user1->id);\n        $user2 = $this->_em->find($user1::class, $user2->id);\n\n        self::assertCount(2, $user1->groups);\n        self::assertCount(2, $user2->groups);\n    }\n\n    public function testCloneThenDirtyPersistentCollection(): void\n    {\n        $user1 = $this->user1;\n        $user2 = $this->user2;\n\n        $group3        = new CmsGroup();\n        $group3->name  = 'test';\n        $user2->groups = clone $user1->groups;\n        $user2->groups->add($group3);\n\n        $this->_em->persist($group3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user1 = $this->_em->find($user1::class, $user1->id);\n        $user2 = $this->_em->find($user1::class, $user2->id);\n\n        self::assertCount(3, $user2->groups);\n        self::assertCount(2, $user1->groups);\n    }\n\n    public function testNotCloneAndPassAroundFlush(): void\n    {\n        $user1 = $this->user1;\n        $user2 = $this->user2;\n\n        $group3        = new CmsGroup();\n        $group3->name  = 'test';\n        $user2->groups = $user1->groups;\n        $user2->groups->add($group3);\n\n        self::assertCount(1, $user1->groups->getInsertDiff());\n\n        $this->_em->persist($group3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user1 = $this->_em->find($user1::class, $user1->id);\n        $user2 = $this->_em->find($user1::class, $user2->id);\n\n        self::assertCount(3, $user2->groups);\n        self::assertCount(3, $user1->groups);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1654Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1654')]\nclass DDC1654Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                DDC1654Post::class,\n                DDC1654Comment::class,\n            ],\n        );\n    }\n\n    public function tearDown(): void\n    {\n        $conn = static::$sharedConn;\n        $conn->executeStatement('DELETE FROM ddc1654post_ddc1654comment');\n        $conn->executeStatement('DELETE FROM DDC1654Comment');\n        $conn->executeStatement('DELETE FROM DDC1654Post');\n    }\n\n    public function testManyToManyRemoveFromCollectionOrphanRemoval(): void\n    {\n        $post             = new DDC1654Post();\n        $post->comments[] = new DDC1654Comment();\n        $post->comments[] = new DDC1654Comment();\n\n        $this->_em->persist($post);\n        $this->_em->flush();\n\n        $post->comments->remove(0);\n        $post->comments->remove(1);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $comments = $this->_em->getRepository(DDC1654Comment::class)->findAll();\n        self::assertCount(0, $comments);\n    }\n\n    public function testManyToManyRemoveElementFromCollectionOrphanRemoval(): void\n    {\n        $post             = new DDC1654Post();\n        $post->comments[] = new DDC1654Comment();\n        $post->comments[] = new DDC1654Comment();\n\n        $this->_em->persist($post);\n        $this->_em->flush();\n\n        $post->comments->removeElement($post->comments[0]);\n        $post->comments->removeElement($post->comments[1]);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $comments = $this->_em->getRepository(DDC1654Comment::class)->findAll();\n        self::assertCount(0, $comments);\n    }\n\n    #[Group('DDC-3382')]\n    public function testManyToManyRemoveElementFromReAddToCollectionOrphanRemoval(): void\n    {\n        $post             = new DDC1654Post();\n        $post->comments[] = new DDC1654Comment();\n        $post->comments[] = new DDC1654Comment();\n\n        $this->_em->persist($post);\n        $this->_em->flush();\n\n        $comment = $post->comments[0];\n        $post->comments->removeElement($comment);\n        $post->comments->add($comment);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $comments = $this->_em->getRepository(DDC1654Comment::class)->findAll();\n        self::assertCount(2, $comments);\n    }\n\n    public function testManyToManyClearCollectionOrphanRemoval(): void\n    {\n        $post             = new DDC1654Post();\n        $post->comments[] = new DDC1654Comment();\n        $post->comments[] = new DDC1654Comment();\n\n        $this->_em->persist($post);\n        $this->_em->flush();\n\n        $post->comments->clear();\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $comments = $this->_em->getRepository(DDC1654Comment::class)->findAll();\n        self::assertCount(0, $comments);\n    }\n\n    #[Group('DDC-3382')]\n    public function testManyToManyClearCollectionReAddOrphanRemoval(): void\n    {\n        $post             = new DDC1654Post();\n        $post->comments[] = new DDC1654Comment();\n        $post->comments[] = new DDC1654Comment();\n\n        $this->_em->persist($post);\n        $this->_em->flush();\n\n        $comment = $post->comments[0];\n        $post->comments->clear();\n        $post->comments->add($comment);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $comments = $this->_em->getRepository(DDC1654Comment::class)->findAll();\n        self::assertCount(1, $comments);\n    }\n}\n\n#[Entity]\nclass DDC1654Post\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC1654Comment> */\n    #[ManyToMany(targetEntity: 'DDC1654Comment', orphanRemoval: true, cascade: ['persist'])]\n    public $comments = [];\n}\n\n#[Entity]\nclass DDC1654Comment\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1655Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\PostLoad;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function get_debug_type;\n\n#[Group('DDC-1655')]\n#[Group('DDC-1640')]\n#[Group('DDC-1556')]\nclass DDC1655Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1655Foo::class,\n            DDC1655Bar::class,\n            DDC1655Baz::class,\n        );\n    }\n\n    public function testPostLoadOneToManyInheritance(): void\n    {\n        $cm = $this->_em->getClassMetadata(DDC1655Foo::class);\n        self::assertEquals(['postLoad' => ['postLoad']], $cm->lifecycleCallbacks);\n\n        $cm = $this->_em->getClassMetadata(DDC1655Bar::class);\n        self::assertEquals(['postLoad' => ['postLoad', 'postSubLoaded']], $cm->lifecycleCallbacks);\n\n        $baz      = new DDC1655Baz();\n        $foo      = new DDC1655Foo();\n        $foo->baz = $baz;\n        $bar      = new DDC1655Bar();\n        $bar->baz = $baz;\n\n        $this->_em->persist($foo);\n        $this->_em->persist($bar);\n        $this->_em->persist($baz);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $baz = $this->_em->find($baz::class, $baz->id);\n        foreach ($baz->foos as $foo) {\n            self::assertEquals(1, $foo->loaded, 'should have loaded callback counter incremented for ' . get_debug_type($foo));\n        }\n    }\n\n    /**\n     * Check that post load is not executed several times when the entity\n     * is rehydrated again although its already known.\n     */\n    public function testPostLoadInheritanceChild(): void\n    {\n        $bar = new DDC1655Bar();\n\n        $this->_em->persist($bar);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $bar = $this->_em->find($bar::class, $bar->id);\n        self::assertEquals(1, $bar->loaded);\n        self::assertEquals(1, $bar->subLoaded);\n\n        $bar = $this->_em->find($bar::class, $bar->id);\n        self::assertEquals(1, $bar->loaded);\n        self::assertEquals(1, $bar->subLoaded);\n\n        $dql = 'SELECT b FROM ' . __NAMESPACE__ . '\\DDC1655Bar b WHERE b.id = ?1';\n        $bar = $this->_em->createQuery($dql)->setParameter(1, $bar->id)->getSingleResult();\n\n        self::assertEquals(1, $bar->loaded);\n        self::assertEquals(1, $bar->subLoaded);\n\n        $this->_em->refresh($bar);\n\n        self::assertEquals(2, $bar->loaded);\n        self::assertEquals(2, $bar->subLoaded);\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['foo' => 'DDC1655Foo', 'bar' => 'DDC1655Bar'])]\n#[HasLifecycleCallbacks]\nclass DDC1655Foo\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var int */\n    public $loaded = 0;\n\n    /** @var DDC1655Baz */\n    #[ManyToOne(targetEntity: 'DDC1655Baz', inversedBy: 'foos')]\n    public $baz;\n\n    #[PostLoad]\n    public function postLoad(): void\n    {\n        $this->loaded++;\n    }\n}\n\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC1655Bar extends DDC1655Foo\n{\n    /** @var int */\n    public $subLoaded;\n\n    #[PostLoad]\n    public function postSubLoaded(): void\n    {\n        $this->subLoaded++;\n    }\n}\n\n#[Entity]\nclass DDC1655Baz\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC1655Foo> */\n    #[OneToMany(targetEntity: 'DDC1655Foo', mappedBy: 'baz')]\n    public $foos = [];\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1666Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1666')]\nclass DDC1666Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testGivenOrphanRemovalOneToOneWhenReplacingThenNoUniqueConstraintError(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'Benjamin';\n        $user->username = 'beberlei';\n        $user->status   = 'something';\n        $user->setEmail($email = new CmsEmail());\n        $email->setEmail('kontakt@beberlei.de');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        self::assertTrue($this->_em->contains($email));\n\n        $user->setEmail($newEmail = new CmsEmail());\n        $newEmail->setEmail('benjamin.eberlei@googlemail.com');\n\n        $this->_em->flush();\n\n        self::assertFalse($this->_em->contains($email));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1685Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse RuntimeException;\n\n#[Group('DDC-1685')]\nclass DDC1685Test extends OrmFunctionalTestCase\n{\n    private Paginator $paginator;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ddc117');\n\n        parent::setUp();\n\n        $this->_em->createQuery('DELETE FROM Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails ad')->execute();\n\n        $article = new DDC117Article('Foo');\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        $articleDetails = new DDC117ArticleDetails($article, 'Very long text');\n        $this->_em->persist($articleDetails);\n        $this->_em->flush();\n\n        $dql   = 'SELECT ad FROM Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails ad';\n        $query = $this->_em->createQuery($dql);\n        $query->setMaxResults(1);\n\n        $this->paginator = new Paginator($query);\n    }\n\n    public function testPaginateCount(): void\n    {\n        self::assertCount(1, $this->paginator);\n    }\n\n    public function testPaginateIterate(): void\n    {\n        foreach ($this->paginator as $ad) {\n            self::assertInstanceOf(DDC117ArticleDetails::class, $ad);\n        }\n    }\n\n    public function testPaginateCountNoOutputWalkers(): void\n    {\n        $this->paginator->setUseOutputWalkers(false);\n        self::assertCount(1, $this->paginator);\n    }\n\n    public function testPaginateIterateNoOutputWalkers(): void\n    {\n        $this->paginator->setUseOutputWalkers(false);\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.');\n\n        foreach ($this->paginator as $ad) {\n            self::assertInstanceOf(DDC117ArticleDetails::class, $ad);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC168Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function ksort;\n\nclass DDC168Test extends OrmFunctionalTestCase\n{\n    /** @var ClassMetadata */\n    protected $oldMetadata;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->oldMetadata = $this->_em->getClassMetadata(CompanyEmployee::class);\n\n        $metadata = clone $this->oldMetadata;\n        ksort($metadata->propertyAccessors);\n        $this->_em->getMetadataFactory()->setMetadataFor(CompanyEmployee::class, $metadata);\n    }\n\n    public function tearDown(): void\n    {\n        $this->_em->getMetadataFactory()->setMetadataFor(CompanyEmployee::class, $this->oldMetadata);\n\n        parent::tearDown();\n    }\n\n    #[Group('DDC-168')]\n    public function testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray(): void\n    {\n        $spouse = new CompanyEmployee();\n        $spouse->setName('Blub');\n        $spouse->setDepartment('Accounting');\n        $spouse->setSalary(500);\n\n        $employee = new CompanyEmployee();\n        $employee->setName('Foo');\n        $employee->setDepartment('bar');\n        $employee->setSalary(1000);\n        $employee->setSpouse($spouse);\n\n        $this->_em->persist($spouse);\n        $this->_em->persist($employee);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery('SELECT e FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e WHERE e.name = ?1');\n        $q->setParameter(1, 'Foo');\n        $theEmployee = $q->getSingleResult();\n\n        self::assertEquals('bar', $theEmployee->getDepartment());\n        self::assertEquals('Foo', $theEmployee->getName());\n        self::assertEquals(1000, $theEmployee->getSalary());\n        self::assertInstanceOf(CompanyEmployee::class, $theEmployee);\n        self::assertInstanceOf(CompanyEmployee::class, $theEmployee->getSpouse());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1695Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTimeZone;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1695')]\nclass DDC1695Test extends OrmFunctionalTestCase\n{\n    public function testIssue(): void\n    {\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('Only with sqlite');\n        }\n\n        $dql = 'SELECT n.smallText, n.publishDate FROM ' . __NAMESPACE__ . '\\\\DDC1695News n';\n        $sql = $this->_em->createQuery($dql)->getSQL();\n\n        self::assertEquals(\n            'SELECT d0_.\"SmallText\" AS SmallText_0, d0_.\"PublishDate\" AS PublishDate_1 FROM \"DDC1695News\" d0_',\n            $sql,\n        );\n    }\n}\n\n#[Table(name: '`DDC1695News`')]\n#[Entity]\nclass DDC1695News\n{\n    #[Column(name: '`IdNews`', type: 'integer', nullable: false)]\n    #[Id]\n    #[GeneratedValue]\n    private int $idNews;\n\n    #[Column(name: '`IdUser`', type: 'bigint', nullable: false)]\n    private int $idUser;\n\n    #[Column(name: '`IdLanguage`', type: 'integer', nullable: false)]\n    private int $idLanguage;\n\n    #[Column(name: '`IdCondition`', type: 'integer', nullable: true)]\n    private int $idCondition;\n\n    #[Column(name: '`IdHealthProvider`', type: 'integer', nullable: true)]\n    private int $idHealthProvider;\n\n    #[Column(name: '`IdSpeciality`', type: 'integer', nullable: true)]\n    private int $idSpeciality;\n\n    #[Column(name: '`IdMedicineType`', type: 'integer', nullable: true)]\n    private int $idMedicineType;\n\n    #[Column(name: '`IdTreatment`', type: 'integer', nullable: true)]\n    private int $idTreatment;\n\n    #[Column(name: '`Title`', type: 'string', nullable: true)]\n    private string $title;\n\n    #[Column(name: '`SmallText`', type: 'string', nullable: true)]\n    private string $smallText;\n\n    #[Column(name: '`LongText`', type: 'string', nullable: true)]\n    private string $longText;\n\n    #[Column(name: '`PublishDate`', type: 'datetimetz', nullable: true)]\n    private DateTimeZone $publishDate;\n\n    #[Column(name: '`IdxNews`', type: 'json_array', nullable: true)]\n    private array $idxNews;\n\n    #[Column(name: '`Highlight`', type: 'boolean', nullable: false)]\n    private bool $highlight;\n\n    #[Column(name: '`Order`', type: 'integer', nullable: false)]\n    private int $order;\n\n    #[Column(name: '`Deleted`', type: 'boolean', nullable: false)]\n    private bool $deleted;\n\n    #[Column(name: '`Active`', type: 'boolean', nullable: false)]\n    private bool $active;\n\n    #[Column(name: '`UpdateToHighlighted`', type: 'boolean', nullable: true)]\n    private bool $updateToHighlighted;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1707Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\PostLoad;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1707')]\nclass DDC1707Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1707Base::class,\n            DDC1707Child::class,\n        );\n    }\n\n    public function testPostLoadOnChild(): void\n    {\n        $class  = $this->_em->getClassMetadata(DDC1707Child::class);\n        $entity = new DDC1707Child();\n\n        $class->invokeLifecycleCallbacks(Events::postLoad, $entity);\n\n        self::assertTrue($entity->postLoad);\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['c' => 'DDC1707Child'])]\n#[HasLifecycleCallbacks]\nabstract class DDC1707Base\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var bool */\n    public $postLoad = false;\n\n    #[PostLoad]\n    public function onPostLoad(): void\n    {\n        $this->postLoad = true;\n    }\n}\n#[Entity]\nclass DDC1707Child extends DDC1707Base\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1719Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1719')]\nclass DDC1719Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC1719SimpleEntity::class);\n    }\n\n    public function testCreateRetrieveUpdateDelete(): void\n    {\n        $e1 = new DDC1719SimpleEntity('Bar 1');\n        $e2 = new DDC1719SimpleEntity('Foo 1');\n\n        // Create\n        $this->_em->persist($e1);\n        $this->_em->persist($e2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $e1Id = $e1->id;\n        $e2Id = $e2->id;\n\n        // Retrieve\n        $e1 = $this->_em->find(DDC1719SimpleEntity::class, $e1Id);\n        $e2 = $this->_em->find(DDC1719SimpleEntity::class, $e2Id);\n\n        self::assertInstanceOf(DDC1719SimpleEntity::class, $e1);\n        self::assertInstanceOf(DDC1719SimpleEntity::class, $e2);\n\n        self::assertEquals($e1Id, $e1->id);\n        self::assertEquals($e2Id, $e2->id);\n\n        self::assertEquals('Bar 1', $e1->value);\n        self::assertEquals('Foo 1', $e2->value);\n\n        $e1->value = 'Bar 2';\n        $e2->value = 'Foo 2';\n\n        // Update\n        $this->_em->persist($e1);\n        $this->_em->persist($e2);\n        $this->_em->flush();\n\n        self::assertEquals('Bar 2', $e1->value);\n        self::assertEquals('Foo 2', $e2->value);\n\n        self::assertInstanceOf(DDC1719SimpleEntity::class, $e1);\n        self::assertInstanceOf(DDC1719SimpleEntity::class, $e2);\n\n        self::assertEquals($e1Id, $e1->id);\n        self::assertEquals($e2Id, $e2->id);\n\n        self::assertEquals('Bar 2', $e1->value);\n        self::assertEquals('Foo 2', $e2->value);\n\n        // Delete\n        $this->_em->remove($e1);\n        $this->_em->remove($e2);\n        $this->_em->flush();\n\n        $e1 = $this->_em->find(DDC1719SimpleEntity::class, $e1Id);\n        $e2 = $this->_em->find(DDC1719SimpleEntity::class, $e2Id);\n\n        self::assertNull($e1);\n        self::assertNull($e2);\n    }\n}\n\n#[Table(name: '`ddc-1719-simple-entity`')]\n#[Entity]\nclass DDC1719SimpleEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer', name: '`simple-entity-id`')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', name: '`simple-entity-value`')]\n        public string $value,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1757Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\QueryBuilder;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function assert;\n\nclass DDC1757Test extends OrmFunctionalTestCase\n{\n    public function testFailingCase(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n        assert($qb instanceof QueryBuilder);\n\n        $qb->select('_a')\n            ->from(DDC1757A::class, '_a')\n            ->from(DDC1757B::class, '_b')\n            ->join('_b.c', '_c')\n            ->join('_c.d', '_d');\n\n        $q   = $qb->getQuery();\n        $dql = $q->getDQL();\n\n        // Show difference between expected and actual queries on error\n        self::assertEquals(\n            'SELECT _a FROM ' . __NAMESPACE__ . '\\DDC1757A _a, ' . __NAMESPACE__ . '\\DDC1757B _b INNER JOIN _b.c _c INNER JOIN _c.d _d',\n            $dql,\n            'Wrong DQL query',\n        );\n    }\n}\n\n#[Entity]\nclass DDC1757A\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n}\n\n#[Entity]\nclass DDC1757B\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'DDC1757C')]\n    private DDC1757C $c;\n}\n\n#[Entity]\nclass DDC1757C\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    #[OneToOne(targetEntity: 'DDC1757D')]\n    private DDC1757D $d;\n}\n\n#[Entity]\nclass DDC1757D\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1778Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1778')]\nclass DDC1778Test extends OrmFunctionalTestCase\n{\n    private CmsUser|null $user;\n\n    private CmsPhonenumber|null $phone;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->user           = new CmsUser();\n        $this->user->username = 'beberlei';\n        $this->user->name     = 'Benjamin';\n        $this->user->status   = 'active';\n\n        $this->phone              = new CmsPhonenumber();\n        $this->phone->phonenumber = '0123456789';\n        $this->user->addPhonenumber($this->phone);\n\n        $this->_em->persist($this->user);\n        $this->_em->persist($this->phone);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user  = $this->_em->find(CmsUser::class, $this->user->getId());\n        $this->phone = $this->_em->find(CmsPhonenumber::class, $this->phone->phonenumber);\n    }\n\n    public function testClear(): void\n    {\n        $clonedNumbers = clone $this->user->getPhonenumbers();\n        $clonedNumbers->clear();\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user = $this->_em->find(CmsUser::class, $this->user->getId());\n\n        self::assertCount(1, $this->user->getPhonenumbers());\n    }\n\n    public function testRemove(): void\n    {\n        $clonedNumbers = clone $this->user->getPhonenumbers();\n        $clonedNumbers->remove(0);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user = $this->_em->find(CmsUser::class, $this->user->getId());\n\n        self::assertCount(1, $this->user->getPhonenumbers());\n    }\n\n    public function testRemoveElement(): void\n    {\n        $clonedNumbers = clone $this->user->getPhonenumbers();\n        $clonedNumbers->removeElement($this->phone);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user = $this->_em->find(CmsUser::class, $this->user->getId());\n\n        self::assertCount(1, $this->user->getPhonenumbers());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1787Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1787')]\nclass DDC1787Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC1787Foo::class,\n            DDC1787Bar::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $bar  = new DDC1787Bar();\n        $bar2 = new DDC1787Bar();\n\n        $this->_em->persist($bar);\n        $this->_em->persist($bar2);\n        $this->_em->flush();\n\n        self::assertSame(1, $bar->getVersion());\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['bar' => 'DDC1787Bar', 'foo' => 'DDC1787Foo'])]\nclass DDC1787Foo\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Version]\n    #[Column(type: 'integer')]\n    private int $version;\n\n    public function getVersion(): int\n    {\n        return $this->version;\n    }\n}\n\n#[Entity]\nclass DDC1787Bar extends DDC1787Foo\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1843Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Quote\\Group;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group as TestGroup;\n\n#[TestGroup('DDC-1845')]\n#[TestGroup('DDC-1843')]\nclass DDC1843Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('quote');\n\n        parent::setUp();\n    }\n\n    public function testCreateRetrieveUpdateDelete(): void\n    {\n        $e1 = new Group('Parent Bar 1');\n        $e2 = new Group('Parent Foo 2');\n\n        $this->_em->persist($e1);\n        $this->_em->persist($e2);\n        $this->_em->flush();\n\n        $e3 = new Group('Bar 3', $e1);\n        $e4 = new Group('Foo 4', $e2);\n\n        // Create\n        $this->_em->persist($e3);\n        $this->_em->persist($e4);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $e1Id = $e1->id;\n        $e2Id = $e2->id;\n        $e3Id = $e3->id;\n        $e4Id = $e4->id;\n\n        // Retrieve\n        $e1 = $this->_em->find(Group::class, $e1Id);\n        $e2 = $this->_em->find(Group::class, $e2Id);\n        $e3 = $this->_em->find(Group::class, $e3Id);\n        $e4 = $this->_em->find(Group::class, $e4Id);\n\n        self::assertInstanceOf(Group::class, $e1);\n        self::assertInstanceOf(Group::class, $e2);\n        self::assertInstanceOf(Group::class, $e3);\n        self::assertInstanceOf(Group::class, $e4);\n\n        self::assertEquals($e1Id, $e1->id);\n        self::assertEquals($e2Id, $e2->id);\n        self::assertEquals($e3Id, $e3->id);\n        self::assertEquals($e4Id, $e4->id);\n\n        self::assertEquals('Parent Bar 1', $e1->name);\n        self::assertEquals('Parent Foo 2', $e2->name);\n        self::assertEquals('Bar 3', $e3->name);\n        self::assertEquals('Foo 4', $e4->name);\n\n        $e1->name = 'Parent Bar 11';\n        $e2->name = 'Parent Foo 22';\n        $e3->name = 'Bar 33';\n        $e4->name = 'Foo 44';\n\n        // Update\n        $this->_em->persist($e1);\n        $this->_em->persist($e2);\n        $this->_em->persist($e3);\n        $this->_em->persist($e4);\n        $this->_em->flush();\n\n        self::assertEquals('Parent Bar 11', $e1->name);\n        self::assertEquals('Parent Foo 22', $e2->name);\n        self::assertEquals('Bar 33', $e3->name);\n        self::assertEquals('Foo 44', $e4->name);\n\n        self::assertInstanceOf(Group::class, $e1);\n        self::assertInstanceOf(Group::class, $e2);\n        self::assertInstanceOf(Group::class, $e3);\n        self::assertInstanceOf(Group::class, $e4);\n\n        self::assertEquals($e1Id, $e1->id);\n        self::assertEquals($e2Id, $e2->id);\n        self::assertEquals($e3Id, $e3->id);\n        self::assertEquals($e4Id, $e4->id);\n\n        self::assertEquals('Parent Bar 11', $e1->name);\n        self::assertEquals('Parent Foo 22', $e2->name);\n        self::assertEquals('Bar 33', $e3->name);\n        self::assertEquals('Foo 44', $e4->name);\n\n        // Delete\n        $this->_em->remove($e4);\n        $this->_em->remove($e3);\n        $this->_em->remove($e2);\n        $this->_em->remove($e1);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertInstanceOf(Group::class, $e1);\n        self::assertInstanceOf(Group::class, $e2);\n        self::assertInstanceOf(Group::class, $e3);\n        self::assertInstanceOf(Group::class, $e4);\n\n        // Retrieve\n        $e1 = $this->_em->find(Group::class, $e1Id);\n        $e2 = $this->_em->find(Group::class, $e2Id);\n        $e3 = $this->_em->find(Group::class, $e3Id);\n        $e4 = $this->_em->find(Group::class, $e4Id);\n\n        self::assertNull($e1);\n        self::assertNull($e2);\n        self::assertNull($e3);\n        self::assertNull($e4);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1884Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Taxi\\Car;\nuse Doctrine\\Tests\\Models\\Taxi\\Driver;\nuse Doctrine\\Tests\\Models\\Taxi\\PaidRide;\nuse Doctrine\\Tests\\Models\\Taxi\\Ride;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1884')]\nclass DDC1884Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('taxi');\n\n        parent::setUp();\n\n        [$bimmer, $crysler, $merc, $volvo] = $this->createCars(Car::class);\n        [$john, $foo]                      = $this->createDrivers(Driver::class);\n        $this->_em->flush();\n\n        $ride1 = new Ride($john, $bimmer);\n        $ride2 = new Ride($john, $merc);\n        $ride3 = new Ride($john, $volvo);\n        $ride4 = new Ride($foo, $merc);\n\n        $this->_em->persist($ride1);\n        $this->_em->persist($ride2);\n        $this->_em->persist($ride3);\n        $this->_em->persist($ride4);\n\n        $ride5 = new PaidRide($john, $bimmer);\n        $ride5->setFare(10.50);\n\n        $ride6 = new PaidRide($john, $merc);\n        $ride6->setFare(16.00);\n\n        $ride7 = new PaidRide($john, $volvo);\n        $ride7->setFare(20.70);\n\n        $ride8 = new PaidRide($foo, $merc);\n        $ride8->setFare(32.15);\n\n        $this->_em->persist($ride5);\n        $this->_em->persist($ride6);\n        $this->_em->persist($ride7);\n        $this->_em->persist($ride8);\n\n        $this->_em->flush();\n    }\n\n    /**\n     * @phpstan-return array{Car, Car, Car, Car}\n     *\n     * @var class-string<Car> $class\n     */\n    private function createCars(string $class): array\n    {\n        $bimmer = new $class();\n        $bimmer->setBrand('BMW');\n        $bimmer->setModel('7-Series');\n\n        $crysler = new $class();\n        $crysler->setBrand('Crysler');\n        $crysler->setModel('300');\n\n        $merc = new $class();\n        $merc->setBrand('Mercedes');\n        $merc->setModel('C-Class');\n\n        $volvo = new $class();\n        $volvo->setBrand('Volvo');\n        $volvo->setModel('XC90');\n\n        $this->_em->persist($bimmer);\n        $this->_em->persist($crysler);\n        $this->_em->persist($merc);\n        $this->_em->persist($volvo);\n\n        return [$bimmer, $crysler, $merc, $volvo];\n    }\n\n    /**\n     * @phpstan-return array{Driver, Driver}\n     *\n     * @var class-string<Driver> $class\n     */\n    private function createDrivers(string $class): array\n    {\n        $john = new $class();\n        $john->setName('John Doe');\n\n        $foo = new $class();\n        $foo->setName('Foo Bar');\n\n        $this->_em->persist($foo);\n        $this->_em->persist($john);\n\n        return [$john, $foo];\n    }\n\n    /**\n     * 1) Ride contains only columns that are part of its composite primary key\n     * 2) We use fetch joins here\n     */\n    public function testSelectFromInverseSideWithCompositePkAndSolelyIdentifierColumnsUsingFetchJoins(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n\n        $result = $qb->select('d, dr, c')\n            ->from(Driver::class, 'd')\n            ->leftJoin('d.freeDriverRides', 'dr')\n            ->leftJoin('dr.car', 'c')\n            ->where('d.name = ?1')\n            ->setParameter(1, 'John Doe')\n            ->getQuery()\n            ->getArrayResult();\n\n        self::assertCount(1, $result);\n        self::assertArrayHasKey('freeDriverRides', $result[0]);\n        self::assertCount(3, $result[0]['freeDriverRides']);\n    }\n\n    /**\n     * 1) PaidRide contains an extra column that is not part of the composite primary key\n     * 2) Again we will use fetch joins\n     */\n    public function testSelectFromInverseSideWithCompositePkUsingFetchJoins(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n\n        $result = $qb->select('d, dr, c')\n            ->from(Driver::class, 'd')\n            ->leftJoin('d.driverRides', 'dr')\n            ->leftJoin('dr.car', 'c')\n            ->where('d.name = ?1')\n            ->setParameter(1, 'John Doe')\n            ->getQuery()->getArrayResult();\n\n        self::assertCount(1, $result);\n        self::assertArrayHasKey('driverRides', $result[0]);\n        self::assertCount(3, $result[0]['driverRides']);\n    }\n\n    /**\n     * The other way around will fail too\n     */\n    public function testSelectFromOwningSideUsingFetchJoins(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n\n        $result =  $qb->select('r, d, c')\n            ->from(PaidRide::class, 'r')\n            ->leftJoin('r.driver', 'd')\n            ->leftJoin('r.car', 'c')\n            ->where('d.name = ?1')\n            ->setParameter(1, 'John Doe')\n            ->getQuery()->getArrayResult();\n\n        self::assertCount(3, $result);\n        self::assertArrayHasKey('driver', $result[0]);\n        self::assertArrayHasKey('car', $result[0]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1885Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Quote\\Group;\nuse Doctrine\\Tests\\Models\\Quote\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group as TestGroup;\n\n#[TestGroup('DDC-1845')]\n#[TestGroup('DDC-1885')]\nclass DDC1885Test extends OrmFunctionalTestCase\n{\n    private User $user;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('quote');\n\n        parent::setUp();\n\n        $user           = new User();\n        $user->name     = 'FabioBatSilva';\n        $user->groups[] = new Group('G 1');\n        $user->groups[] = new Group('G 2');\n        $this->user     = $user;\n\n        // Create\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testCreateRetrieveUpdateDelete(): void\n    {\n        $user = $this->user;\n        $g1   = $user->getGroups()->get(0);\n        $g2   = $user->getGroups()->get(1);\n\n        $u1Id = $user->id;\n        $g1Id = $g1->id;\n        $g2Id = $g2->id;\n\n        // Retrieve\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(2, $user->groups);\n\n        $g1 = $user->getGroups()->get(0);\n        $g2 = $user->getGroups()->get(1);\n\n        self::assertInstanceOf(Group::class, $g1);\n        self::assertInstanceOf(Group::class, $g2);\n\n        $g1->name = 'Bar 11';\n        $g2->name = 'Foo 22';\n\n        // Update\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        // Delete\n        $this->_em->remove($user);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(User::class, $u1Id));\n        self::assertNull($this->_em->find(Group::class, $g1Id));\n        self::assertNull($this->_em->find(Group::class, $g2Id));\n    }\n\n    public function testRemoveItem(): void\n    {\n        $user = $this->user;\n        $u1Id = $user->id;\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(2, $user->groups);\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(0));\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(1));\n\n        $user->getGroups()->remove(0);\n\n        // Update\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(1, $user->getGroups());\n    }\n\n    public function testClearAll(): void\n    {\n        $user = $this->user;\n        $u1Id = $user->id;\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(2, $user->groups);\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(0));\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(1));\n\n        $user->getGroups()->clear();\n\n        // Update\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(0, $user->getGroups());\n    }\n\n    public function testCountExtraLazy(): void\n    {\n        $user = $this->user;\n        $u1Id = $user->id;\n        $user = $this->_em->find(User::class, $u1Id);\n\n        self::assertInstanceOf(User::class, $user);\n        self::assertEquals('FabioBatSilva', $user->name);\n        self::assertEquals($u1Id, $user->id);\n\n        self::assertCount(2, $user->groups);\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(0));\n        self::assertInstanceOf(Group::class, $user->getGroups()->get(1));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1918Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function iterator_to_array;\n\n#[Group('DDC-1918')]\nclass DDC1918Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testLastPageCorrect(): void\n    {\n        $groups = [];\n        for ($i = 0; $i < 3; $i++) {\n            $group       = new CmsGroup();\n            $group->name = 'test';\n            $this->_em->persist($group);\n\n            $groups[] = $group;\n        }\n\n        for ($i = 0; $i < 10; $i++) {\n            $user           = new CmsUser();\n            $user->username = 'user' . $i;\n            $user->name     = 'user' . $i;\n            $user->status   = 'active';\n            $user->groups   = $groups;\n\n            $this->_em->persist($user);\n        }\n\n        $this->_em->flush();\n\n        $query = $this->_em->createQuery('SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g');\n        $query->setFirstResult(6);\n        $query->setMaxResults(3);\n\n        $paginator = new Paginator($query, true);\n        self::assertCount(3, iterator_to_array($paginator));\n\n        $query->setFirstResult(8);\n        $query->setMaxResults(3);\n\n        $paginator = new Paginator($query, true);\n        self::assertCount(2, iterator_to_array($paginator));\n\n        $query->setFirstResult(10);\n        $query->setMaxResults(3);\n\n        $paginator = new Paginator($query, true);\n        self::assertCount(0, iterator_to_array($paginator));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1925Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('DDC-1925')]\n#[Group('DDC-1210')]\nclass DDC1925Test extends OrmFunctionalTestCase\n{\n    public function testIssue(): void\n    {\n        $this->createSchemaForModels(DDC1925User::class, DDC1925Product::class);\n\n        $user = new DDC1925User();\n        $user->setTitle('Test User');\n\n        $product = new DDC1925Product();\n        $product->setTitle('Test product');\n\n        $this->_em->persist($user);\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $product->addBuyer($user);\n\n        $this->_em->getUnitOfWork()\n                  ->computeChangeSets();\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedProduct = $this->_em->find(DDC1925Product::class, $product->getId());\n        assert($persistedProduct instanceof DDC1925Product);\n\n        self::assertEquals($user, $persistedProduct->getBuyers()->first());\n    }\n}\n\n#[Table]\n#[Entity]\nclass DDC1925Product\n{\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(name: 'title', type: 'string', length: 255)]\n    private string|null $title = null;\n\n    /** @phpstan-var Collection<int, DDC1925User> */\n    #[JoinTable(name: 'user_purchases')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC1925User')]\n    private $buyers;\n\n    /**\n     * Default constructor\n     */\n    public function __construct()\n    {\n        $this->buyers = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setTitle(string $title): void\n    {\n        $this->title = $title;\n    }\n\n    /**\n     * Get title\n     */\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    public function getBuyers(): Collection\n    {\n        return $this->buyers;\n    }\n\n    public function addBuyer(DDC1925User $buyer): void\n    {\n        $this->buyers[] = $buyer;\n    }\n}\n\n#[Table]\n#[Entity]\nclass DDC1925User\n{\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(name: 'title', type: 'string', length: 255)]\n    private string|null $title = null;\n\n    /**\n     * Get id\n     */\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    /**\n     * Set title\n     */\n    public function setTitle(string $title): void\n    {\n        $this->title = $title;\n    }\n\n    /**\n     * Get title\n     */\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC192Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-192')]\nclass DDC192Test extends OrmFunctionalTestCase\n{\n    public function testSchemaCreation(): void\n    {\n        $classes = [\n            DDC192User::class,\n            DDC192Phonenumber::class,\n        ];\n\n        $this->createSchemaForModels(...$classes);\n\n        $tables = $this->createSchemaManager()->listTableNames();\n\n        foreach ($classes as $class) {\n            self::assertContains(\n                $this->_em->getClassMetadata($class)->getTableName(),\n                $tables,\n            );\n        }\n    }\n}\n\n#[Table(name: 'ddc192_users')]\n#[Entity]\nclass DDC192User\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255)]\n    public $name;\n}\n\n\n#[Table(name: 'ddc192_phonenumbers')]\n#[Entity]\nclass DDC192Phonenumber\n{\n    /** @var string */\n    #[Id]\n    #[Column(name: 'phone', type: 'string', length: 40)]\n    protected $phone;\n\n    /** @var DDC192User */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC192User')]\n    #[JoinColumn(name: 'userId', referencedColumnName: 'id')]\n    protected $user;\n\n    public function setPhone(string $value): void\n    {\n        $this->phone = $value;\n    }\n\n    public function getPhone(): string\n    {\n        return $this->phone;\n    }\n\n    public function setUser(DDC192User $user): void\n    {\n        $this->user = $user;\n    }\n\n    public function getUser(): DDC192User\n    {\n        return $this->user;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1995Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1995')]\nclass DDC1995Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function testIssue(): void\n    {\n        $person = new CompanyPerson();\n        $person->setName('p1');\n\n        $employee = new CompanyEmployee();\n        $employee->setName('Foo');\n        $employee->setDepartment('bar');\n        $employee->setSalary(1000);\n\n        $this->_em->persist($person);\n        $this->_em->persist($employee);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql   = 'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF ?1';\n        $class = $this->_em->getClassMetadata(CompanyEmployee::class);\n\n        $result = $this->_em->createQuery($dql)\n                ->setParameter(1, $class)\n                ->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CompanyEmployee::class, $result[0]);\n    }\n\n    public function testQueryCache(): void\n    {\n        $person = new CompanyPerson();\n        $person->setName('p1');\n\n        $employee = new CompanyEmployee();\n        $employee->setName('Foo');\n        $employee->setDepartment('bar');\n        $employee->setSalary(1000);\n\n        $this->_em->persist($person);\n        $this->_em->persist($employee);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql    = 'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF :type';\n        $class1 = $this->_em->getClassMetadata(CompanyEmployee::class);\n        $class2 = $this->_em->getClassMetadata(CompanyPerson::class);\n\n        $result1 = $this->_em->createQuery($dql)\n                ->setParameter('type', $class1)\n                ->useQueryCache(true)\n                ->getResult();\n\n        $result2 = $this->_em->createQuery($dql)\n                ->setParameter('type', $class2)\n                ->useQueryCache(true)\n                ->getResult();\n\n        self::assertCount(1, $result1);\n        self::assertCount(2, $result2);\n\n        self::assertInstanceOf(CompanyEmployee::class, $result1[0]);\n        self::assertContainsOnlyInstancesOf(CompanyPerson::class, $result2);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC1998Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\n#[Group('DDC-1998')]\nclass DDC1998Test extends OrmFunctionalTestCase\n{\n    public function testSqlConversionAsIdentifier(): void\n    {\n        Type::addType('ddc1998', DDC1998Type::class);\n\n        $this->createSchemaForModels(DDC1998Entity::class);\n\n        $entity     = new DDC1998Entity();\n        $entity->id = new DDC1998Id('foo');\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $entity->num++;\n\n        $this->_em->flush();\n\n        $this->_em->remove($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $found = $this->_em->find(DDC1998Entity::class, $entity->id);\n        self::assertNull($found);\n\n        $found = $this->_em->find(DDC1998Entity::class, 'foo');\n        self::assertNull($found);\n\n        self::assertCount(0, $this->_em->getRepository(DDC1998Entity::class)->findAll());\n    }\n}\n\n#[Entity]\nclass DDC1998Entity\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'ddc1998', length: 255)]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $num = 0;\n}\n\nclass DDC1998Type extends StringType\n{\n    public const NAME = 'ddc1998';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        return (string) $value;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): DDC1998Id\n    {\n        return new DDC1998Id($value);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n\nclass DDC1998Id implements Stringable\n{\n    public function __construct(private string $val)\n    {\n    }\n\n    public function __toString(): string\n    {\n        return $this->val;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC199Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC199Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC199ParentClass::class,\n            DDC199ChildClass::class,\n            DDC199RelatedClass::class,\n        );\n    }\n\n    public function testPolymorphicLoading(): void\n    {\n        $child             = new DDC199ChildClass();\n        $child->parentData = 'parentData';\n        $child->childData  = 'childData';\n        $this->_em->persist($child);\n\n        $related1              = new DDC199RelatedClass();\n        $related1->relatedData = 'related1';\n        $related1->parent      = $child;\n        $this->_em->persist($related1);\n\n        $related2              = new DDC199RelatedClass();\n        $related2->relatedData = 'related2';\n        $related2->parent      = $child;\n        $this->_em->persist($related2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('select e,r from Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC199ParentClass e join e.relatedEntities r');\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(DDC199ParentClass::class, $result[0]);\n        self::assertTrue($result[0]->relatedEntities->isInitialized());\n        self::assertEquals(2, $result[0]->relatedEntities->count());\n        self::assertInstanceOf(DDC199RelatedClass::class, $result[0]->relatedEntities[0]);\n        self::assertInstanceOf(DDC199RelatedClass::class, $result[0]->relatedEntities[1]);\n    }\n}\n\n\n#[Table(name: 'ddc199_entities')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['parent' => 'DDC199ParentClass', 'child' => 'DDC199ChildClass'])]\nclass DDC199ParentClass\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $parentData;\n\n    /** @phpstan-var Collection<int, DDC199RelatedClass> */\n    #[OneToMany(targetEntity: 'DDC199RelatedClass', mappedBy: 'parent')]\n    public $relatedEntities;\n}\n\n\n#[Entity]\nclass DDC199ChildClass extends DDC199ParentClass\n{\n    /** @var string */\n    #[Column]\n    public $childData;\n}\n\n#[Table(name: 'ddc199_relatedclass')]\n#[Entity]\nclass DDC199RelatedClass\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $relatedData;\n\n    /** @var DDC199ParentClass */\n    #[ManyToOne(targetEntity: 'DDC199ParentClass', inversedBy: 'relatedEntities')]\n    #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')]\n    public $parent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2012Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function explode;\nuse function implode;\nuse function is_array;\nuse function sprintf;\nuse function strtolower;\n\n#[Group('DDC-2012')]\n#[Group('non-cacheable')]\nclass DDC2012Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType(DDC2012TsVectorType::MYTYPE, DDC2012TsVectorType::class);\n\n        DDC2012TsVectorType::$calls = [];\n\n        $this->createSchemaForModels(\n            DDC2012Item::class,\n            DDC2012ItemPerson::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $item      = new DDC2012ItemPerson();\n        $item->tsv = ['word1', 'word2', 'word3'];\n\n        $this->_em->persist($item);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $item = $this->_em->find($item::class, $item->id);\n\n        self::assertArrayHasKey('convertToDatabaseValueSQL', DDC2012TsVectorType::$calls);\n        self::assertArrayHasKey('convertToDatabaseValue', DDC2012TsVectorType::$calls);\n        self::assertArrayHasKey('convertToPHPValue', DDC2012TsVectorType::$calls);\n\n        self::assertCount(1, DDC2012TsVectorType::$calls['convertToDatabaseValueSQL']);\n        self::assertCount(1, DDC2012TsVectorType::$calls['convertToDatabaseValue']);\n        self::assertCount(1, DDC2012TsVectorType::$calls['convertToPHPValue']);\n\n        self::assertInstanceOf(DDC2012Item::class, $item);\n        self::assertEquals(['word1', 'word2', 'word3'], $item->tsv);\n\n        $item->tsv = ['word1', 'word2'];\n\n        $this->_em->persist($item);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $item = $this->_em->find($item::class, $item->id);\n\n        self::assertCount(2, DDC2012TsVectorType::$calls['convertToDatabaseValueSQL']);\n        self::assertCount(2, DDC2012TsVectorType::$calls['convertToDatabaseValue']);\n        self::assertCount(2, DDC2012TsVectorType::$calls['convertToPHPValue']);\n\n        self::assertInstanceOf(DDC2012Item::class, $item);\n        self::assertEquals(['word1', 'word2'], $item->tsv);\n    }\n}\n\n#[Table(name: 'ddc2010_item')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type_id', type: 'smallint')]\n#[DiscriminatorMap([1 => 'DDC2012ItemPerson', 2 => 'DDC2012Item'])]\nclass DDC2012Item\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @phpstan-var list<string> */\n    #[Column(name: 'tsv', type: 'tsvector', length: 255, nullable: true)]\n    public $tsv;\n}\n\n#[Table(name: 'ddc2010_item_person')]\n#[Entity]\nclass DDC2012ItemPerson extends DDC2012Item\n{\n}\n\nclass DDC2012TsVectorType extends Type\n{\n    public const MYTYPE = 'tsvector';\n\n    /** @phpstan-var array<string, list<array{value: mixed, platform: AbstractPlatform}>> */\n    public static $calls = [];\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed\n    {\n        if (is_array($value)) {\n            $value = implode(' ', $value);\n        }\n\n        self::$calls[__FUNCTION__][] = [\n            'value'     => $value,\n            'platform'  => $platform,\n        ];\n\n        return $value;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @return list<string>\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): array\n    {\n        self::$calls[__FUNCTION__][] = [\n            'value'     => $value,\n            'platform'  => $platform,\n        ];\n\n        return explode(' ', strtolower($value));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string\n    {\n        self::$calls[__FUNCTION__][] = [\n            'sqlExpr'   => $sqlExpr,\n            'platform'  => $platform,\n        ];\n\n        // changed to upper expression to keep the test compatible with other Databases\n        //sprintf('to_tsvector(%s)', $sqlExpr);\n\n        return sprintf('UPPER(%s)', $sqlExpr);\n    }\n\n    public function getName(): string\n    {\n        return self::MYTYPE;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2074Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2074')]\nclass DDC2074Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n    }\n\n    public function testShouldNotScheduleDeletionOnClonedInstances(): void\n    {\n        $class      = $this->_em->getClassMetadata(ECommerceProduct::class);\n        $product    = new ECommerceProduct();\n        $category   = new ECommerceCategory();\n        $collection = new PersistentCollection($this->_em, $class, new ArrayCollection([$category]));\n        $collection->setOwner($product, $class->associationMappings['categories']);\n\n        $uow              = $this->_em->getUnitOfWork();\n        $clonedCollection = clone $collection;\n        $clonedCollection->clear();\n\n        self::assertCount(0, $uow->getScheduledCollectionDeletions());\n    }\n\n    public function testSavingClonedPersistentCollection(): void\n    {\n        $product  = new ECommerceProduct();\n        $category = new ECommerceCategory();\n        $category->setName('foo');\n        $product->addCategory($category);\n\n        $this->_em->persist($product);\n        $this->_em->persist($category);\n        $this->_em->flush();\n\n        $newProduct = clone $product;\n\n        $this->_em->persist($newProduct);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $product1 = $this->_em->find(ECommerceProduct::class, $product->getId());\n        $product2 = $this->_em->find(ECommerceProduct::class, $newProduct->getId());\n\n        self::assertCount(1, $product1->getCategories());\n        self::assertCount(1, $product2->getCategories());\n\n        self::assertSame($product1->getCategories()->get(0), $product2->getCategories()->get(0));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2084Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2084\\MyEntity1;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2084\\MyEntity2;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2084')]\nclass DDC2084Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            __NAMESPACE__ . '\\DDC2084\\MyEntity1',\n            __NAMESPACE__ . '\\DDC2084\\MyEntity2',\n        );\n    }\n\n    public function loadFixture(): MyEntity1\n    {\n        $e2 = new MyEntity2('Foo');\n        $e1 = new MyEntity1($e2);\n\n        $this->_em->persist($e2);\n        $this->_em->flush();\n\n        $this->_em->persist($e1);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        return $e1;\n    }\n\n    public function testIssue(): void\n    {\n        $e1 = $this->loadFixture();\n        $e2 = $e1->getMyEntity2();\n        $e  = $this->_em->find(__NAMESPACE__ . '\\DDC2084\\MyEntity1', $e2);\n\n        self::assertInstanceOf(__NAMESPACE__ . '\\DDC2084\\MyEntity1', $e);\n        self::assertInstanceOf(__NAMESPACE__ . '\\DDC2084\\MyEntity2', $e->getMyEntity2());\n        self::assertEquals('Foo', $e->getMyEntity2()->getValue());\n    }\n\n    public function testInvalidIdentifierBindingEntityException(): void\n    {\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage(\n            <<<'EXCEPTION'\nBinding entities to query parameters only allowed for entities that have an identifier.\nClass \"Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2084\\MyEntity2\" does not have an identifier.\nEXCEPTION,\n        );\n        $this->_em->find(__NAMESPACE__ . '\\DDC2084\\MyEntity1', new MyEntity2('Foo'));\n    }\n}\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2084;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'DDC2084_ENTITY1')]\n#[Entity]\nclass MyEntity1\n{\n    public function __construct(\n        #[Id]\n        #[OneToOne(targetEntity: 'MyEntity2')]\n        #[JoinColumn(name: 'entity2_id', referencedColumnName: 'id')]\n        private MyEntity2 $entity2,\n    ) {\n    }\n\n    public function setMyEntity2(MyEntity2 $myEntity2): void\n    {\n        $this->entity2 = $myEntity2;\n    }\n\n    public function getMyEntity2(): MyEntity2\n    {\n        return $this->entity2;\n    }\n}\n\n#[Table(name: 'DDC2084_ENTITY2')]\n#[Entity]\nclass MyEntity2\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    public function __construct(\n        #[Column]\n        private string $value,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getValue(): string\n    {\n        return $this->value;\n    }\n\n    public function setValue(string $value): void\n    {\n        $this->value = $value;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2090Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2090')]\n#[Group('non-cacheable')]\nclass DDC2090Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    public function testIssue(): void\n    {\n        $date1     = new DateTime('2011-11-11 11:11:11');\n        $date2     = new DateTime('2012-12-12 12:12:12');\n        $employee1 = new CompanyEmployee();\n        $employee2 = new CompanyEmployee();\n\n        $employee1->setName('Fabio B. Silva');\n        $employee1->setStartDate(new DateTime('yesterday'));\n        $employee1->setDepartment('R&D');\n        $employee1->setSalary(100);\n\n        $employee2->setName('Doctrine Bot');\n        $employee1->setStartDate(new DateTime('yesterday'));\n        $employee2->setDepartment('QA');\n        $employee2->setSalary(100);\n\n        $this->_em->persist($employee1);\n        $this->_em->persist($employee2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->_em->createQueryBuilder()\n            ->update(CompanyEmployee::class, 'e')\n            ->set('e.startDate', ':date')\n            ->set('e.salary', ':salary')\n            ->where('e = :e')\n            ->setParameters(new ArrayCollection([\n                new Parameter('e', $employee1),\n                new Parameter('date', $date1),\n                new Parameter('salary', 101),\n            ]))\n            ->getQuery()\n            ->useQueryCache(true)\n            ->execute();\n\n        $this->_em->createQueryBuilder()\n            ->update(CompanyEmployee::class, 'e')\n            ->set('e.startDate', ':date')\n            ->set('e.salary', ':salary')\n            ->where('e = :e')\n            ->setParameters(new ArrayCollection([\n                new Parameter('e', $employee2),\n                new Parameter('date', $date2),\n                new Parameter('salary', 102),\n            ]))\n            ->getQuery()\n            ->useQueryCache(true)\n            ->execute();\n\n        $this->_em->clear();\n\n        $e1 = $this->_em->find(CompanyEmployee::class, $employee1->getId());\n        $e2 = $this->_em->find(CompanyEmployee::class, $employee2->getId());\n\n        self::assertEquals(101, $e1->getSalary());\n        self::assertEquals(102, $e2->getSalary());\n        self::assertEquals($date1, $e1->getStartDate());\n        self::assertEquals($date2, $e2->getStartDate());\n\n        $this->_em->createQueryBuilder()\n            ->update(CompanyEmployee::class, 'e')\n            ->set('e.startDate', '?1')\n            ->set('e.salary', '?2')\n            ->where('e = ?0')\n            ->setParameters(new ArrayCollection([\n                new Parameter('0', $employee1),\n                new Parameter('1', $date1),\n                new Parameter('2', 101),\n            ]))\n            ->getQuery()\n            ->useQueryCache(true)\n            ->execute();\n\n        $this->_em->createQueryBuilder()\n            ->update(CompanyEmployee::class, 'e')\n            ->set('e.startDate', '?1')\n            ->set('e.salary', '?2')\n            ->where('e = ?0')\n            ->setParameters(new ArrayCollection([\n                new Parameter('0', $employee2),\n                new Parameter('1', $date2),\n                new Parameter('2', 102),\n            ]))\n            ->getQuery()\n            ->useQueryCache(true)\n            ->execute();\n\n        $this->_em->clear();\n\n        $e1 = $this->_em->find(CompanyEmployee::class, $employee1->getId());\n        $e2 = $this->_em->find(CompanyEmployee::class, $employee2->getId());\n\n        self::assertEquals(101, $e1->getSalary());\n        self::assertEquals(102, $e2->getSalary());\n        self::assertEquals($date1, $e1->getStartDate());\n        self::assertEquals($date2, $e2->getStartDate());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2106Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2106')]\nclass DDC2106Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2106Entity::class);\n    }\n\n    public function testDetachedEntityAsId(): void\n    {\n        // We want an uninitialized PersistentCollection $entity->children\n        $entity = new DDC2106Entity();\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n        $entity = $this->_em->getRepository(DDC2106Entity::class)->findOneBy([]);\n\n        // ... and a managed entity without id\n        $entityWithoutId = new DDC2106Entity();\n        $this->_em->persist($entityWithoutId);\n\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq('parent', $entityWithoutId));\n\n        self::assertCount(0, $entity->children->matching($criteria));\n    }\n}\n\n#[Entity]\nclass DDC2106Entity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC2106Entity */\n    #[ManyToOne(targetEntity: 'DDC2106Entity', inversedBy: 'children')]\n    public $parent;\n\n    /** @phpstan-var Collection<int, DDC2106Entity> */\n    #[OneToMany(targetEntity: 'DDC2106Entity', mappedBy: 'parent', cascade: ['persist'])]\n    public $children;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC211Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC211Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC211User::class, DDC211Group::class);\n    }\n\n    public function testIssue(): void\n    {\n        $user = new DDC211User();\n        $user->setName('John Doe');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $groupNames = ['group 1', 'group 2', 'group 3', 'group 4'];\n        foreach ($groupNames as $name) {\n            $group = new DDC211Group();\n            $group->setName($name);\n            $this->_em->persist($group);\n            $this->_em->flush();\n\n            if (! $user->getGroups()->contains($group)) {\n                $user->getGroups()->add($group);\n                $group->getUsers()->add($user);\n                $this->_em->flush();\n            }\n        }\n\n        self::assertEquals(4, $user->getGroups()->count());\n    }\n}\n\n\n#[Table(name: 'ddc211_users')]\n#[Entity]\nclass DDC211User\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255)]\n    protected $name;\n\n    /** @phpstan-var Collection<int, DDC211Group> */\n    #[JoinTable(name: 'user_groups')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC211Group', inversedBy: 'users')]\n    protected $groups;\n\n    public function __construct()\n    {\n        $this->groups = new ArrayCollection();\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    /** @phpstan-return Collection<int, DDC211Group> */\n    public function getGroups(): Collection\n    {\n        return $this->groups;\n    }\n}\n\n#[Table(name: 'ddc211_groups')]\n#[Entity]\nclass DDC211Group\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255)]\n    protected $name;\n\n    /** @phpstan-var Collection<int, DDC211User> */\n    #[ManyToMany(targetEntity: 'DDC211User', mappedBy: 'groups')]\n    protected $users;\n\n    public function __construct()\n    {\n        $this->users = new ArrayCollection();\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    /** @phpstan-return Collection<int, DDC211User> */\n    public function getUsers(): Collection\n    {\n        return $this->users;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2138Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Schema\\ForeignKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\ForeignKeyConstraintEditor;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\Table as DbalTable;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\n\nuse function array_map;\nuse function assert;\nuse function class_exists;\nuse function reset;\n\nclass DDC2138Test extends OrmFunctionalTestCase\n{\n    /**\n     * With this test, we will create the same foreign key twice which is fine, but we will tap a deprecation\n     * in DBAL 4.4. This has to be fixed during the validation of metadata. For now, we will simply ignore that\n     * deprecation.\n     */\n    #[Group('DDC-2138')]\n    #[IgnoreDeprecations]\n    public function testForeignKeyOnSTIWithMultipleMapping(): void\n    {\n        $schema = $this->getSchemaForModels(\n            DDC2138User::class,\n            DDC2138Structure::class,\n            DDC2138UserFollowedObject::class,\n            DDC2138UserFollowedStructure::class,\n            DDC2138UserFollowedUser::class,\n        );\n        self::assertTrue($schema->hasTable('users_followed_objects'), 'Table users_followed_objects should exist.');\n\n        $table = $schema->getTable('users_followed_objects');\n        assert($table instanceof DbalTable);\n        self::assertTrue(self::columnIsIndexed($table, 'object_id'));\n        self::assertTrue(self::columnIsIndexed($table, 'user_id'));\n        $foreignKeys = $table->getForeignKeys();\n        self::assertCount(1, $foreignKeys, 'user_id column has to have FK, but not object_id');\n\n        $fk = reset($foreignKeys);\n        assert($fk instanceof ForeignKeyConstraint);\n\n        if (class_exists(ForeignKeyConstraintEditor::class)) {\n            self::assertEquals('users', $fk->getReferencedTableName()->toString());\n\n            $localColumns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $fk->getReferencingColumnNames());\n        } else {\n            self::assertEquals('users', $fk->getForeignTableName());\n\n            $localColumns = $fk->getLocalColumns();\n        }\n\n        self::assertContains('user_id', $localColumns);\n        self::assertCount(1, $localColumns);\n    }\n\n    private static function columnIsIndexed(DbalTable $table, string $column): bool\n    {\n        foreach ($table->getIndexes() as $index) {\n            if ($index->spansColumns([$column])) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n\n\n\n#[Table(name: 'structures')]\n#[Entity]\nclass DDC2138Structure\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected int|null $id = null;\n\n    #[Column(length: 32, nullable: true)]\n    protected string|null $name = null;\n}\n\n#[Table(name: 'users_followed_objects')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'object_type', type: 'smallint')]\n#[DiscriminatorMap([4 => 'DDC2138UserFollowedUser', 3 => 'DDC2138UserFollowedStructure'])]\nabstract class DDC2138UserFollowedObject\n{\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int|null $id = null;\n}\n\n#[Entity]\nclass DDC2138UserFollowedStructure extends DDC2138UserFollowedObject\n{\n    /**\n     * Construct a UserFollowedStructure entity\n     */\n    public function __construct(\n        #[ManyToOne(inversedBy: 'followedStructures')]\n        #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)]\n        public DDC2138User $user,\n        #[ManyToOne]\n        #[JoinColumn(name: 'object_id', referencedColumnName: 'id', nullable: false)]\n        public DDC2138Structure $followedStructure,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC2138UserFollowedUser extends DDC2138UserFollowedObject\n{\n    /**\n     * Construct a UserFollowedUser entity\n     */\n    public function __construct(\n        #[ManyToOne(inversedBy: 'followedUsers')]\n        #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)]\n        public DDC2138User $user,\n        #[ManyToOne]\n        #[JoinColumn(name: 'object_id', referencedColumnName: 'id', nullable: false)]\n        public DDC2138User $followedUser,\n    ) {\n    }\n}\n\n#[Table(name: 'users')]\n#[Entity]\nclass DDC2138User\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int|null $id = null;\n\n    #[Column(length: 32, nullable: true)]\n    public string|null $name = null;\n\n    /** @var Collection<int, DDC2138UserFollowedUser> */\n    #[OneToMany(targetEntity: DDC2138UserFollowedUser::class, mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    public Collection $followedUsers;\n\n    /** @var Collection<int, DDC2138UserFollowedStructure> */\n    #[OneToMany(targetEntity: DDC2138UserFollowedStructure::class, mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    public Collection $followedStructures;\n\n    public function __construct()\n    {\n        $this->followedUsers      = new ArrayCollection();\n        $this->followedStructures = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2175Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2175')]\nclass DDC2175Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2175Entity::class);\n    }\n\n    public function testIssue(): void\n    {\n        $entity        = new DDC2175Entity();\n        $entity->field = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertEquals(1, $entity->version);\n\n        $entity->field = 'bar';\n        $this->_em->flush();\n\n        self::assertEquals(2, $entity->version);\n\n        $entity->field = 'baz';\n        $this->_em->flush();\n\n        self::assertEquals(3, $entity->version);\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap(['entity' => 'DDC2175Entity'])]\nclass DDC2175Entity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $field;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2182Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC2182Test extends OrmFunctionalTestCase\n{\n    public function testPassColumnOptionsToJoinColumns(): void\n    {\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof MySQLPlatform) {\n            self::markTestSkipped('This test is useful for all databases, but designed only for mysql.');\n        }\n\n        $sql = $this->_schemaTool->getCreateSchemaSql(\n            [\n                $this->_em->getClassMetadata(DDC2182OptionParent::class),\n                $this->_em->getClassMetadata(DDC2182OptionChild::class),\n            ],\n        );\n        self::assertThat($sql[0], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE DDC2182OptionParent (id INT UNSIGNED NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE DDC2182OptionParent (id INT UNSIGNED NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertThat($sql[1], self::logicalOr(\n            // DBAL < 4.3\n            self::equalTo('CREATE TABLE DDC2182OptionChild (id VARCHAR(255) NOT NULL, parent_id INT UNSIGNED DEFAULT NULL, INDEX IDX_B314D4AD727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n            // DBAL 4.3 (see https://github.com/doctrine/dbal/pull/6864)\n            self::equalTo('CREATE TABLE DDC2182OptionChild (id VARCHAR(255) NOT NULL, parent_id INT UNSIGNED DEFAULT NULL, INDEX IDX_B314D4AD727ACA70 (parent_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'),\n        ));\n        self::assertEquals('ALTER TABLE DDC2182OptionChild ADD CONSTRAINT FK_B314D4AD727ACA70 FOREIGN KEY (parent_id) REFERENCES DDC2182OptionParent (id)', $sql[2]);\n    }\n}\n\n#[Table]\n#[Entity]\nclass DDC2182OptionParent\n{\n    #[Id]\n    #[Column(type: 'integer', options: ['unsigned' => true])]\n    private int $id;\n}\n\n#[Table]\n#[Entity]\nclass DDC2182OptionChild\n{\n    #[Id]\n    #[Column]\n    private string $id;\n\n    #[ManyToOne(targetEntity: 'DDC2182OptionParent')]\n    #[JoinColumn(referencedColumnName: 'id')]\n    private DDC2182OptionParent $parent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2214Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n/**\n * Verifies that the type of parameters being bound to an SQL query is the same\n * of the identifier of the entities used as parameters in the DQL query, even\n * if the bound objects are proxies.\n */\n#[Group('DDC-2214')]\nclass DDC2214Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2214Foo::class, DDC2214Bar::class);\n    }\n\n    public function testIssue(): void\n    {\n        $foo = new DDC2214Foo();\n        $bar = new DDC2214Bar();\n\n        $foo->bar = $bar;\n\n        $this->_em->persist($foo);\n        $this->_em->persist($bar);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $foo = $this->_em->find(DDC2214Foo::class, $foo->id);\n        assert($foo instanceof DDC2214Foo);\n        $bar = $foo->bar;\n\n        $related = $this\n            ->_em\n            ->createQuery('SELECT b FROM ' . __NAMESPACE__ . '\\DDC2214Bar b WHERE b.id IN(:ids)')\n            ->setParameter('ids', [$bar])\n            ->getResult();\n\n        self::assertEquals([1 => ParameterType::INTEGER], $this->getLastLoggedQuery()['types']);\n    }\n}\n\n#[Entity]\nclass DDC2214Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC2214Bar */\n    #[ManyToOne(targetEntity: 'DDC2214Bar')]\n    public $bar;\n}\n\n#[Entity]\nclass DDC2214Bar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2224Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function sprintf;\n\n#[Group('DDC-2224')]\nclass DDC2224Test extends OrmFunctionalTestCase\n{\n    public static function setUpBeforeClass(): void\n    {\n        Type::addType('DDC2224Type', DDC2224Type::class);\n    }\n\n    public function testIssue(): Query\n    {\n        $dql   = 'SELECT e FROM ' . __NAMESPACE__ . '\\DDC2224Entity e WHERE e.field = :field';\n        $query = $this->_em->createQuery($dql);\n        $query->setQueryCache(new ArrayAdapter());\n\n        $query->setParameter('field', 'test', 'DDC2224Type');\n        self::assertStringEndsWith('.field = FUNCTION(?)', $query->getSQL());\n\n        return $query;\n    }\n\n    #[Depends('testIssue')]\n    public function testCacheMissWhenTypeChanges(Query $query): void\n    {\n        $query->setParameter('field', 'test', 'string');\n        self::assertStringEndsWith('.field = ?', $query->getSQL());\n    }\n}\n\nclass DDC2224Type extends Type\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return 'DDC2224Type';\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string\n    {\n        return sprintf('FUNCTION(%s)', $sqlExpr);\n    }\n}\n\n#[Entity]\nclass DDC2224Entity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var mixed */\n    #[Column(type: 'DDC2224Type', length: 255)]\n    public $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2252Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2252')]\nclass DDC2252Test extends OrmFunctionalTestCase\n{\n    /** @phpstan-var DDC2252User */\n    private $user;\n\n    /** @phpstan-var DDC2252MerchantAccount */\n    private $merchant;\n\n    /** @phpstan-var DDC2252Membership */\n    private $membership;\n\n    /** @phpstan-var list<DDC2252Privilege> */\n    private array $privileges = [];\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2252User::class,\n            DDC2252Privilege::class,\n            DDC2252Membership::class,\n            DDC2252MerchantAccount::class,\n        );\n\n        $this->loadFixtures();\n    }\n\n    public function loadFixtures(): void\n    {\n        $this->user       = new DDC2252User();\n        $this->merchant   = new DDC2252MerchantAccount();\n        $this->membership = new DDC2252Membership($this->user, $this->merchant);\n\n        $this->privileges[] = new DDC2252Privilege();\n        $this->privileges[] = new DDC2252Privilege();\n        $this->privileges[] = new DDC2252Privilege();\n\n        $this->membership->addPrivilege($this->privileges[0]);\n        $this->membership->addPrivilege($this->privileges[1]);\n        $this->membership->addPrivilege($this->privileges[2]);\n\n        $this->_em->persist($this->user);\n        $this->_em->persist($this->merchant);\n        $this->_em->persist($this->privileges[0]);\n        $this->_em->persist($this->privileges[1]);\n        $this->_em->persist($this->privileges[2]);\n        $this->_em->flush();\n\n        $this->_em->persist($this->membership);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testIssue(): void\n    {\n        $identifier = [\n            'merchantAccount' => $this->merchant->getAccountid(),\n            'userAccount'     => $this->user->getUid(),\n        ];\n\n        $membership = $this->_em->find(DDC2252Membership::class, $identifier);\n\n        self::assertInstanceOf(DDC2252Membership::class, $membership);\n        self::assertCount(3, $membership->getPrivileges());\n\n        $membership->getPrivileges()->remove(2);\n        $this->_em->persist($membership);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $membership = $this->_em->find(DDC2252Membership::class, $identifier);\n\n        self::assertInstanceOf(DDC2252Membership::class, $membership);\n        self::assertCount(2, $membership->getPrivileges());\n\n        $membership->getPrivileges()->clear();\n        $this->_em->persist($membership);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $membership = $this->_em->find(DDC2252Membership::class, $identifier);\n\n        self::assertInstanceOf(DDC2252Membership::class, $membership);\n        self::assertCount(0, $membership->getPrivileges());\n\n        $membership->addPrivilege($privilege3 = new DDC2252Privilege());\n        $this->_em->persist($privilege3);\n        $this->_em->persist($membership);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $membership = $this->_em->find(DDC2252Membership::class, $identifier);\n\n        self::assertInstanceOf(DDC2252Membership::class, $membership);\n        self::assertCount(1, $membership->getPrivileges());\n    }\n}\n\n#[Table(name: 'ddc2252_acl_privilege')]\n#[Entity]\nclass DDC2252Privilege\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $privilegeid;\n\n    public function getPrivilegeid(): int\n    {\n        return $this->privilegeid;\n    }\n}\n\n#[Table(name: 'ddc2252_mch_account')]\n#[Entity]\nclass DDC2252MerchantAccount\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    protected $accountid = 111;\n\n    public function getAccountid(): int\n    {\n        return $this->accountid;\n    }\n}\n\n#[Table(name: 'ddc2252_user_account')]\n#[Entity]\nclass DDC2252User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    protected $uid = 222;\n\n    /** @phpstan-var Collection<int, DDC2252Membership> */\n    #[OneToMany(targetEntity: 'DDC2252Membership', mappedBy: 'userAccount', cascade: ['persist'])]\n    #[JoinColumn(name: 'uid', referencedColumnName: 'uid')]\n    protected $memberships;\n\n    public function __construct()\n    {\n        $this->memberships = new ArrayCollection();\n    }\n\n    public function getUid(): int\n    {\n        return $this->uid;\n    }\n\n    /** @phpstan-return Collection<int, DDC2252Membership> */\n    public function getMemberships(): Collection\n    {\n        return $this->memberships;\n    }\n\n    public function addMembership(DDC2252Membership $membership): void\n    {\n        $this->memberships[] = $membership;\n    }\n}\n\n#[Table(name: 'ddc2252_mch_account_member')]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC2252Membership\n{\n    /** @phpstan-var Collection<int, DDC2252Privilege> */\n    #[JoinTable(name: 'ddc2252_user_mch_account_privilege')]\n    #[JoinColumn(name: 'mch_accountid', referencedColumnName: 'mch_accountid')]\n    #[JoinColumn(name: 'uid', referencedColumnName: 'uid')]\n    #[InverseJoinColumn(name: 'privilegeid', referencedColumnName: 'privilegeid')]\n    #[ManyToMany(targetEntity: 'DDC2252Privilege', indexBy: 'privilegeid')]\n    protected $privileges;\n\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC2252User', inversedBy: 'memberships')]\n        #[JoinColumn(name: 'uid', referencedColumnName: 'uid')]\n        protected DDC2252User $userAccount,\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC2252MerchantAccount')]\n        #[JoinColumn(name: 'mch_accountid', referencedColumnName: 'accountid')]\n        protected DDC2252MerchantAccount $merchantAccount,\n    ) {\n        $this->privileges = new ArrayCollection();\n    }\n\n    public function addPrivilege(DDC2252Privilege $privilege): void\n    {\n        $this->privileges[] = $privilege;\n    }\n\n    /** @phpstan-var Collection<int, DDC2252Privilege> */\n    public function getPrivileges(): Collection\n    {\n        return $this->privileges;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2306Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('DDC-2306')]\nclass DDC2306Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2306Zone::class,\n            DDC2306User::class,\n            DDC2306Address::class,\n            DDC2306UserAddress::class,\n        );\n    }\n\n    /**\n     * Verifies that when eager loading is triggered, proxies are kept managed.\n     *\n     * The problem resides in the refresh hint passed to {@see \\Doctrine\\ORM\\UnitOfWork::createEntity},\n     * which, as of DDC-1734, causes the proxy to be marked as un-managed.\n     * The check against the identity map only uses the identifier hash and the passed in class name, and\n     * does not take into account the fact that the set refresh hint may be for an entity of a different\n     * type from the one passed to {@see \\Doctrine\\ORM\\UnitOfWork::createEntity}\n     *\n     * As a result, a refresh requested for an entity `Foo` with identifier `123` may cause a proxy\n     * of type `Bar` with identifier `123` to be marked as un-managed.\n     */\n    public function testIssue(): void\n    {\n        $zone          = new DDC2306Zone();\n        $user          = new DDC2306User();\n        $address       = new DDC2306Address();\n        $userAddress   = new DDC2306UserAddress($user, $address);\n        $user->zone    = $zone;\n        $address->zone = $zone;\n\n        $this->_em->persist($zone);\n        $this->_em->persist($user);\n        $this->_em->persist($address);\n        $this->_em->persist($userAddress);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $address = $this->_em->find(DDC2306Address::class, $address->id);\n        assert($address instanceof DDC2306Address);\n        $user = $address->users->first()->user;\n\n        $this->assertTrue($this->isUninitializedObject($user));\n        self::assertInstanceOf(DDC2306User::class, $user);\n\n        $userId = $user->id;\n\n        self::assertNotNull($userId);\n\n        $this->_em->getUnitOfWork()->initializeObject($user);\n\n        self::assertEquals(\n            $userId,\n            $user->id,\n            'As of DDC-1734, the identifier is NULL for un-managed proxies. The identifier should be an integer here',\n        );\n    }\n}\n\n#[Entity]\nclass DDC2306Zone\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC2306User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2306UserAddress[]|Collection */\n    #[OneToMany(targetEntity: 'DDC2306UserAddress', mappedBy: 'user')]\n    public $addresses;\n\n    /** @var DDC2306Zone */\n    #[ManyToOne(targetEntity: 'DDC2306Zone', fetch: 'EAGER')]\n    public $zone;\n\n    /** Constructor */\n    public function __construct()\n    {\n        $this->addresses = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC2306Address\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2306UserAddress[]|Collection */\n    #[OneToMany(targetEntity: 'DDC2306UserAddress', mappedBy: 'address', orphanRemoval: true)]\n    public $users;\n\n    /** @var DDC2306Zone */\n    #[ManyToOne(targetEntity: 'DDC2306Zone', fetch: 'EAGER')]\n    public $zone;\n\n    /** Constructor */\n    public function __construct()\n    {\n        $this->users = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC2306UserAddress\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** Constructor */\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC2306User')]\n        public DDC2306User $user,\n        #[ManyToOne(targetEntity: 'DDC2306Address', fetch: 'LAZY')]\n        public DDC2306Address $address,\n    ) {\n        $user->addresses->add($this);\n        $address->users->add($this);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2346Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2346')]\nclass DDC2346Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2346Foo::class,\n            DDC2346Bar::class,\n            DDC2346Baz::class,\n        );\n    }\n\n    /**\n     * Verifies that fetching a OneToMany association with fetch=\"EAGER\" does not cause N+1 queries\n     */\n    public function testIssue(): void\n    {\n        $foo1 = new DDC2346Foo();\n        $foo2 = new DDC2346Foo();\n\n        $baz1 = new DDC2346Baz();\n        $baz2 = new DDC2346Baz();\n\n        $baz1->foo = $foo1;\n        $baz2->foo = $foo2;\n\n        $foo1->bars[] = $baz1;\n        $foo1->bars[] = $baz2;\n\n        $this->_em->persist($foo1);\n        $this->_em->persist($foo2);\n        $this->_em->persist($baz1);\n        $this->_em->persist($baz2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $fetchedBazs = $this->_em->getRepository(DDC2346Baz::class)->findAll();\n\n        self::assertCount(2, $fetchedBazs);\n        $this->assertQueryCount(2, 'The total number of executed queries is 2, and not n+1');\n    }\n}\n\n#[Entity]\nclass DDC2346Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2346Bar[]|Collection */\n    #[OneToMany(targetEntity: 'DDC2346Bar', mappedBy: 'foo')]\n    public $bars;\n\n    /** Constructor */\n    public function __construct()\n    {\n        $this->bars = new ArrayCollection();\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['bar' => 'DDC2346Bar', 'baz' => 'DDC2346Baz'])]\nclass DDC2346Bar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2346Foo */\n    #[ManyToOne(targetEntity: 'DDC2346Foo', inversedBy: 'bars', fetch: 'EAGER')]\n    public $foo;\n}\n\n\n#[Entity]\nclass DDC2346Baz extends DDC2346Bar\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2350Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2350')]\n#[Group('non-cacheable')]\nclass DDC2350Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2350User::class, DDC2350Bug::class);\n    }\n\n    public function testEagerCollectionsAreOnlyRetrievedOnce(): void\n    {\n        $user       = new DDC2350User();\n        $bug1       = new DDC2350Bug();\n        $bug1->user = $user;\n        $bug2       = new DDC2350Bug();\n        $bug2->user = $user;\n\n        $this->_em->persist($user);\n        $this->_em->persist($bug1);\n        $this->_em->persist($bug2);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n        $user = $this->_em->find(DDC2350User::class, $user->id);\n\n        $this->assertQueryCount(2);\n\n        self::assertCount(2, $user->reportedBugs);\n\n        $this->assertQueryCount(2);\n    }\n}\n\n#[Entity]\nclass DDC2350User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC2350Bug> */\n    #[OneToMany(targetEntity: 'DDC2350Bug', mappedBy: 'user', fetch: 'EAGER')]\n    public $reportedBugs;\n}\n\n#[Entity]\nclass DDC2350Bug\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n    /** @var DDC2350User */\n    #[ManyToOne(targetEntity: 'DDC2350User', inversedBy: 'reportedBugs')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2359Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\n#[Group('DDC-2359')]\nclass DDC2359Test extends TestCase\n{\n    /**\n     * Verifies that {@see \\Doctrine\\ORM\\Mapping\\ClassMetadataFactory::wakeupReflection} is\n     * not called twice when loading metadata from a driver\n     */\n    public function testIssue(): void\n    {\n        $mockDriver    = $this->createMock(MappingDriver::class);\n        $mockMetadata  = $this->createMock(ClassMetadata::class);\n        $entityManager = $this->createMock(EntityManager::class);\n\n        $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)\n            ->onlyMethods(['newClassMetadataInstance', 'wakeupReflection'])\n            ->getMock();\n\n        $configuration = $this->getMockBuilder(Configuration::class)\n            ->onlyMethods(['getMetadataDriverImpl'])\n            ->getMock();\n\n        $connection = $this->createMock(Connection::class);\n\n        $configuration\n            ->method('getMetadataDriverImpl')\n            ->willReturn($mockDriver);\n\n        $entityManager->expects(self::any())->method('getConfiguration')->willReturn($configuration);\n        $entityManager->expects(self::any())->method('getConnection')->willReturn($connection);\n        $entityManager\n            ->method('getEventManager')\n            ->willReturn(new EventManager());\n\n        $metadataFactory->method('newClassMetadataInstance')->willReturn($mockMetadata);\n        $metadataFactory->expects(self::once())->method('wakeupReflection');\n\n        $metadataFactory->setEntityManager($entityManager);\n\n        $mockMetadata->method('getName')->willReturn(DDC2359Foo::class);\n\n        self::assertSame($mockMetadata, $metadataFactory->getMetadataFor(DDC2359Foo::class));\n    }\n}\n\n#[Entity]\nclass DDC2359Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC237Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC237Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC237EntityX::class,\n            DDC237EntityY::class,\n            DDC237EntityZ::class,\n        );\n    }\n\n    public function testUninitializedProxyIsInitializedOnFetchJoin(): void\n    {\n        $x = new DDC237EntityX();\n        $y = new DDC237EntityY();\n        $z = new DDC237EntityZ();\n\n        $x->data = 'X';\n        $y->data = 'Y';\n        $z->data = 'Z';\n\n        $x->y = $y;\n        $z->y = $y;\n\n        $this->_em->persist($x);\n        $this->_em->persist($y);\n        $this->_em->persist($z);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $x2 = $this->_em->find($x::class, $x->id); // proxy injected for Y\n        self::assertTrue($this->isUninitializedObject($x2->y));\n\n        // proxy for Y is in identity map\n\n        $z2 = $this->_em->createQuery('select z,y from ' . $z::class . ' z join z.y y where z.id = ?1')\n                ->setParameter(1, $z->id)\n                ->getSingleResult();\n        self::assertFalse($this->isUninitializedObject($z2->y));\n        self::assertEquals('Y', $z2->y->data);\n        self::assertEquals($y->id, $z2->y->id);\n\n        // since the Y is the same, the instance from the identity map is\n        // used, even if it is a proxy.\n\n        self::assertNotSame($x, $x2);\n        self::assertNotSame($z, $z2);\n        self::assertSame($z2->y, $x2->y);\n    }\n}\n\n\n#[Table(name: 'ddc237_x')]\n#[Entity]\nclass DDC237EntityX\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n    /** @var DDC237EntityY */\n    #[OneToOne(targetEntity: 'DDC237EntityY')]\n    #[JoinColumn(name: 'y_id', referencedColumnName: 'id')]\n    public $y;\n}\n\n\n#[Table(name: 'ddc237_y')]\n#[Entity]\nclass DDC237EntityY\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n}\n\n#[Table(name: 'ddc237_z')]\n#[Entity]\nclass DDC237EntityZ\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n\n    /** @var DDC237EntityY */\n    #[OneToOne(targetEntity: 'DDC237EntityY')]\n    #[JoinColumn(name: 'y_id', referencedColumnName: 'id')]\n    public $y;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2387Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Schema\\Name\\Identifier;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\ORM\\Functional\\DatabaseDriverTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\n\nclass DDC2387Test extends DatabaseDriverTestCase\n{\n    #[Group('DDC-2387')]\n    public function testCompositeAssociationKeyDetection(): void\n    {\n        $product = new Table('ddc2387_product');\n        $product->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $product->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $product->setPrimaryKey(['id']);\n        }\n\n        $attributes = new Table('ddc2387_attributes');\n        $attributes->addColumn('product_id', 'integer');\n        $attributes->addColumn('attribute_name', 'string');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $attributes->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('product_id')), new UnqualifiedName(Identifier::unquoted('attribute_name'))], true));\n        } else {\n            $attributes->setPrimaryKey(['product_id', 'attribute_name']);\n        }\n\n        $attributes->addForeignKeyConstraint('ddc2387_product', ['product_id'], ['product_id']);\n\n        $metadata = $this->convertToClassMetadata([$product, $attributes], []);\n\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_NONE, $metadata['Ddc2387Attributes']->generatorType);\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $metadata['Ddc2387Product']->generatorType);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2415Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Id\\AbstractIdGenerator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function md5;\n\n#[Group('DDC-2415')]\nclass DDC2415Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->_em->getConfiguration()->setMetadataDriverImpl(new StaticPHPDriver([]));\n\n        $this->createSchemaForModels(\n            DDC2415ParentEntity::class,\n            DDC2415ChildEntity::class,\n        );\n    }\n\n    public function testTicket(): void\n    {\n        $parentMetadata = $this->_em->getClassMetadata(DDC2415ParentEntity::class);\n        $childMetadata  = $this->_em->getClassMetadata(DDC2415ChildEntity::class);\n\n        self::assertEquals($parentMetadata->generatorType, $childMetadata->generatorType);\n        self::assertEquals($parentMetadata->customGeneratorDefinition, $childMetadata->customGeneratorDefinition);\n        self::assertEquals(DDC2415Generator::class, $parentMetadata->customGeneratorDefinition['class']);\n\n        $e1 = new DDC2415ChildEntity('ChildEntity 1');\n        $e2 = new DDC2415ChildEntity('ChildEntity 2');\n\n        $this->_em->persist($e1);\n        $this->_em->persist($e2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals(md5($e1->getName()), $e1->getId());\n        self::assertEquals(md5($e2->getName()), $e2->getId());\n    }\n}\n\nclass DDC2415ParentEntity\n{\n    /** @var string */\n    protected $id;\n\n    public function getId(): string\n    {\n        return $this->id;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'        => true,\n                'fieldName' => 'id',\n                'type'      => 'string',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);\n        $metadata->setCustomGeneratorDefinition(['class' => DDC2415Generator::class]);\n\n        $metadata->isMappedSuperclass = true;\n    }\n}\n\nclass DDC2415ChildEntity extends DDC2415ParentEntity\n{\n    public function __construct(protected string $name)\n    {\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type'      => 'string',\n            ],\n        );\n    }\n}\n\nclass DDC2415Generator extends AbstractIdGenerator\n{\n    public function generateId(EntityManagerInterface $em, object|null $entity): string\n    {\n        return md5($entity->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2494Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2494')]\n#[Group('non-cacheable')]\nclass DDC2494Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        DDC2494TinyIntType::$calls = [];\n\n        Type::addType('ddc2494_tinyint', DDC2494TinyIntType::class);\n\n        $this->createSchemaForModels(\n            DDC2494Currency::class,\n            DDC2494Campaign::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $currency = new DDC2494Currency(1, 2);\n\n        $this->_em->persist($currency);\n        $this->_em->flush();\n\n        $campaign = new DDC2494Campaign($currency);\n\n        $this->_em->persist($campaign);\n        $this->_em->flush();\n        $this->_em->close();\n\n        self::assertArrayHasKey('convertToDatabaseValue', DDC2494TinyIntType::$calls);\n        self::assertCount(3, DDC2494TinyIntType::$calls['convertToDatabaseValue']);\n\n        $item = $this->_em->find(DDC2494Campaign::class, $campaign->getId());\n\n        self::assertInstanceOf(DDC2494Campaign::class, $item);\n        self::assertInstanceOf(DDC2494Currency::class, $item->getCurrency());\n\n        $this->getQueryLog()->reset()->enable();\n\n        self::assertTrue($this->isUninitializedObject($item->getCurrency()));\n\n        self::assertArrayHasKey('convertToPHPValue', DDC2494TinyIntType::$calls);\n        self::assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);\n\n        self::assertIsInt($item->getCurrency()->getId());\n        self::assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);\n        self::assertTrue($this->isUninitializedObject($item->getCurrency()));\n\n        $this->assertQueryCount(0);\n\n        self::assertIsInt($item->getCurrency()->getTemp());\n        self::assertCount(3, DDC2494TinyIntType::$calls['convertToPHPValue']);\n        self::assertFalse($this->isUninitializedObject($item->getCurrency()));\n\n        $this->assertQueryCount(1);\n    }\n}\n\n#[Table(name: 'ddc2494_currency')]\n#[Entity]\nclass DDC2494Currency\n{\n    /** @phpstan-var Collection<int, DDC2494Campaign> */\n    #[OneToMany(targetEntity: 'DDC2494Campaign', mappedBy: 'currency')]\n    protected $campaigns;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'ddc2494_tinyint')]\n        protected int $id,\n        #[Column(name: 'temp', type: 'ddc2494_tinyint', nullable: false)]\n        protected int $temp,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getTemp(): int\n    {\n        return $this->temp;\n    }\n\n    /** @phpstan-return Collection<int, DDC2494Campaign> */\n    public function getCampaigns(): Collection\n    {\n        return $this->campaigns;\n    }\n}\n\n#[Table(name: 'ddc2494_campaign')]\n#[Entity]\nclass DDC2494Campaign\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC2494Currency', inversedBy: 'campaigns')]\n        #[JoinColumn(name: 'currency_id', referencedColumnName: 'id', nullable: false)]\n        protected DDC2494Currency $currency,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getCurrency(): DDC2494Currency\n    {\n        return $this->currency;\n    }\n}\n\nclass DDC2494TinyIntType extends Type\n{\n    /** @phpstan-var array<string, list<array{value:mixed, return: string, platform: AbstractPlatform}>> */\n    public static $calls = [];\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getSmallIntTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        $return = (string) $value;\n\n        self::$calls[__FUNCTION__][] = [\n            'value'     => $value,\n            'return'    => $return,\n            'platform'  => $platform,\n        ];\n\n        return $return;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): int\n    {\n        $return = (int) $value;\n\n        self::$calls[__FUNCTION__][] = [\n            'value'     => $value,\n            'return'    => $return,\n            'platform'  => $platform,\n        ];\n\n        return $return;\n    }\n\n    public function getName(): string\n    {\n        return 'ddc2494_tinyint';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2519Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUser;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2519')]\nclass DDC2519Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('legacy');\n\n        parent::setUp();\n\n        $this->loadFixture();\n    }\n\n    #[Group('DDC-2519')]\n    public function testIssue(): void\n    {\n        $dql    = 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference l';\n        $result = $this->_em->createQuery($dql)->getResult();\n\n        self::assertCount(2, $result);\n        self::assertInstanceOf(LegacyUserReference::class, $result[0]);\n        self::assertInstanceOf(LegacyUserReference::class, $result[1]);\n\n        self::assertInstanceOf(LegacyUser::class, $result[0]->source());\n        self::assertInstanceOf(LegacyUser::class, $result[0]->target());\n        self::assertInstanceOf(LegacyUser::class, $result[1]->source());\n        self::assertInstanceOf(LegacyUser::class, $result[1]->target());\n\n        self::assertTrue($this->isUninitializedObject($result[0]->target()));\n        self::assertTrue($this->isUninitializedObject($result[0]->source()));\n        self::assertTrue($this->isUninitializedObject($result[1]->target()));\n        self::assertTrue($this->isUninitializedObject($result[1]->source()));\n\n        self::assertNotNull($result[0]->source()->getId());\n        self::assertNotNull($result[0]->target()->getId());\n        self::assertNotNull($result[1]->source()->getId());\n        self::assertNotNull($result[1]->target()->getId());\n    }\n\n    public function loadFixture(): void\n    {\n        $user1           = new LegacyUser();\n        $user1->username = 'FabioBatSilva';\n        $user1->name     = 'Fabio B. Silva';\n\n        $user2           = new LegacyUser();\n        $user2->username = 'doctrinebot';\n        $user2->name     = 'Doctrine Bot';\n\n        $user3           = new LegacyUser();\n        $user3->username = 'test';\n        $user3->name     = 'Tester';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n        $this->_em->persist($user3);\n\n        $this->_em->flush();\n\n        $this->_em->persist(new LegacyUserReference($user1, $user2, 'foo'));\n        $this->_em->persist(new LegacyUserReference($user1, $user3, 'bar'));\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2575Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2575')]\nclass DDC2575Test extends OrmFunctionalTestCase\n{\n    /** @phpstan-var list<DDC2575Root> */\n    private array $rootsEntities = [];\n\n    /** @phpstan-var list<DDC2575A> */\n    private array $aEntities = [];\n\n    /** @phpstan-var list<DDC2575B> */\n    private array $bEntities = [];\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2575Root::class,\n            DDC2575A::class,\n            DDC2575B::class,\n        );\n\n        $entityRoot1 = new DDC2575Root(1);\n        $entityB1    = new DDC2575B(2);\n        $entityA1    = new DDC2575A($entityRoot1, $entityB1);\n\n        $this->_em->persist($entityRoot1);\n        $this->_em->persist($entityA1);\n        $this->_em->persist($entityB1);\n\n        $entityRoot2 = new DDC2575Root(3);\n        $entityB2    = new DDC2575B(4);\n        $entityA2    = new DDC2575A($entityRoot2, $entityB2);\n\n        $this->_em->persist($entityRoot2);\n        $this->_em->persist($entityA2);\n        $this->_em->persist($entityB2);\n\n        $this->_em->flush();\n\n        $this->rootsEntities[] = $entityRoot1;\n        $this->rootsEntities[] = $entityRoot2;\n\n        $this->aEntities[] = $entityA1;\n        $this->aEntities[] = $entityA2;\n\n        $this->bEntities[] = $entityB1;\n        $this->bEntities[] = $entityB2;\n\n        $this->_em->clear();\n    }\n\n    public function testHydrationIssue(): void\n    {\n        $repository = $this->_em->getRepository(DDC2575Root::class);\n        $qb         = $repository->createQueryBuilder('r')\n            ->select('r, a, b')\n            ->leftJoin('r.aRelation', 'a')\n            ->leftJoin('a.bRelation', 'b');\n\n        $query  = $qb->getQuery();\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n\n        $row = $result[0];\n        self::assertNotNull($row->aRelation);\n        self::assertEquals(1, $row->id);\n        self::assertNotNull($row->aRelation->rootRelation);\n        self::assertSame($row, $row->aRelation->rootRelation);\n        self::assertNotNull($row->aRelation->bRelation);\n        self::assertEquals(2, $row->aRelation->bRelation->id);\n\n        $row = $result[1];\n        self::assertNotNull($row->aRelation);\n        self::assertEquals(3, $row->id);\n        self::assertNotNull($row->aRelation->rootRelation);\n        self::assertSame($row, $row->aRelation->rootRelation);\n        self::assertNotNull($row->aRelation->bRelation);\n        self::assertEquals(4, $row->aRelation->bRelation->id);\n    }\n}\n\n#[Entity]\nclass DDC2575Root\n{\n    /** @var DDC2575A */\n    #[OneToOne(targetEntity: 'DDC2575A', mappedBy: 'rootRelation')]\n    public $aRelation;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n        #[Column(type: 'integer')]\n        public int $sampleField = 0,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC2575A\n{\n    public function __construct(\n        #[Id]\n        #[OneToOne(targetEntity: 'DDC2575Root', inversedBy: 'aRelation')]\n        #[JoinColumn(name: 'root_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n        public DDC2575Root $rootRelation,\n        #[ManyToOne(targetEntity: 'DDC2575B')]\n        #[JoinColumn(name: 'b_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n        public DDC2575B $bRelation,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC2575B\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n        #[Column(type: 'integer')]\n        public int $sampleField = 0,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2579Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\n#[Group('DDC-2579')]\nclass DDC2579Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType(DDC2579Type::NAME, DDC2579Type::class);\n\n        $this->createSchemaForModels(\n            DDC2579Entity::class,\n            DDC2579EntityAssoc::class,\n            DDC2579AssocAssoc::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $id         = new DDC2579Id('foo');\n        $assoc      = new DDC2579AssocAssoc($id);\n        $assocAssoc = new DDC2579EntityAssoc($assoc);\n        $entity     = new DDC2579Entity($assocAssoc);\n        $repository = $this->_em->getRepository(DDC2579Entity::class);\n\n        $this->_em->persist($assoc);\n        $this->_em->persist($assocAssoc);\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $entity->value++;\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $id       = $entity->id;\n        $value    = $entity->value;\n        $criteria = ['assoc' => $assoc, 'id' => $id];\n        $entity   = $repository->findOneBy($criteria);\n\n        self::assertInstanceOf(DDC2579Entity::class, $entity);\n        self::assertEquals($value, $entity->value);\n\n        $this->_em->remove($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($repository->findOneBy($criteria));\n        self::assertCount(0, $repository->findAll());\n    }\n}\n\n#[Entity]\nclass DDC2579Entity\n{\n    /** @var DDC2579Id */\n    #[Id]\n    #[Column(type: 'ddc2579', length: 255)]\n    public $id;\n\n    /** @var DDC2579EntityAssoc */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC2579EntityAssoc')]\n    #[JoinColumn(name: 'relation_id', referencedColumnName: 'association_id')]\n    public $assoc;\n\n    public function __construct(\n        DDC2579EntityAssoc $assoc,\n        #[Column(type: 'integer')]\n        public int $value = 0,\n    ) {\n        $this->id    = $assoc->assocAssoc->associationId;\n        $this->assoc = $assoc;\n    }\n}\n\n#[Entity]\nclass DDC2579EntityAssoc\n{\n    public function __construct(\n        /** @var DDC2579AssocAssoc */\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC2579AssocAssoc')]\n        #[JoinColumn(name: 'association_id', referencedColumnName: 'associationId')]\n        public $assocAssoc,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC2579AssocAssoc\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'ddc2579', length: 255)]\n        public DDC2579Id $associationId,\n    ) {\n    }\n}\n\n\nclass DDC2579Type extends StringType\n{\n    public const NAME = 'ddc2579';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        return (string) $value;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): DDC2579Id\n    {\n        return new DDC2579Id($value);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n\nclass DDC2579Id implements Stringable\n{\n    public function __construct(private string $val)\n    {\n    }\n\n    public function __toString(): string\n    {\n        return $this->val;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC258Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC258Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC258Super::class,\n            DDC258Class1::class,\n            DDC258Class2::class,\n            DDC258Class3::class,\n        );\n    }\n\n    #[Group('DDC-258')]\n    public function testIssue(): void\n    {\n        $c1              = new DDC258Class1();\n        $c1->title       = 'Foo';\n        $c1->description = 'Foo';\n\n        $c2              = new DDC258Class2();\n        $c2->title       = 'Bar';\n        $c2->description = 'Bar';\n        $c2->text        = 'Bar';\n\n        $c3          = new DDC258Class3();\n        $c3->apples  = 'Baz';\n        $c3->bananas = 'Baz';\n\n        $this->_em->persist($c1);\n        $this->_em->persist($c2);\n        $this->_em->persist($c3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $e2 = $this->_em->find(DDC258Super::class, $c2->id);\n\n        self::assertInstanceOf(DDC258Class2::class, $e2);\n        self::assertEquals('Bar', $e2->title);\n        self::assertEquals('Bar', $e2->description);\n        self::assertEquals('Bar', $e2->text);\n\n        $all = $this->_em->getRepository(DDC258Super::class)->findAll();\n\n        foreach ($all as $obj) {\n            if ($obj instanceof DDC258Class1) {\n                self::assertEquals('Foo', $obj->title);\n                self::assertEquals('Foo', $obj->description);\n            } elseif ($obj instanceof DDC258Class2) {\n                self::assertSame($e2, $obj);\n                self::assertEquals('Bar', $obj->title);\n                self::assertEquals('Bar', $obj->description);\n                self::assertEquals('Bar', $obj->text);\n            } elseif ($obj instanceof DDC258Class3) {\n                self::assertEquals('Baz', $obj->apples);\n                self::assertEquals('Baz', $obj->bananas);\n            } else {\n                self::fail('Instance of DDC258Class1, DDC258Class2 or DDC258Class3 expected.');\n            }\n        }\n    }\n}\n\n#[Table(name: 'DDC258Super')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['class1' => 'DDC258Class1', 'class2' => 'DDC258Class2', 'class3' => 'DDC258Class3'])]\nabstract class DDC258Super\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\nclass DDC258Class1 extends DDC258Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $title;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $description;\n}\n\n#[Entity]\nclass DDC258Class2 extends DDC258Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $title;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $description;\n\n    /** @var string */\n    #[Column(name: 'text', type: 'text')]\n    public $text;\n}\n\n/**\n * An extra class to demonstrate why title and description aren't in Super\n */\n#[Entity]\nclass DDC258Class3 extends DDC258Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $apples;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $bananas;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2602Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Event\\PostLoadEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function in_array;\nuse function json_decode;\n\nuse const JSON_THROW_ON_ERROR;\n\n#[Group('DDC-2602')]\nclass DDC2602Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2602User::class,\n            DDC2602Biography::class,\n            DDC2602BiographyField::class,\n            DDC2602BiographyFieldChoice::class,\n        );\n\n        $this->loadFixture();\n    }\n\n    public function testPostLoadListenerShouldBeAbleToRunQueries(): void\n    {\n        $eventManager = $this->_em->getEventManager();\n        $eventManager->addEventListener([Events::postLoad], new DDC2602PostLoadListener());\n\n        $result = $this->_em->createQuery('SELECT u, b FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2602User u JOIN u.biography b')\n                             ->getResult();\n\n        self::assertCount(2, $result);\n        self::assertCount(2, $result[0]->biography->fieldList);\n        self::assertCount(1, $result[1]->biography->fieldList);\n    }\n\n    private function loadFixture(): void\n    {\n        $user1                 = new DDC2602User();\n        $user2                 = new DDC2602User();\n        $biography1            = new DDC2602Biography();\n        $biography2            = new DDC2602Biography();\n        $biographyField1       = new DDC2602BiographyField();\n        $biographyField2       = new DDC2602BiographyField();\n        $biographyFieldChoice1 = new DDC2602BiographyFieldChoice();\n        $biographyFieldChoice2 = new DDC2602BiographyFieldChoice();\n        $biographyFieldChoice3 = new DDC2602BiographyFieldChoice();\n        $biographyFieldChoice4 = new DDC2602BiographyFieldChoice();\n        $biographyFieldChoice5 = new DDC2602BiographyFieldChoice();\n        $biographyFieldChoice6 = new DDC2602BiographyFieldChoice();\n\n        $user1->name      = 'Gblanco';\n        $user1->biography = $biography1;\n\n        $user2->name      = 'Beberlei';\n        $user2->biography = $biography2;\n\n        $biography1->user    = $user1;\n        $biography1->content = '[{\"field\": 1, \"choiceList\": [1,3]}, {\"field\": 2, \"choiceList\": [5]}]';\n\n        $biography2->user    = $user2;\n        $biography2->content = '[{\"field\": 1, \"choiceList\": [1,2,3,4]}]';\n\n        $biographyField1->alias = 'question_1';\n        $biographyField1->label = 'Question 1';\n        $biographyField1->choiceList->add($biographyFieldChoice1);\n        $biographyField1->choiceList->add($biographyFieldChoice2);\n        $biographyField1->choiceList->add($biographyFieldChoice3);\n        $biographyField1->choiceList->add($biographyFieldChoice4);\n\n        $biographyField2->alias = 'question_2';\n        $biographyField2->label = 'Question 2';\n        $biographyField2->choiceList->add($biographyFieldChoice5);\n        $biographyField2->choiceList->add($biographyFieldChoice6);\n\n        $biographyFieldChoice1->field = $biographyField1;\n        $biographyFieldChoice1->label = 'Answer 1.1';\n\n        $biographyFieldChoice2->field = $biographyField1;\n        $biographyFieldChoice2->label = 'Answer 1.2';\n\n        $biographyFieldChoice3->field = $biographyField1;\n        $biographyFieldChoice3->label = 'Answer 1.3';\n\n        $biographyFieldChoice4->field = $biographyField1;\n        $biographyFieldChoice4->label = 'Answer 1.4';\n\n        $biographyFieldChoice5->field = $biographyField2;\n        $biographyFieldChoice5->label = 'Answer 2.1';\n\n        $biographyFieldChoice6->field = $biographyField2;\n        $biographyFieldChoice6->label = 'Answer 2.2';\n\n        $this->_em->persist($user1);\n        $this->_em->persist($user2);\n\n        $this->_em->persist($biographyField1);\n        $this->_em->persist($biographyField2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n\n\nclass DDC2602PostLoadListener\n{\n    public function postLoad(PostLoadEventArgs $event): void\n    {\n        $entity = $event->getObject();\n\n        if (! ($entity instanceof DDC2602Biography)) {\n            return;\n        }\n\n        $entityManager = $event->getObjectManager();\n        $query         = $entityManager->createQuery('\n            SELECT f, fc\n              FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC2602BiographyField f INDEX BY f.id\n              JOIN f.choiceList fc INDEX BY fc.id\n        ');\n\n        $result    = $query->getResult();\n        $content   = json_decode($entity->content, null, 512, JSON_THROW_ON_ERROR);\n        $fieldList = new ArrayCollection();\n\n        foreach ($content as $selection) {\n            $field          = $result[$selection->field];\n            $choiceList     = $selection->choiceList;\n            $fieldSelection = new DDC2602FieldSelection();\n\n            $fieldSelection->field      = $field;\n            $fieldSelection->choiceList = $field->choiceList->filter(static fn ($choice) => in_array($choice->id, $choiceList, true));\n\n            $fieldList->add($fieldSelection);\n        }\n\n        $entity->fieldList = $fieldList;\n    }\n}\n\n\n#[Entity]\nclass DDC2602User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 15)]\n    public $name;\n\n    /** @var DDC2602Biography */\n    #[OneToOne(targetEntity: 'DDC2602Biography', inversedBy: 'user', cascade: ['persist', 'refresh', 'remove'])]\n    #[JoinColumn(nullable: false)]\n    public $biography;\n}\n\n#[Entity]\nclass DDC2602Biography\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC2602User */\n    #[OneToOne(targetEntity: 'DDC2602User', mappedBy: 'biography', cascade: ['persist', 'refresh'])]\n    public $user;\n\n    /** @var string */\n    #[Column(type: 'text', nullable: true)]\n    public $content;\n\n    /** @var array */\n    public $fieldList = [];\n}\n\n#[Entity]\nclass DDC2602BiographyField\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', unique: true, length: 100)]\n    public $alias;\n\n    /** @var string */\n    #[Column(type: 'string', length: 100)]\n    public $label;\n\n    /** @var ArrayCollection */\n    #[OneToMany(targetEntity: 'DDC2602BiographyFieldChoice', mappedBy: 'field', cascade: ['persist', 'refresh'])]\n    public $choiceList;\n\n    public function __construct()\n    {\n        $this->choiceList = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC2602BiographyFieldChoice\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', unique: true, length: 100)]\n    public $label;\n\n    /** @var DDC2602BiographyField */\n    #[ManyToOne(targetEntity: 'DDC2602BiographyField', inversedBy: 'choiceList')]\n    #[JoinColumn(onDelete: 'CASCADE')]\n    public $field;\n}\n\nclass DDC2602FieldSelection\n{\n    /** @var DDC2602BiographyField */\n    public $field;\n\n    /** @var ArrayCollection */\n    public $choiceList;\n\n    public function __construct()\n    {\n        $this->choiceList = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2655Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2655')]\nclass DDC2655Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testSingleScalarOneOrNullResult(): void\n    {\n        $query = $this->_em->createQuery(\"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'happy_doctrine_user'\");\n        self::assertNull($query->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2660Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2660')]\nclass DDC2660Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        try {\n            $this->_schemaTool->createSchema(\n                [\n                    $this->_em->getClassMetadata(DDC2660Product::class),\n                    $this->_em->getClassMetadata(DDC2660Customer::class),\n                    $this->_em->getClassMetadata(DDC2660CustomerOrder::class),\n                ],\n            );\n        } catch (Exception) {\n            return;\n        }\n\n        for ($i = 0; $i < 5; $i++) {\n            $product  = new DDC2660Product();\n            $customer = new DDC2660Customer();\n            $order    = new DDC2660CustomerOrder($product, $customer, 'name' . $i);\n\n            $this->_em->persist($product);\n            $this->_em->persist($customer);\n            $this->_em->flush();\n\n            $this->_em->persist($order);\n            $this->_em->flush();\n        }\n\n        $this->_em->clear();\n    }\n\n    public function testIssueWithExtraColumn(): void\n    {\n        $sql = 'SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o';\n\n        $rsm = new ResultSetMappingBuilder($this->getEntityManager());\n        $rsm->addRootEntityFromClassMetadata(DDC2660CustomerOrder::class, 'c');\n\n        $query  = $this->_em->createNativeQuery($sql, $rsm);\n        $result = $query->getResult();\n\n        self::assertCount(5, $result);\n\n        foreach ($result as $order) {\n            self::assertNotNull($order);\n            self::assertInstanceOf(DDC2660CustomerOrder::class, $order);\n        }\n    }\n\n    public function testIssueWithoutExtraColumn(): void\n    {\n        $sql = 'SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o';\n\n        $rsm = new ResultSetMappingBuilder($this->getEntityManager());\n        $rsm->addRootEntityFromClassMetadata(DDC2660CustomerOrder::class, 'c');\n\n        $query  = $this->_em->createNativeQuery($sql, $rsm);\n        $result = $query->getResult();\n\n        self::assertCount(5, $result);\n\n        foreach ($result as $order) {\n            self::assertNotNull($order);\n            self::assertInstanceOf(DDC2660CustomerOrder::class, $order);\n        }\n    }\n}\n#[Table(name: 'ddc_2660_product')]\n#[Entity]\nclass DDC2660Product\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Table(name: 'ddc_2660_customer')]\n#[Entity]\nclass DDC2660Customer\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Table(name: 'ddc_2660_customer_order')]\n#[Entity]\nclass DDC2660CustomerOrder\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC2660Product')]\n        public DDC2660Product $product,\n        #[Id]\n        #[ManyToOne(targetEntity: 'DDC2660Customer')]\n        public DDC2660Customer $customer,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2692Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\EventSubscriber;\nuse Doctrine\\ORM\\Event\\PreFlushEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2692')]\nclass DDC2692Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2692Foo::class);\n\n        $this->_em->clear();\n    }\n\n    public function testIsListenerCalledOnlyOnceOnPreFlush(): void\n    {\n        $listener = new class implements EventSubscriber\n        {\n            /** @var int */\n            public $registeredCalls = 0;\n\n            /**\n             * {@inheritDoc}\n             */\n            public function getSubscribedEvents(): array\n            {\n                return [Events::preFlush];\n            }\n\n            public function preFlush(PreFlushEventArgs $args): void\n            {\n                ++$this->registeredCalls;\n            }\n        };\n\n        $this->_em->getEventManager()->addEventSubscriber($listener);\n\n        $this->_em->persist(new DDC2692Foo());\n        $this->_em->persist(new DDC2692Foo());\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertSame(1, $listener->registeredCalls);\n    }\n}\n#[Table(name: 'ddc_2692_foo')]\n#[Entity]\nclass DDC2692Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2759Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2759')]\nclass DDC2759Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC2759Qualification::class,\n            DDC2759Category::class,\n            DDC2759QualificationMetadata::class,\n            DDC2759MetadataCategory::class,\n        );\n\n        $qualification         = new DDC2759Qualification();\n        $qualificationMetadata = new DDC2759QualificationMetadata($qualification);\n\n        $category1 = new DDC2759Category();\n        $category2 = new DDC2759Category();\n\n        $metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1);\n        $metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2);\n\n        $this->_em->persist($qualification);\n        $this->_em->persist($qualificationMetadata);\n\n        $this->_em->persist($category1);\n        $this->_em->persist($category2);\n\n        $this->_em->persist($metadataCategory1);\n        $this->_em->persist($metadataCategory2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testCorrectNumberOfAssociationsIsReturned(): void\n    {\n        $repository = $this->_em->getRepository(DDC2759Qualification::class);\n\n        $builder = $repository->createQueryBuilder('q')\n            ->select('q, qm, qmc')\n            ->innerJoin('q.metadata', 'qm')\n            ->innerJoin('qm.metadataCategories', 'qmc');\n\n        $result = $builder->getQuery()\n            ->getArrayResult();\n\n        self::assertCount(2, $result[0]['metadata']['metadataCategories']);\n    }\n}\n\n#[Table(name: 'ddc_2759_qualification')]\n#[Entity]\nclass DDC2759Qualification\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2759QualificationMetadata */\n    #[OneToOne(targetEntity: 'DDC2759QualificationMetadata', mappedBy: 'content')]\n    public $metadata;\n}\n\n#[Table(name: 'ddc_2759_category')]\n#[Entity]\nclass DDC2759Category\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC2759MetadataCategory> */\n    #[OneToMany(targetEntity: 'DDC2759MetadataCategory', mappedBy: 'category')]\n    public $metadataCategories;\n}\n\n#[Table(name: 'ddc_2759_qualification_metadata')]\n#[Entity]\nclass DDC2759QualificationMetadata\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC2759MetadataCategory> */\n    #[OneToMany(targetEntity: 'DDC2759MetadataCategory', mappedBy: 'metadata')]\n    protected $metadataCategories;\n\n    public function __construct(\n        #[OneToOne(targetEntity: 'DDC2759Qualification', inversedBy: 'metadata')]\n        public DDC2759Qualification $content,\n    ) {\n    }\n}\n\n#[Table(name: 'ddc_2759_metadata_category')]\n#[Entity]\nclass DDC2759MetadataCategory\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC2759QualificationMetadata', inversedBy: 'metadataCategories')]\n        public DDC2759QualificationMetadata $metadata,\n        #[ManyToOne(targetEntity: 'DDC2759Category', inversedBy: 'metadataCategories')]\n        public DDC2759Category $category,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2775Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Functional tests for cascade remove with class table inheritance.\n */\nclass DDC2775Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                User::class,\n                Role::class,\n                AdminRole::class,\n                Authorization::class,\n            ],\n        );\n    }\n\n    #[Group('DDC-2775')]\n    public function testIssueCascadeRemove(): void\n    {\n        $role = new AdminRole();\n        $user = new User();\n        $user->addRole($role);\n\n        $authorization = new Authorization();\n        $user->addAuthorization($authorization);\n        $role->addAuthorization($authorization);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        // Need to clear so that associations are lazy-loaded\n        $this->_em->clear();\n\n        $user = $this->_em->find(User::class, $user->id);\n\n        $this->_em->remove($user);\n        $this->_em->flush();\n\n        self::assertEmpty($this->_em->getRepository(Authorization::class)->findAll());\n\n        // With the bug, the second flush throws an error because the cascade remove didn't work correctly\n        $this->_em->flush();\n    }\n}\n\n#[Table(name: 'ddc2775_role')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'role_type', type: 'string')]\n#[DiscriminatorMap(['admin' => 'AdminRole'])]\nabstract class Role\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var User */\n    #[ManyToOne(targetEntity: 'User', inversedBy: 'roles')]\n    public $user;\n\n    /** @phpstan-var Collection<int, Authorization> */\n    #[OneToMany(targetEntity: 'Authorization', mappedBy: 'role', cascade: ['all'], orphanRemoval: true)]\n    public $authorizations;\n\n    public function addAuthorization(Authorization $authorization): void\n    {\n        $this->authorizations[] = $authorization;\n        $authorization->role    = $this;\n    }\n}\n\n#[Table(name: 'ddc2775_admin_role')]\n#[Entity]\nclass AdminRole extends Role\n{\n}\n\n#[Table(name: 'ddc2775_authorizations')]\n#[Entity]\nclass Authorization\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var User */\n    #[ManyToOne(targetEntity: 'User', inversedBy: 'authorizations')]\n    public $user;\n\n    /** @var Role */\n    #[ManyToOne(targetEntity: 'Role', inversedBy: 'authorizations')]\n    public $role;\n}\n\n#[Table(name: 'ddc2775_users')]\n#[Entity]\nclass User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @phpstan-var Collection<int, Role> */\n    #[OneToMany(targetEntity: 'Role', mappedBy: 'user', cascade: ['all'], orphanRemoval: true)]\n    public $roles;\n\n    /** @phpstan-var Collection<int, Authorization> */\n    #[OneToMany(targetEntity: 'Authorization', mappedBy: 'user', cascade: ['all'], orphanRemoval: true)]\n    public $authorizations;\n\n    public function addRole(Role $role): void\n    {\n        $this->roles[] = $role;\n        $role->user    = $this;\n    }\n\n    public function addAuthorization(Authorization $authorization): void\n    {\n        $this->authorizations[] = $authorization;\n        $authorization->user    = $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2780Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2780')]\nclass DDC2780Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2780User::class, DDC2780Project::class);\n    }\n\n    /**\n     * Verifies that IS [NOT] NULL can be used on join aliases\n     */\n    public function testIssue(): void\n    {\n        $user    = new DDC2780User();\n        $project = new DDC2780Project();\n\n        $user->project = $project;\n\n        $this->_em->persist($project);\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n            ->select('user')\n            ->from(DDC2780User::class, 'user')\n            ->leftJoin('user.project', 'project')\n            ->where('project IS NOT NULL')\n            ->getQuery()\n            ->getOneOrNullResult();\n\n        self::assertInstanceOf(DDC2780User::class, $result);\n    }\n}\n\n#[Entity]\nclass DDC2780User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2780Project */\n    #[ManyToOne(targetEntity: 'DDC2780Project')]\n    public $project;\n}\n\n#[Entity]\nclass DDC2780Project\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC2780User[] */\n    #[OneToMany(targetEntity: 'DDC2780User', mappedBy: 'project')]\n    public $users;\n\n    /** Constructor */\n    public function __construct()\n    {\n        $this->users = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2790Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Event\\OnFlushEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_intersect_key;\nuse function intval;\n\n#[Group('DDC-2790')]\nclass DDC2790Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    /**\n     * Verifies that entities scheduled for deletion are not treated as updated by UoW,\n     * even if their properties are changed after the remove() call\n     */\n    public function testIssue(): void\n    {\n        $this->_em->getEventManager()->addEventListener(Events::onFlush, new OnFlushListener());\n\n        $entity           = new CmsUser();\n        $entity->username = 'romanb';\n        $entity->name     = 'Roman';\n\n        $qb = $this->_em->createQueryBuilder();\n        $qb->from($entity::class, 'c');\n        $qb->select('count(c)');\n        $initial = intval($qb->getQuery()->getSingleScalarResult());\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->remove($entity);\n        // in Doctrine <2.5, this causes an UPDATE statement to be added before the DELETE statement\n        // (and consequently also triggers preUpdate/postUpdate for the entity in question)\n        $entity->name = 'Robin';\n\n        $this->_em->flush();\n\n        $qb = $this->_em->createQueryBuilder();\n        $qb->from($entity::class, 'c');\n        $qb->select('count(c)');\n        $count = intval($qb->getQuery()->getSingleScalarResult());\n        self::assertEquals($initial, $count);\n    }\n}\n\nclass OnFlushListener\n{\n    /**\n     * onFLush listener that tries to cancel deletions by calling persist if the entity is listed\n     * as updated in UoW\n     */\n    public function onFlush(OnFlushEventArgs $args): void\n    {\n        $em        = $args->getObjectManager();\n        $uow       = $em->getUnitOfWork();\n        $deletions = $uow->getScheduledEntityDeletions();\n        $updates   = $uow->getScheduledEntityUpdates();\n\n        $undelete = array_intersect_key($deletions, $updates);\n        foreach ($undelete as $d) {\n            $em->persist($d);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC279Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC279Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC279EntityXAbstract::class,\n            DDC279EntityX::class,\n            DDC279EntityY::class,\n            DDC279EntityZ::class,\n        );\n    }\n\n    #[Group('DDC-279')]\n    public function testDDC279(): void\n    {\n        $x = new DDC279EntityX();\n        $y = new DDC279EntityY();\n        $z = new DDC279EntityZ();\n\n        $x->data = 'X';\n        $y->data = 'Y';\n        $z->data = 'Z';\n\n        $x->y = $y;\n        $y->z = $z;\n\n        $this->_em->persist($x);\n        $this->_em->persist($y);\n        $this->_em->persist($z);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery(\n            'SELECT x, y, z FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC279EntityX x ' .\n            'INNER JOIN x.y y INNER JOIN y.z z WHERE x.id = ?1',\n        )->setParameter(1, $x->id);\n\n        $result = $query->getResult();\n\n        $expected1 = 'Y';\n        $expected2 = 'Z';\n\n        self::assertCount(1, $result);\n\n        self::assertEquals($expected1, $result[0]->y->data);\n        self::assertEquals($expected2, $result[0]->y->z->data);\n    }\n}\n\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['DDC279EntityX' => 'DDC279EntityX'])]\nabstract class DDC279EntityXAbstract\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'id', type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n}\n\n#[Entity]\nclass DDC279EntityX extends DDC279EntityXAbstract\n{\n    /** @var DDC279EntityY */\n    #[OneToOne(targetEntity: 'DDC279EntityY')]\n    #[JoinColumn(name: 'y_id', referencedColumnName: 'id')]\n    public $y;\n}\n\n#[Entity]\nclass DDC279EntityY\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'id', type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n\n    /** @var DDC279EntityZ */\n    #[OneToOne(targetEntity: 'DDC279EntityZ')]\n    #[JoinColumn(name: 'z_id', referencedColumnName: 'id')]\n    public $z;\n}\n\n#[Entity]\nclass DDC279EntityZ\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'id', type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2825Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\Models\\DDC2825\\ExplicitSchemaAndTable;\nuse Doctrine\\Tests\\Models\\DDC2825\\SchemaAndTableInTableName;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * This class makes tests on the correct use of a database schema when entities are stored\n */\n#[Group('DDC-2825')]\nclass DDC2825Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        if (! $platform->supportsSchemas()) {\n            self::markTestSkipped('This test is only useful for databases that support schemas or can emulate them.');\n        }\n    }\n\n    /** @param class-string $className */\n    #[DataProvider('getTestedClasses')]\n    public function testPersistenceOfEntityWithSchemaMapping(string $className): void\n    {\n        $this->createSchemaForModels($className);\n\n        $this->_em->persist(new $className());\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertCount(1, $this->_em->getRepository($className)->findAll());\n    }\n\n    /** @return list<array{class-string}> */\n    public static function getTestedClasses(): array\n    {\n        return [\n            [ExplicitSchemaAndTable::class],\n            [SchemaAndTableInTableName::class],\n            [DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName::class],\n            [File::class],\n        ];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'myschema.order')]\nclass DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: '`file`', schema: 'yourschema')]\nclass File\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2862Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2862')]\n#[Group('DDC-2183')]\nclass DDC2862Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2862User::class, DDC2862Driver::class);\n    }\n\n    public function testIssue(): void\n    {\n        $user1   = new DDC2862User('Foo');\n        $driver1 = new DDC2862Driver('Bar', $user1);\n\n        $this->_em->persist($user1);\n        $this->_em->persist($driver1);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->_em->getCache()->containsEntity(DDC2862User::class, ['id' => $user1->getId()]));\n        self::assertTrue($this->_em->getCache()->containsEntity(DDC2862Driver::class, ['id' => $driver1->getId()]));\n\n        $this->getQueryLog()->reset()->enable();\n        $driver2 = $this->_em->find(DDC2862Driver::class, $driver1->getId());\n\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(DDC2862Driver::class, $driver2);\n        self::assertInstanceOf(DDC2862User::class, $driver2->getUserProfile());\n\n        $driver2->setName('Franta');\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertTrue($this->_em->getCache()->containsEntity(DDC2862User::class, ['id' => $user1->getId()]));\n        self::assertTrue($this->_em->getCache()->containsEntity(DDC2862Driver::class, ['id' => $driver1->getId()]));\n\n        $this->getQueryLog()->reset()->enable();\n        $driver3 = $this->_em->find(DDC2862Driver::class, $driver1->getId());\n\n        $this->assertQueryCount(0);\n        self::assertInstanceOf(DDC2862Driver::class, $driver3);\n        self::assertInstanceOf(DDC2862User::class, $driver3->getUserProfile());\n        self::assertEquals('Franta', $driver3->getName());\n        self::assertEquals('Foo', $driver3->getUserProfile()->getName());\n    }\n\n    public function testIssueReopened(): void\n    {\n        $user1   = new DDC2862User('Foo');\n        $driver1 = new DDC2862Driver('Bar', $user1);\n\n        $this->_em->persist($user1);\n        $this->_em->persist($driver1);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->_em->getCache()->evictEntityRegion(DDC2862User::class);\n        $this->_em->getCache()->evictEntityRegion(DDC2862Driver::class);\n\n        self::assertFalse($this->_em->getCache()->containsEntity(DDC2862User::class, ['id' => $user1->getId()]));\n        self::assertFalse($this->_em->getCache()->containsEntity(DDC2862Driver::class, ['id' => $driver1->getId()]));\n\n        $this->getQueryLog()->reset()->enable();\n        $driver2 = $this->_em->find(DDC2862Driver::class, $driver1->getId());\n\n        self::assertInstanceOf(DDC2862Driver::class, $driver2);\n        self::assertInstanceOf(DDC2862User::class, $driver2->getUserProfile());\n        $this->assertQueryCount(1);\n\n        $this->_em->clear();\n\n        self::assertFalse($this->_em->getCache()->containsEntity(DDC2862User::class, ['id' => $user1->getId()]));\n        self::assertTrue($this->_em->getCache()->containsEntity(DDC2862Driver::class, ['id' => $driver1->getId()]));\n\n        $this->getQueryLog()->reset()->enable();\n        $driver3 = $this->_em->find(DDC2862Driver::class, $driver1->getId());\n\n        self::assertInstanceOf(DDC2862Driver::class, $driver3);\n        self::assertInstanceOf(DDC2862User::class, $driver3->getUserProfile());\n        $this->assertQueryCount(0);\n        self::assertEquals('Foo', $driver3->getUserProfile()->getName());\n        $this->assertQueryCount(1);\n\n        $this->getQueryLog()->reset()->enable();\n        $driver4 = $this->_em->find(DDC2862Driver::class, $driver1->getId());\n\n        self::assertInstanceOf(DDC2862Driver::class, $driver4);\n        self::assertInstanceOf(DDC2862User::class, $driver4->getUserProfile());\n        $this->assertQueryCount(0);\n        self::assertEquals('Foo', $driver4->getUserProfile()->getName());\n        $this->assertQueryCount(0);\n    }\n}\n\n#[Table(name: 'ddc2862_drivers')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass DDC2862Driver\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        protected string $name,\n        #[Cache]\n        #[OneToOne(targetEntity: 'DDC2862User')]\n        protected DDC2862User|null $userProfile = null,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setUserProfile(DDC2862User $userProfile): void\n    {\n        $this->userProfile = $userProfile;\n    }\n\n    public function getUserProfile(): DDC2862User\n    {\n        return $this->userProfile;\n    }\n}\n\n#[Table(name: 'ddc2862_users')]\n#[Entity]\n#[Cache('NONSTRICT_READ_WRITE')]\nclass DDC2862User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        protected string $name,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2895Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\PrePersist;\nuse Doctrine\\ORM\\Mapping\\PreUpdate;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function assert;\n\nclass DDC2895Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2895::class);\n    }\n\n    public function testPostLoadOneToManyInheritance(): void\n    {\n        $cm = $this->_em->getClassMetadata(DDC2895::class);\n\n        self::assertEquals(\n            [\n                'prePersist' => ['setLastModifiedPreUpdate'],\n                'preUpdate' => ['setLastModifiedPreUpdate'],\n            ],\n            $cm->lifecycleCallbacks,\n        );\n\n        $ddc2895 = new DDC2895();\n\n        $this->_em->persist($ddc2895);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $ddc2895 = $this->_em->find($ddc2895::class, $ddc2895->id);\n        assert($ddc2895 instanceof DDC2895);\n\n        self::assertNotNull($ddc2895->getLastModified());\n    }\n}\n\n#[MappedSuperclass]\n#[HasLifecycleCallbacks]\nabstract class AbstractDDC2895\n{\n    /** @var DateTime */\n    #[Column(name: 'last_modified', type: 'datetimetz', nullable: false)]\n    protected $lastModified;\n\n    #[PrePersist]\n    #[PreUpdate]\n    public function setLastModifiedPreUpdate(): void\n    {\n        $this->setLastModified(new DateTime());\n    }\n\n    public function setLastModified(DateTime $lastModified): void\n    {\n        $this->lastModified = $lastModified;\n    }\n\n    public function getLastModified(): DateTime\n    {\n        return $this->lastModified;\n    }\n}\n\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC2895 extends AbstractDDC2895\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function setId(mixed $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getId(): mixed\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2931Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2931')]\nclass DDC2931Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC2931User::class);\n    }\n\n    public function testIssue(): void\n    {\n        $first  = new DDC2931User();\n        $second = new DDC2931User();\n        $third  = new DDC2931User();\n\n        $second->parent = $first;\n        $third->parent  = $second;\n\n        $this->_em->persist($first);\n        $this->_em->persist($second);\n        $this->_em->persist($third);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $second = $this->_em->find(DDC2931User::class, $second->id);\n\n        self::assertSame(2, $second->getRank());\n    }\n\n    public function testFetchJoinedEntitiesCanBeRefreshed(): void\n    {\n        $first  = new DDC2931User();\n        $second = new DDC2931User();\n        $third  = new DDC2931User();\n\n        $second->parent = $first;\n        $third->parent  = $second;\n\n        $first->value  = 1;\n        $second->value = 2;\n        $third->value  = 3;\n\n        $this->_em->persist($first);\n        $this->_em->persist($second);\n        $this->_em->persist($third);\n\n        $this->_em->flush();\n\n        $first->value  = 4;\n        $second->value = 5;\n        $third->value  = 6;\n\n        $refreshedSecond = $this\n            ->_em\n            ->createQuery(\n                'SELECT e, p, c FROM '\n                . __NAMESPACE__ . '\\\\DDC2931User e LEFT JOIN e.parent p LEFT JOIN e.child c WHERE e = :id',\n            )\n            ->setParameter('id', $second)\n            ->setHint(Query::HINT_REFRESH, true)\n            ->getResult();\n\n        self::assertCount(1, $refreshedSecond);\n        self::assertSame(1, $first->value);\n        self::assertSame(2, $second->value);\n        self::assertSame(3, $third->value);\n    }\n}\n\n\n#[Entity]\nclass DDC2931User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC2931User */\n    #[OneToOne(targetEntity: 'DDC2931User', inversedBy: 'child')]\n    public $parent;\n\n    /** @var DDC2931User */\n    #[OneToOne(targetEntity: 'DDC2931User', mappedBy: 'parent')]\n    public $child;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $value = 0;\n\n    /**\n     * Return Rank recursively\n     * My rank is 1 + rank of my parent\n     */\n    public function getRank(): int\n    {\n        return 1 + ($this->parent ? $this->parent->getRank() : 0);\n    }\n\n    public function __wakeup(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2943Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2943')]\nclass DDC2943Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n        $this->useModelSet('cache');\n\n        parent::setUp();\n    }\n\n    private function loadFixtures(): void\n    {\n        $this->_em->persist(new Country('Brazil'));\n        $this->_em->persist(new Country('Canada'));\n        $this->_em->persist(new Country('Germany'));\n        $this->_em->persist(new Country('France'));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testIssue(): void\n    {\n        $this->loadFixtures();\n\n        $region = $this->_em->getCache()->getEntityCacheRegion(Country::class);\n        $dql    = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $query  = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->setFirstResult(0)\n            ->setMaxResults(2);\n\n        $this->assertPaginatorQueryPut(new Paginator(clone $query), $region->getName(), 4, 2);\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->assertPaginatorQueryHit(new Paginator(clone $query), $region->getName(), 4, 2);\n    }\n\n    public function testIssueNonFetchJoin(): void\n    {\n        $this->loadFixtures();\n\n        $region = $this->_em->getCache()->getEntityCacheRegion(Country::class);\n        $dql    = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c';\n        $query  = $this->_em->createQuery($dql)\n            ->setCacheable(true)\n            ->setFirstResult(0)\n            ->setMaxResults(2);\n\n        $this->assertPaginatorQueryPut(new Paginator(clone $query, false), $region->getName(), 4, 2);\n\n        $this->_em->clear();\n        $this->secondLevelCacheLogger->clearStats();\n\n        $this->assertPaginatorQueryHit(new Paginator(clone $query, false), $region->getName(), 4, 2);\n    }\n\n    public function assertPaginatorQueryPut(Paginator $paginator, $regionName, $count, $pageSize): void\n    {\n        self::assertCount($count, $paginator);\n        self::assertCount($pageSize, $paginator->getIterator());\n\n        self::assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount(Cache::DEFAULT_QUERY_REGION_NAME));\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount(Cache::DEFAULT_QUERY_REGION_NAME));\n        self::assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($regionName));\n        self::assertEquals($count, $this->secondLevelCacheLogger->getRegionPutCount($regionName));\n    }\n\n    public function assertPaginatorQueryHit(Paginator $paginator, $regionName, $count, $pageSize): void\n    {\n        self::assertCount($count, $paginator);\n        self::assertCount($pageSize, $paginator->getIterator());\n\n        self::assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount(Cache::DEFAULT_QUERY_REGION_NAME));\n        self::assertEquals(0, $this->secondLevelCacheLogger->getRegionPutCount(Cache::DEFAULT_QUERY_REGION_NAME));\n        self::assertEquals($pageSize, $this->secondLevelCacheLogger->getRegionHitCount($regionName));\n        self::assertEquals(0, $this->secondLevelCacheLogger->getRegionPutCount($regionName));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2984Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\ConversionException;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\nuse function is_string;\n\n#[Group('DDC-2984')]\nclass DDC2984Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! Type::hasType('ddc2984_domain_user_id')) {\n            Type::addType(\n                'ddc2984_domain_user_id',\n                DDC2984UserIdCustomDbalType::class,\n            );\n        }\n\n        $this->createSchemaForModels(DDC2984User::class);\n    }\n\n    public function testIssue(): void\n    {\n        $user = new DDC2984User(new DDC2984DomainUserId('unique_id_within_a_vo'));\n        $user->applyName('Alex');\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $repository = $this->_em->getRepository(__NAMESPACE__ . '\\DDC2984User');\n\n        $sameUser = $repository->find(new DDC2984DomainUserId('unique_id_within_a_vo'));\n\n        //Until know, everything works as expected\n        self::assertTrue($user->sameIdentityAs($sameUser));\n\n        $this->_em->clear();\n\n        //After clearing the identity map, the UnitOfWork produces the warning described in DDC-2984\n        $equalUser = $repository->find(new DDC2984DomainUserId('unique_id_within_a_vo'));\n\n        self::assertNotSame($user, $equalUser);\n        self::assertTrue($user->sameIdentityAs($equalUser));\n    }\n}\n\n#[Table(name: 'users')]\n#[Entity]\nclass DDC2984User\n{\n    #[Column(type: 'string', length: 50)]\n    private string|null $name = null;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'ddc2984_domain_user_id', length: 255)]\n        #[GeneratedValue(strategy: 'NONE')]\n        private DDC2984DomainUserId $userId,\n    ) {\n    }\n\n    public function userId(): DDC2984DomainUserId\n    {\n        return $this->userId;\n    }\n\n    public function name(): string\n    {\n        return $this->name;\n    }\n\n    public function applyName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function sameIdentityAs(DDC2984User $other): bool\n    {\n        return $this->userId()->sameValueAs($other->userId());\n    }\n}\n\n/**\n * DDC2984DomainUserId ValueObject\n */\nclass DDC2984DomainUserId implements Stringable\n{\n    public function __construct(private string $userIdString)\n    {\n    }\n\n    public function toString(): string\n    {\n        return $this->userIdString;\n    }\n\n    public function __toString(): string\n    {\n        return $this->toString();\n    }\n\n    public function sameValueAs(DDC2984DomainUserId $other): bool\n    {\n        return $this->toString() === $other->toString();\n    }\n}\n\nclass DDC2984UserIdCustomDbalType extends StringType\n{\n    private const TYPE_NAME = 'ddc2984_domain_user_id';\n\n    public function getName(): string\n    {\n        return self::TYPE_NAME;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): DDC2984DomainUserId|null\n    {\n        return ! empty($value)\n            ? new DDC2984DomainUserId($value)\n            : null;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed\n    {\n        if (empty($value)) {\n            return null;\n        }\n\n        if (is_string($value)) {\n            return $value;\n        }\n\n        if (! $value instanceof DDC2984DomainUserId) {\n            throw ConversionException::conversionFailed($value, self::TYPE_NAME);\n        }\n\n        return $value->toString();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC2996Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\PreFlush;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2996')]\nclass DDC2996Test extends OrmFunctionalTestCase\n{\n    public function testIssue(): void\n    {\n        $this->createSchemaForModels(\n            DDC2996User::class,\n            DDC2996UserPreference::class,\n        );\n\n        $pref        = new DDC2996UserPreference();\n        $pref->user  = new DDC2996User();\n        $pref->value = 'foo';\n\n        $this->_em->persist($pref);\n        $this->_em->persist($pref->user);\n        $this->_em->flush();\n\n        $pref->value = 'bar';\n        $this->_em->flush();\n\n        self::assertEquals(1, $pref->user->counter);\n\n        $this->_em->clear();\n\n        $pref = $this->_em->find(DDC2996UserPreference::class, $pref->id);\n        self::assertEquals(1, $pref->user->counter);\n    }\n}\n\n#[Entity]\nclass DDC2996User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $counter = 0;\n}\n\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC2996UserPreference\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $value;\n\n    /** @var DDC2996User */\n    #[ManyToOne(targetEntity: 'DDC2996User')]\n    public $user;\n\n    #[PreFlush]\n    public function preFlush($event): void\n    {\n        $em  = $event->getObjectManager();\n        $uow = $em->getUnitOfWork();\n\n        if ($uow->getOriginalEntityData($this->user)) {\n            $this->user->counter++;\n            $uow->recomputeSingleEntityChangeSet(\n                $em->getClassMetadata($this->user::class),\n                $this->user,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3033Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Event\\PostUpdateEventArgs;\nuse Doctrine\\ORM\\Event\\PreUpdateEventArgs;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\PostUpdate;\nuse Doctrine\\ORM\\Mapping\\PreUpdate;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-3033')]\nclass DDC3033Test extends OrmFunctionalTestCase\n{\n    public function testIssue(): void\n    {\n        $this->createSchemaForModels(\n            DDC3033User::class,\n            DDC3033Product::class,\n        );\n\n        $user       = new DDC3033User();\n        $user->name = 'Test User';\n        $this->_em->persist($user);\n\n        $user2       = new DDC3033User();\n        $user2->name = 'Test User 2';\n        $this->_em->persist($user2);\n\n        $product           = new DDC3033Product();\n        $product->title    = 'Test product';\n        $product->buyers[] = $user;\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $product->title    = 'Test Change title';\n        $product->buyers[] = $user2;\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $expect = [\n            'title' => [\n                0 => 'Test product',\n                1 => 'Test Change title',\n            ],\n        ];\n\n        self::assertEquals($expect, $product->changeSet);\n    }\n}\n\n#[Table]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC3033Product\n{\n    /** @phpstan-var array<string, array{mixed, mixed}> */\n    public $changeSet = [];\n\n    /** @var int $id */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string $title */\n    #[Column(name: 'title', type: 'string', length: 255)]\n    public $title;\n\n    /** @var Collection<int, DDC3033User> */\n    #[JoinTable(name: 'user_purchases_3033')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC3033User')]\n    public $buyers;\n\n    /**\n     * Default constructor\n     */\n    public function __construct()\n    {\n        $this->buyers = new ArrayCollection();\n    }\n\n    #[PreUpdate]\n    public function preUpdate(PreUpdateEventArgs $eventArgs): void\n    {\n    }\n\n    #[PostUpdate]\n    public function postUpdate(PostUpdateEventArgs $eventArgs): void\n    {\n        $em            = $eventArgs->getObjectManager();\n        $uow           = $em->getUnitOfWork();\n        $entity        = $eventArgs->getObject();\n        $classMetadata = $em->getClassMetadata($entity::class);\n\n        $uow->computeChangeSet($classMetadata, $entity);\n        $this->changeSet = $uow->getEntityChangeSet($entity);\n    }\n}\n\n#[Table]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC3033User\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 255)]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3042Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function substr_count;\n\n#[Group('DDC-3042')]\nclass DDC3042Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC3042Foo::class, DDC3042Bar::class);\n    }\n\n    public function testSQLGenerationDoesNotProvokeAliasCollisions(): void\n    {\n        self::assertSame(\n            1,\n            substr_count(\n                $this\n                ->_em\n                ->createQuery(\n                    'SELECT f, b FROM ' . __NAMESPACE__ . '\\DDC3042Foo f JOIN ' . __NAMESPACE__ . '\\DDC3042Bar b ON 1 = 1',\n                )\n                ->getSQL(),\n                'field_11',\n            ),\n            'The alias \"field11\" should only appear once in the SQL query.',\n        );\n    }\n}\n\n#[Entity]\nclass DDC3042Foo\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $field;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field1;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field2;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field3;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field4;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field5;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field6;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field7;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field8;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field9;\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $field10;\n}\n\n#[Entity]\nclass DDC3042Bar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3068Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Taxi\\Car;\nuse Doctrine\\Tests\\Models\\Taxi\\Driver;\nuse Doctrine\\Tests\\Models\\Taxi\\Ride;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-3068')]\nclass DDC3068Test extends OrmFunctionalTestCase\n{\n    private Driver $foo;\n\n    private Car $merc;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('taxi');\n\n        parent::setUp();\n\n        $this->foo = new Driver();\n        $this->foo->setName('Foo Bar');\n        $this->_em->persist($this->foo);\n\n        $this->merc = new Car();\n        $this->merc->setBrand('Mercedes');\n        $this->merc->setModel('C-Class');\n        $this->_em->persist($this->merc);\n\n        $this->_em->flush();\n\n        $ride = new Ride($this->foo, $this->merc);\n        $this->_em->persist($ride);\n\n        $this->_em->flush();\n    }\n\n    public function testFindUsingAnArrayOfObjectAsPrimaryKey(): void\n    {\n        $ride1 = $this->_em->find(Ride::class, [\n            'driver' => $this->foo->getId(),\n            'car'    => $this->merc->getBrand(),\n        ]);\n\n        self::assertInstanceOf(Ride::class, $ride1);\n\n        $ride2 = $this->_em->find(Ride::class, [\n            'driver' => $this->foo,\n            'car'    => $this->merc,\n        ]);\n\n        self::assertInstanceOf(Ride::class, $ride2);\n        self::assertSame($ride1, $ride2);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC309Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\n\nclass DDC309Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC309Country::class, DDC309User::class);\n    }\n\n    public function testTwoToIterableHydrations(): void\n    {\n        $c1 = new DDC309Country();\n        $c2 = new DDC309Country();\n        $u1 = new DDC309User();\n        $u2 = new DDC309User();\n\n        $this->_em->persist($c1);\n        $this->_em->persist($c2);\n        $this->_em->persist($u1);\n        $this->_em->persist($u2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        /** @var Generator<int, DDC309Country> $q */\n        $q = $this->_em->createQuery('SELECT c FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC309Country c')->toIterable();\n        $c = $q->current();\n\n        self::assertEquals(1, $c->id);\n\n        /** @var Generator<int, DDC309User> $r */\n        $r = $this->_em->createQuery('SELECT u FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC309User u')->toIterable();\n        $u = $r->current();\n\n        self::assertEquals(1, $u->id);\n\n        $q->next();\n        $r->next();\n        $c = $q->current();\n        $u = $r->current();\n\n        self::assertEquals(2, $c->id);\n        self::assertEquals(2, $u->id);\n\n        do {\n            $q->next();\n        } while ($q->valid());\n\n        do {\n            $r->next();\n        } while ($r->valid());\n    }\n}\n\n#[Entity]\nclass DDC309Country\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC309User\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3103Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\Persistence\\Mapping\\StaticReflectionService;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\nuse function serialize;\nuse function unserialize;\n\n#[CoversClass(ClassMetadata::class)]\n#[Group('DDC-3103')]\nclass DDC3103Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        if (! class_exists(StaticReflectionService::class)) {\n            self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version');\n        }\n\n        parent::setUp();\n    }\n\n    public function testIssue(): void\n    {\n        $classMetadata = new ClassMetadata(DDC3103ArticleId::class);\n\n        $this->createAttributeDriver()->loadMetadataForClass(DDC3103ArticleId::class, $classMetadata);\n\n        self::assertTrue(\n            $classMetadata->isEmbeddedClass,\n            'The isEmbeddedClass property should be true from the mapping data.',\n        );\n\n        self::assertTrue(\n            unserialize(serialize($classMetadata))->isEmbeddedClass,\n            'The isEmbeddedClass property should still be true after serialization and unserialization.',\n        );\n    }\n}\n\n#[Embeddable]\nclass DDC3103ArticleId\n{\n    #[Column(name: 'name', type: 'string', length: 255)]\n    protected string $nameValue;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3123Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Assert;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionProperty;\n\n#[Group('DDC-3123')]\nclass DDC3123Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testIssue(): void\n    {\n        $user = new CmsUser();\n        $uow  = $this->_em->getUnitOfWork();\n\n        $user->name     = 'Marco';\n        $user->username = 'ocramius';\n\n        $this->_em->persist($user);\n        $uow->scheduleExtraUpdate($user, ['name' => 'changed name']);\n\n        $this->_em->getEventManager()->addEventListener(Events::postFlush, new class ($uow) {\n            /** @var UnitOfWork */\n            private $uow;\n\n            public function __construct(UnitOfWork $uow)\n            {\n                $this->uow = $uow;\n            }\n\n            public function postFlush(): void\n            {\n                $property = new ReflectionProperty(UnitOfWork::class, 'extraUpdates');\n\n                Assert::assertEmpty(\n                    $property->getValue($this->uow),\n                    'ExtraUpdates are reset before postFlush',\n                );\n            }\n        });\n\n        $this->_em->flush();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3160Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Event\\OnFlushEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * FlushEventTest\n */\nclass DDC3160Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-3160')]\n    public function testNoUpdateOnInsert(): void\n    {\n        $listener = new DDC3160OnFlushListener();\n        $this->_em->getEventManager()->addEventListener(Events::onFlush, $listener);\n\n        $user           = new CmsUser();\n        $user->username = 'romanb';\n        $user->name     = 'Roman';\n        $user->status   = 'Dev';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->refresh($user);\n\n        self::assertEquals('romanc', $user->username);\n        self::assertEquals(1, $listener->inserts);\n        self::assertEquals(0, $listener->updates);\n    }\n}\n\nclass DDC3160OnFlushListener\n{\n    /** @var int */\n    public $inserts = 0;\n\n    /** @var int */\n    public $updates = 0;\n\n    public function onFlush(OnFlushEventArgs $args): void\n    {\n        $em  = $args->getObjectManager();\n        $uow = $em->getUnitOfWork();\n\n        foreach ($uow->getScheduledEntityInsertions() as $entity) {\n            $this->inserts++;\n            if ($entity instanceof CmsUser) {\n                $entity->username = 'romanc';\n                $cm               = $em->getClassMetadata($entity::class);\n                $uow->recomputeSingleEntityChangeSet($cm, $entity);\n            }\n        }\n\n        foreach ($uow->getScheduledEntityUpdates() as $entity) {\n            $this->updates++;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3170Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-2306')]\nclass DDC3170Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC3170AbstractEntityJoined::class,\n            DDC3170ProductJoined::class,\n            DDC3170AbstractEntitySingleTable::class,\n            DDC3170ProductSingleTable::class,\n        );\n    }\n\n    /**\n     * Tests that the discriminator column is correctly read from the meta mappings when fetching a\n     * child from an inheritance mapped class.\n     *\n     * The simple object hydration maps the type field to a field alias like type2. This mapping needs\n     * to be considered when loading the discriminator column's value from the SQL result.\n     *\n     * {@see \\Doctrine\\ORM\\Internal\\Hydration\\SimpleObjectHydrator::hydrateRowData()}\n     */\n    public function testIssue(): void\n    {\n        $productJoined      = new DDC3170ProductJoined();\n        $productSingleTable = new DDC3170ProductSingleTable();\n\n        $this->_em->persist($productJoined);\n        $this->_em->persist($productSingleTable);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQueryBuilder()\n                  ->select('p')\n                  ->from(DDC3170ProductJoined::class, 'p')\n                  ->getQuery()\n                  ->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT);\n\n        self::assertCount(1, $result);\n        self::assertContainsOnly(DDC3170ProductJoined::class, $result);\n\n        $result = $this->_em->createQueryBuilder()\n                  ->select('p')\n                  ->from(DDC3170ProductSingleTable::class, 'p')\n                  ->getQuery()\n                  ->getResult(AbstractQuery::HYDRATE_SIMPLEOBJECT);\n\n        self::assertCount(1, $result);\n        self::assertContainsOnly(DDC3170ProductSingleTable::class, $result);\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['product' => 'DDC3170ProductJoined'])]\nabstract class DDC3170AbstractEntityJoined\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC3170ProductJoined extends DDC3170AbstractEntityJoined\n{\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['product' => 'DDC3170ProductSingleTable'])]\nabstract class DDC3170AbstractEntitySingleTable\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC3170ProductSingleTable extends DDC3170AbstractEntitySingleTable\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3192Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_search;\n\n#[Group('DDC-2494')]\n#[Group('non-cacheable')]\nclass DDC3192Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (Type::hasType('ddc3192_currency_code')) {\n            self::fail(\n                'Type ddc3192_currency_code exists for testing DDC-3192 only, ' .\n                'but it has already been registered for some reason',\n            );\n        }\n\n        Type::addType('ddc3192_currency_code', DDC3192CurrencyCode::class);\n\n        $this->createSchemaForModels(\n            DDC3192Currency::class,\n            DDC3192Transaction::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $currency = new DDC3192Currency('BYR');\n\n        $this->_em->persist($currency);\n        $this->_em->flush();\n\n        $amount      = 50;\n        $transaction = new DDC3192Transaction($amount, $currency);\n\n        $this->_em->persist($transaction);\n        $this->_em->flush();\n        $this->_em->close();\n\n        $resultByPersister = $this->_em->find(DDC3192Transaction::class, $transaction->id);\n\n        // This works: DDC2494 makes persister set type mapping info to ResultSetMapping\n        self::assertEquals('BYR', $resultByPersister->currency->code);\n\n        $this->_em->close();\n\n        $query = $this->_em->createQuery();\n        $query->setDQL('SELECT t FROM ' . DDC3192Transaction::class . ' t WHERE t.id = ?1');\n        $query->setParameter(1, $transaction->id);\n\n        $resultByQuery = $query->getSingleResult();\n\n        // This is fixed here: before the fix it used to return 974.\n        // because unlike the BasicEntityPersister, SQLWalker doesn't set type info\n        self::assertEquals('BYR', $resultByQuery->currency->code);\n    }\n}\n\n#[Table(name: 'ddc3192_currency')]\n#[Entity]\nclass DDC3192Currency\n{\n    /** @var Collection<int, DDC3192Transaction> */\n    #[OneToMany(targetEntity: 'DDC3192Transaction', mappedBy: 'currency')]\n    public $transactions;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'ddc3192_currency_code')]\n        public string $code,\n    ) {\n    }\n}\n\n#[Table(name: 'ddc3192_transaction')]\n#[Entity]\nclass DDC3192Transaction\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'integer')]\n        public int $amount,\n        #[ManyToOne(targetEntity: 'DDC3192Currency', inversedBy: 'transactions')]\n        #[JoinColumn(name: 'currency_id', referencedColumnName: 'code', nullable: false)]\n        public DDC3192Currency $currency,\n    ) {\n    }\n}\n\nclass DDC3192CurrencyCode extends Type\n{\n    /** @phpstan-var array<string, int> */\n    private static array $map = ['BYR' => 974];\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getSmallIntTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): int\n    {\n        return self::$map[$value];\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): mixed\n    {\n        return array_search((int) $value, self::$map, true);\n    }\n\n    public function getName(): string\n    {\n        return 'ddc3192_currency_code';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3223Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\n/**\n * Functional tests for get Id after clone child entity\n */\nclass DDC3223Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                Journalist::class,\n                Participant::class,\n                Status::class,\n                ProfileStatus::class,\n            ],\n        );\n    }\n\n    public function testIssueGetId(): void\n    {\n        $profileStatus = new ProfileStatus();\n\n        $participant                = new Journalist();\n        $participant->profileStatus = $profileStatus;\n\n        $this->_em->persist($profileStatus);\n        $this->_em->persist($participant);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $participant = $this->_em->find(Participant::class, $participant->id);\n\n        $profileStatus = clone $participant->profileStatus;\n\n        self::assertSame(1, $profileStatus->getId(), 'The identifier on the cloned instance is an integer');\n    }\n}\n\n#[Table(name: 'ddc3223_journalist')]\n#[Entity]\nclass Journalist extends Participant\n{\n}\n\n#[Table(name: 'ddc3223_participant')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['journalist' => 'Journalist', 'participant' => 'Participant'])]\nclass Participant\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var ProfileStatus */\n    #[ManyToOne(targetEntity: 'ProfileStatus')]\n    public $profileStatus;\n}\n\n#[Table(name: 'ddc3223_status')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['profile' => 'ProfileStatus', 'status' => 'Status'])]\nclass Status\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Entity]\nclass ProfileStatus extends Status\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3300Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Tools\\ResolveTargetEntityListener;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-3300')]\nclass DDC3300Test extends OrmFunctionalTestCase\n{\n    public function testResolveTargetEntitiesChangesDiscriminatorMapValues(): void\n    {\n        $resolveTargetEntity = new ResolveTargetEntityListener();\n\n        $resolveTargetEntity->addResolveTargetEntity(\n            DDC3300Boss::class,\n            DDC3300HumanBoss::class,\n            [],\n        );\n\n        $resolveTargetEntity->addResolveTargetEntity(\n            DDC3300Employee::class,\n            DDC3300HumanEmployee::class,\n            [],\n        );\n\n        $this->_em->getEventManager()->addEventSubscriber($resolveTargetEntity);\n\n        $this->createSchemaForModels(DDC3300Person::class);\n\n        $boss     = new DDC3300HumanBoss('boss');\n        $employee = new DDC3300HumanEmployee('employee');\n\n        $this->_em->persist($boss);\n        $this->_em->persist($employee);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals($boss, $this->_em->find(DDC3300Boss::class, $boss->id));\n        self::assertEquals($employee, $this->_em->find(DDC3300Employee::class, $employee->id));\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['boss' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC3300Boss', 'employee' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC3300Employee'])]\nabstract class DDC3300Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\ninterface DDC3300Boss\n{\n}\n\n#[Entity]\nclass DDC3300HumanBoss extends DDC3300Person implements DDC3300Boss\n{\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $bossCol,\n    ) {\n    }\n}\n\ninterface DDC3300Employee\n{\n}\n\n#[Entity]\nclass DDC3300HumanEmployee extends DDC3300Person implements DDC3300Employee\n{\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $employeeCol,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3303Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC3303Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC3303Employee::class);\n    }\n\n    /**\n     * When using an embedded field in an inheritance, private properties should also be inherited.\n     */\n    #[Group('GH-4097')]\n    #[Group('GH-4277')]\n    #[Group('GH-5867')]\n    public function testEmbeddedObjectsAreAlsoInherited(): void\n    {\n        $employee = new DDC3303Employee(\n            'John Doe',\n            new DDC3303Address('Somewhere', 123, 'Over the rainbow'),\n            'Doctrine Inc',\n        );\n\n        $this->_em->persist($employee);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals($employee, $this->_em->find(DDC3303Employee::class, 'John Doe'));\n    }\n}\n\n#[MappedSuperclass]\nabstract class DDC3303Person\n{\n    public function __construct(\n        #[Id]\n        #[GeneratedValue(strategy: 'NONE')]\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n        #[Embedded(class: 'DDC3303Address')]\n        private DDC3303Address $address,\n    ) {\n    }\n}\n\n#[Embeddable]\nclass DDC3303Address\n{\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $street,\n        #[Column(type: 'integer')]\n        private int $number,\n        #[Column(type: 'string', length: 255)]\n        private string $city,\n    ) {\n    }\n}\n\n#[Table(name: 'ddc3303_employee')]\n#[Entity]\nclass DDC3303Employee extends DDC3303Person\n{\n    public function __construct(\n        string $name,\n        DDC3303Address $address,\n        #[Column(type: 'string', length: 255)]\n        private string $company,\n    ) {\n        parent::__construct($name, $address);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC331Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function strtolower;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy.\n */\nclass DDC331Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-331')]\n    public function testSelectFieldOnRootEntity(): void\n    {\n        $q = $this->_em->createQuery('SELECT e.name FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e');\n        self::assertEquals(\n            strtolower('SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id'),\n            strtolower($q->getSQL()),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3330Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function iterator_to_array;\n\n/**\n * Functional tests for paginator with collection order\n */\nclass DDC3330Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                DDC3330Building::class,\n                DDC3330Hall::class,\n            ],\n        );\n    }\n\n    public function testIssueCollectionOrderWithPaginator(): void\n    {\n        $this->createBuildingAndHalls();\n        $this->createBuildingAndHalls();\n        $this->createBuildingAndHalls();\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery(\n            'SELECT b, h' .\n            ' FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC3330Building b' .\n            ' LEFT JOIN b.halls h' .\n            ' ORDER BY b.id ASC, h.name DESC',\n        )\n        ->setMaxResults(3);\n\n        $paginator = new Paginator($query, true);\n\n        self::assertCount(3, iterator_to_array($paginator), 'Count is not correct for pagination');\n    }\n\n    /**\n     * Create a building and 10 halls\n     */\n    private function createBuildingAndHalls(): void\n    {\n        $building = new DDC3330Building();\n\n        for ($i = 0; $i < 10; $i++) {\n            $hall       = new DDC3330Hall();\n            $hall->name = 'HALL-' . $i;\n            $building->addHall($hall);\n        }\n\n        $this->_em->persist($building);\n        $this->_em->flush();\n    }\n}\n\n#[Table(name: 'ddc3330_building')]\n#[Entity]\nclass DDC3330Building\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC3330Hall> */\n    #[OneToMany(targetEntity: 'DDC3330Hall', mappedBy: 'building', cascade: ['persist'])]\n    public $halls;\n\n    public function addHall(DDC3330Hall $hall): void\n    {\n        $this->halls[]  = $hall;\n        $hall->building = $this;\n    }\n}\n\n#[Table(name: 'ddc3330_hall')]\n#[Entity]\nclass DDC3330Hall\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC3330Building */\n    #[ManyToOne(targetEntity: 'DDC3330Building', inversedBy: 'halls')]\n    public $building;\n\n    /** @var string */\n    #[Column(type: 'string', length: 100)]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3346Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\DDC3346\\DDC3346Article;\nuse Doctrine\\Tests\\Models\\DDC3346\\DDC3346Author;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('DDC-3346')]\nclass DDC3346Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('ddc3346');\n\n        parent::setUp();\n\n        $this->loadAuthorFixture();\n    }\n\n    public function testFindOneWithEagerFetchWillNotHydrateLimitedCollection(): void\n    {\n        $author = $this->_em->getRepository(DDC3346Author::class)->findOneBy(\n            ['username' => 'bwoogy'],\n        );\n        assert($author instanceof DDC3346Author);\n\n        self::assertCount(2, $author->articles);\n    }\n\n    public function testFindLimitedWithEagerFetchWillNotHydrateLimitedCollection(): void\n    {\n        /** @var DDC3346Author[] $authors */\n        $authors = $this->_em->getRepository(DDC3346Author::class)->findBy(\n            ['username' => 'bwoogy'],\n            null,\n            1,\n        );\n\n        self::assertCount(1, $authors);\n        self::assertCount(2, $authors[0]->articles);\n    }\n\n    public function testFindWithEagerFetchAndOffsetWillNotHydrateLimitedCollection(): void\n    {\n        /** @var DDC3346Author[] $authors */\n        $authors = $this->_em->getRepository(DDC3346Author::class)->findBy(\n            ['username' => 'bwoogy'],\n            null,\n            null,\n            0, // using an explicitly defined offset\n        );\n\n        self::assertCount(1, $authors);\n        self::assertCount(2, $authors[0]->articles);\n    }\n\n    private function loadAuthorFixture(): void\n    {\n        $user     = new DDC3346Author();\n        $article1 = new DDC3346Article();\n        $article2 = new DDC3346Article();\n\n        $user->username   = 'bwoogy';\n        $article1->user   = $user;\n        $article2->user   = $user;\n        $user->articles[] = $article1;\n        $user->articles[] = $article2;\n\n        $this->_em->persist($user);\n        $this->_em->persist($article1);\n        $this->_em->persist($article2);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC345Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\PrePersist;\nuse Doctrine\\ORM\\Mapping\\PreUpdate;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\UniqueConstraint;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC345Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC345User::class,\n            DDC345Group::class,\n            DDC345Membership::class,\n        );\n    }\n\n    public function testTwoIterateHydrations(): void\n    {\n        // Create User\n        $user       = new DDC345User();\n        $user->name = 'Test User';\n        $this->_em->persist($user); // $em->flush() does not change much here\n\n        // Create Group\n        $group       = new DDC345Group();\n        $group->name = 'Test Group';\n        $this->_em->persist($group); // $em->flush() does not change much here\n\n        $membership        = new DDC345Membership();\n        $membership->group = $group;\n        $membership->user  = $user;\n        $membership->state = 'active';\n\n        //$this->_em->persist($membership); // COMMENT OUT TO SEE BUG\n        /*\n        This should be not necessary, but without, its PrePersist is called twice,\n        $membership seems to be persisted twice, but all properties but the\n        ones set by LifecycleCallbacks are deleted.\n        */\n\n        $user->memberships->add($membership);\n        $group->memberships->add($membership);\n\n        $this->_em->flush();\n\n        self::assertEquals(1, $membership->prePersistCallCount);\n        self::assertEquals(0, $membership->preUpdateCallCount);\n        self::assertInstanceOf(DateTime::class, $membership->updated);\n    }\n}\n\n#[Entity]\nclass DDC345User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, DDC345Membership> */\n    #[OneToMany(targetEntity: 'DDC345Membership', mappedBy: 'user', cascade: ['persist'])]\n    public $memberships;\n\n    public function __construct()\n    {\n        $this->memberships = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC345Group\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, DDC345Membership> */\n    #[OneToMany(targetEntity: 'DDC345Membership', mappedBy: 'group', cascade: ['persist'])]\n    public $memberships;\n\n    public function __construct()\n    {\n        $this->memberships = new ArrayCollection();\n    }\n}\n\n#[Table(name: 'ddc345_memberships')]\n#[UniqueConstraint(name: 'ddc345_memship_fks', columns: ['user_id', 'group_id'])]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC345Membership\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC345User */\n    #[OneToOne(targetEntity: 'DDC345User', inversedBy: 'memberships')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)]\n    public $user;\n\n    /** @var DDC345Group */\n    #[OneToOne(targetEntity: 'DDC345Group', inversedBy: 'memberships')]\n    #[JoinColumn(name: 'group_id', referencedColumnName: 'id', nullable: false)]\n    public $group;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $state;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    public $updated;\n\n    /** @var int */\n    public $prePersistCallCount = 0;\n\n    /** @var int */\n    public $preUpdateCallCount = 0;\n\n    #[PrePersist]\n    public function doStuffOnPrePersist(): void\n    {\n        //echo \"***** PrePersist\\n\";\n        ++$this->prePersistCallCount;\n        $this->updated = new DateTime();\n    }\n\n    #[PreUpdate]\n    public function doStuffOnPreUpdate(): void\n    {\n        //echo \"***** PreUpdate\\n\";\n        ++$this->preUpdateCallCount;\n        $this->updated = new DateTime();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC353Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC353Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC353File::class, DDC353Picture::class);\n    }\n\n    public function testWorkingCase(): void\n    {\n        $file = new DDC353File();\n\n        $picture = new DDC353Picture();\n        $picture->setFile($file);\n\n        $em = $this->_em;\n        $em->persist($picture);\n        $em->flush();\n        $em->clear();\n\n        $fileId = $file->getFileId();\n        self::assertGreaterThan(0, $fileId);\n\n        $file = $em->getReference(DDC353File::class, $fileId);\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($file), 'Reference Proxy should be marked MANAGED.');\n\n        $picture = $em->find(DDC353Picture::class, $picture->getPictureId());\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), 'Lazy Proxy should be marked MANAGED.');\n\n        $em->remove($picture);\n        $em->flush();\n    }\n\n    public function testFailingCase(): void\n    {\n        $file = new DDC353File();\n\n        $picture = new DDC353Picture();\n        $picture->setFile($file);\n\n        $em = $this->_em;\n        $em->persist($picture);\n        $em->flush();\n        $em->clear();\n\n        $fileId    = $file->getFileId();\n        $pictureId = $picture->getPictureId();\n\n        self::assertGreaterThan(0, $fileId);\n\n        $picture = $em->find(DDC353Picture::class, $pictureId);\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), 'Lazy Proxy should be marked MANAGED.');\n\n        $em->remove($picture);\n        $em->flush();\n    }\n}\n\n#[Entity]\nclass DDC353Picture\n{\n    #[Column(name: 'picture_id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private int $pictureId;\n\n    #[JoinColumn(name: 'file_id', referencedColumnName: 'file_id')]\n    #[ManyToOne(targetEntity: 'DDC353File', cascade: ['persist', 'remove'])]\n    private DDC353File|null $file = null;\n\n    public function getPictureId(): int\n    {\n        return $this->pictureId;\n    }\n\n    public function setFile(DDC353File $value): void\n    {\n        $this->file = $value;\n    }\n\n    public function getFile(): DDC353File\n    {\n        return $this->file;\n    }\n}\n\n#[Entity]\nclass DDC353File\n{\n    /** @var int */\n    #[Column(name: 'file_id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $fileId;\n\n    /**\n     * Get fileId\n     */\n    public function getFileId(): int\n    {\n        return $this->fileId;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3582Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function assert;\n\nclass DDC3582Test extends OrmFunctionalTestCase\n{\n    public function testNestedEmbeddablesAreHydratedWithProperClass(): void\n    {\n        $this->createSchemaForModels(DDC3582Entity::class);\n        $this->_em->persist(new DDC3582Entity('foo'));\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity = $this->_em->find(DDC3582Entity::class, 'foo');\n        assert($entity instanceof DDC3582Entity);\n\n        self::assertInstanceOf(DDC3582Embeddable1::class, $entity->embeddable1);\n        self::assertInstanceOf(DDC3582Embeddable2::class, $entity->embeddable1->embeddable2);\n        self::assertInstanceOf(DDC3582Embeddable3::class, $entity->embeddable1->embeddable2->embeddable3);\n    }\n}\n\n#[Entity]\nclass DDC3582Entity\n{\n    /** @var DDC3582Embeddable1 */\n    #[Embedded(class: 'DDC3582Embeddable1')]\n    public $embeddable1;\n\n    public function __construct(\n        #[Column]\n        #[Id]\n        private string $id,\n    ) {\n        $this->embeddable1 = new DDC3582Embeddable1();\n    }\n}\n\n#[Embeddable]\nclass DDC3582Embeddable1\n{\n    /** @var DDC3582Embeddable2 */\n    #[Embedded(class: 'DDC3582Embeddable2')]\n    public $embeddable2;\n\n    public function __construct()\n    {\n        $this->embeddable2 = new DDC3582Embeddable2();\n    }\n}\n\n#[Embeddable]\nclass DDC3582Embeddable2\n{\n    /** @var DDC3582Embeddable3 */\n    #[Embedded(class: 'DDC3582Embeddable3')]\n    public $embeddable3;\n\n    public function __construct()\n    {\n        $this->embeddable3 = new DDC3582Embeddable3();\n    }\n}\n\n#[Embeddable]\nclass DDC3582Embeddable3\n{\n    /** @var string */\n    #[Column]\n    public $embeddedValue = 'foo';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3597Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\DDC3597\\DDC3597Image;\nuse Doctrine\\Tests\\Models\\DDC3597\\DDC3597Media;\nuse Doctrine\\Tests\\Models\\DDC3597\\DDC3597Root;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-117')]\nclass DDC3597Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC3597Root::class,\n            DDC3597Media::class,\n            DDC3597Image::class,\n        );\n    }\n\n    #[Group('DDC-3597')]\n    public function testSaveImageEntity(): void\n    {\n        $imageEntity = new DDC3597Image('foobar');\n        $imageEntity->setFormat('JPG');\n        $imageEntity->setSize(123);\n        $imageEntity->getDimension()->setWidth(300);\n        $imageEntity->getDimension()->setHeight(500);\n\n        $this->_em->persist($imageEntity);\n        $this->_em->flush(); //before this fix, it will fail with a exception\n\n        $this->_em->clear();\n\n        //request entity\n        $imageEntity = $this->_em->find(DDC3597Image::class, $imageEntity->getId());\n        self::assertInstanceOf(DDC3597Image::class, $imageEntity);\n\n        //cleanup\n        $this->_em->remove($imageEntity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        //check delete\n        $imageEntity = $this->_em->find(DDC3597Image::class, $imageEntity->getId());\n        self::assertNull($imageEntity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3634Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Configuration;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Driver\\Connection as DriverConnection;\nuse Doctrine\\DBAL\\Driver\\Middleware;\nuse Doctrine\\DBAL\\Driver\\Middleware\\AbstractConnectionMiddleware;\nuse Doctrine\\DBAL\\Driver\\Middleware\\AbstractDriverMiddleware;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Doctrine\\Tests\\TestUtil;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse const PHP_INT_MAX;\n\n#[Group('DDC-3634')]\nclass DDC3634Test extends OrmFunctionalTestCase\n{\n    private LastInsertIdMocker $idMocker;\n\n    protected function setUp(): void\n    {\n        $this->idMocker = new LastInsertIdMocker();\n        $config         = new Configuration();\n        $config->setMiddlewares([new LastInsertIdMockMiddleware($this->idMocker)]);\n\n        $this->_em         = $this->getEntityManager(TestUtil::getConnection($config));\n        $this->_schemaTool = new SchemaTool($this->_em);\n\n        parent::setUp();\n\n        $metadata = $this->_em->getClassMetadata(DDC3634Entity::class);\n\n        if (! $metadata->idGenerator->isPostInsertGenerator()) {\n            self::markTestSkipped('Need a post-insert ID generator in order to make this test work correctly');\n        }\n\n        $this->createSchemaForModels(\n            DDC3634Entity::class,\n            DDC3634JTIBaseEntity::class,\n            DDC3634JTIChildEntity::class,\n        );\n    }\n\n    public function testSavesVeryLargeIntegerAutoGeneratedValue(): void\n    {\n        $veryLargeId              = PHP_INT_MAX . PHP_INT_MAX;\n        $this->idMocker->mockedId = $veryLargeId;\n\n        $entity = new DDC3634Entity();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertSame($veryLargeId, $entity->id);\n    }\n\n    public function testSavesIntegerAutoGeneratedValue(): void\n    {\n        $entity = new DDC3634Entity();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertNotNull($entity->id);\n    }\n\n    public function testSavesIntegerAutoGeneratedValueWithJoinedInheritance(): void\n    {\n        $entity = new DDC3634JTIChildEntity();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertNotNull($entity->id);\n    }\n}\n\n#[Entity]\nclass DDC3634Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'bigint')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap([DDC3634JTIBaseEntity::class => DDC3634JTIBaseEntity::class, DDC3634JTIChildEntity::class => DDC3634JTIChildEntity::class])]\nclass DDC3634JTIBaseEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'bigint')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\nclass DDC3634JTIChildEntity extends DDC3634JTIBaseEntity\n{\n}\n\nclass LastInsertIdMocker\n{\n    public string|null $mockedId = null;\n}\n\nfinal class LastInsertIdMockConnection extends AbstractConnectionMiddleware\n{\n    public function __construct(DriverConnection $wrappedConnection, private LastInsertIdMocker $idMocker)\n    {\n        parent::__construct($wrappedConnection);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function lastInsertId($name = null): int|string\n    {\n        return $this->idMocker->mockedId ?? parent::lastInsertId($name);\n    }\n}\n\nfinal class LastInsertIdMockDriver extends AbstractDriverMiddleware\n{\n    public function __construct(Driver $wrappedDriver, private LastInsertIdMocker $idMocker)\n    {\n        parent::__construct($wrappedDriver);\n    }\n\n    public function connect(array $params): LastInsertIdMockConnection\n    {\n        return new LastInsertIdMockConnection(\n            parent::connect($params),\n            $this->idMocker,\n        );\n    }\n}\n\nfinal class LastInsertIdMockMiddleware implements Middleware\n{\n    public function __construct(private LastInsertIdMocker $idMocker)\n    {\n    }\n\n    public function wrap(Driver $driver): LastInsertIdMockDriver\n    {\n        return new LastInsertIdMockDriver($driver, $this->idMocker);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3644Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Functional tests for orphan removal with one to many association.\n */\nclass DDC3644Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                DDC3644User::class,\n                DDC3644Address::class,\n                DDC3644Animal::class,\n                DDC3644Pet::class,\n            ],\n        );\n    }\n\n    #[Group('DDC-3644')]\n    public function testIssueWithRegularEntity(): void\n    {\n        // Define initial dataset\n        $current   = new DDC3644Address('Sao Paulo, SP, Brazil');\n        $previous  = new DDC3644Address('Rio de Janeiro, RJ, Brazil');\n        $initial   = new DDC3644Address('Sao Carlos, SP, Brazil');\n        $addresses = new ArrayCollection([$current, $previous, $initial]);\n        $user      = new DDC3644User();\n\n        $user->name = 'Guilherme Blanco';\n        $user->setAddresses($addresses);\n\n        $this->_em->persist($user);\n        $this->_em->persist($current);\n        $this->_em->persist($previous);\n        $this->_em->persist($initial);\n\n        $this->_em->flush();\n\n        $userId = $user->id;\n        unset($current, $previous, $initial, $addresses, $user);\n\n        $this->_em->clear();\n\n        // Replace entire collection (this should trigger OneToManyPersister::remove())\n        $current   = new DDC3644Address('Toronto, ON, Canada');\n        $addresses = new ArrayCollection([$current]);\n        $user      = $this->_em->find(DDC3644User::class, $userId);\n\n        $user->setAddresses($addresses);\n\n        $this->_em->persist($user);\n        $this->_em->persist($current);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // We should only have 1 item in the collection list now\n        $user = $this->_em->find(DDC3644User::class, $userId);\n\n        self::assertCount(1, $user->addresses);\n\n        // We should only have 1 item in the addresses table too\n        $repository = $this->_em->getRepository(DDC3644Address::class);\n        $addresses  = $repository->findAll();\n\n        self::assertCount(1, $addresses);\n    }\n\n    #[Group('DDC-3644')]\n    public function testIssueWithJoinedEntity(): void\n    {\n        // Define initial dataset\n        $actual = new DDC3644Pet('Catharina');\n        $past   = new DDC3644Pet('Nanny');\n        $pets   = new ArrayCollection([$actual, $past]);\n        $user   = new DDC3644User();\n\n        $user->name = 'Guilherme Blanco';\n        $user->setPets($pets);\n\n        $this->_em->persist($user);\n        $this->_em->persist($actual);\n        $this->_em->persist($past);\n\n        $this->_em->flush();\n\n        $userId = $user->id;\n        unset($actual, $past, $pets, $user);\n\n        $this->_em->clear();\n\n        // Replace entire collection (this should trigger OneToManyPersister::remove())\n        $actual = new DDC3644Pet('Valentina');\n        $pets   = new ArrayCollection([$actual]);\n        $user   = $this->_em->find(DDC3644User::class, $userId);\n\n        $user->setPets($pets);\n\n        $this->_em->persist($user);\n        $this->_em->persist($actual);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // We should only have 1 item in the collection list now\n        $user = $this->_em->find(DDC3644User::class, $userId);\n\n        self::assertCount(1, $user->pets);\n\n        // We should only have 1 item in the pets table too\n        $repository = $this->_em->getRepository(DDC3644Pet::class);\n        $pets       = $repository->findAll();\n\n        self::assertCount(1, $pets);\n    }\n}\n\n#[Entity]\nclass DDC3644User\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer', name: 'hash_id')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, DDC3644Address> */\n    #[OneToMany(targetEntity: 'DDC3644Address', mappedBy: 'user', orphanRemoval: true)]\n    public $addresses = [];\n\n    /** @phpstan-var Collection<int, DDC3644Pet> */\n    #[OneToMany(targetEntity: 'DDC3644Pet', mappedBy: 'owner', orphanRemoval: true)]\n    public $pets = [];\n\n    public function setAddresses(Collection $addresses): void\n    {\n        $this->addresses = $addresses;\n\n        $addresses->map(function ($address): void {\n            $address->user = $this;\n        });\n    }\n\n    public function setPets(Collection $pets): void\n    {\n        $this->pets = $pets;\n\n        $pets->map(function ($pet): void {\n            $pet->owner = $this;\n        });\n    }\n}\n\n#[Entity]\nclass DDC3644Address\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC3644User */\n    #[ManyToOne(targetEntity: 'DDC3644User', inversedBy: 'addresses')]\n    #[JoinColumn(referencedColumnName: 'hash_id')]\n    public $user;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $address,\n    ) {\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discriminator', type: 'string')]\n#[DiscriminatorMap(['pet' => 'DDC3644Pet'])]\nabstract class DDC3644Animal\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC3644Pet extends DDC3644Animal\n{\n    /** @var DDC3644User */\n    #[ManyToOne(targetEntity: 'DDC3644User', inversedBy: 'pets')]\n    #[JoinColumn(referencedColumnName: 'hash_id')]\n    public $owner;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3719Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC3719Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-3719')]\n    public function testCriteriaOnNotOwningSide(): void\n    {\n        $manager = new CompanyManager();\n        $manager->setName('Gandalf');\n        $manager->setSalary(666);\n        $manager->setTitle('Boss');\n        $manager->setDepartment('Marketing');\n        $this->_em->persist($manager);\n\n        $contractA = new CompanyFlexContract();\n        $contractA->markCompleted();\n        $contractA->addManager($manager);\n        $this->_em->persist($contractA);\n\n        $contractB = new CompanyFlexContract();\n        $contractB->addManager($manager);\n        $this->_em->persist($contractB);\n\n        $this->_em->flush();\n        $this->_em->refresh($manager);\n\n        $contracts = $manager->managedContracts;\n        self::assertCount(2, $contracts);\n\n        $criteria = Criteria::create(true);\n        $criteria->where(Criteria::expr()->eq('completed', true));\n\n        $completedContracts = $contracts->matching($criteria);\n        self::assertCount(1, $completedContracts);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC371Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-371')]\nclass DDC371Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC371Parent::class, DDC371Child::class);\n    }\n\n    public function testIssue(): void\n    {\n        $parent           = new DDC371Parent();\n        $parent->data     = 'parent';\n        $parent->children = new ArrayCollection();\n\n        $child       = new DDC371Child();\n        $child->data = 'child';\n\n        $child->parent = $parent;\n        $parent->children->add($child);\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $children = $this->_em->createQuery('select c,p from ' . __NAMESPACE__ . '\\DDC371Child c '\n                . 'left join c.parent p where c.id = 1 and p.id = 1')\n                ->setHint(Query::HINT_REFRESH, true)\n                ->getResult();\n\n        self::assertCount(1, $children);\n        self::assertFalse($this->isUninitializedObject($children[0]->parent));\n        self::assertFalse($children[0]->parent->children->isInitialized());\n        self::assertEquals(0, $children[0]->parent->children->unwrap()->count());\n    }\n}\n\n#[Entity]\nclass DDC371Child\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n    /** @var DDC371Parent */\n    #[ManyToOne(targetEntity: 'DDC371Parent', inversedBy: 'children')]\n    #[JoinColumn(name: 'parentId')]\n    public $parent;\n}\n\n#[Entity]\nclass DDC371Parent\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n\n    /** @phpstan-var Collection<int, DDC371Child> */\n    #[OneToMany(targetEntity: 'DDC371Child', mappedBy: 'parent')]\n    public $children;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3785Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\nclass DDC3785Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType('ddc3785_asset_id', DDC3785AssetIdType::class);\n\n        $this->createSchemaForModels(\n            DDC3785Asset::class,\n            DDC3785AssetId::class,\n            DDC3785Attribute::class,\n        );\n    }\n\n    #[Group('DDC-3785')]\n    public function testOwningValueObjectIdIsCorrectlyTransformedWhenRemovingOrphanedChildEntities(): void\n    {\n        $id = new DDC3785AssetId('919609ba-57d9-4a13-be1d-d202521e858a');\n\n        $attributes = [\n            $attribute1 = new DDC3785Attribute('foo1', 'bar1'),\n            $attribute2 = new DDC3785Attribute('foo2', 'bar2'),\n        ];\n\n        $this->_em->persist($asset = new DDC3785Asset($id, $attributes));\n        $this->_em->flush();\n\n        $asset->getAttributes()\n              ->removeElement($attribute1);\n\n        $idToBeRemoved = $attribute1->id;\n\n        $this->_em->persist($asset);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(DDC3785Attribute::class, $idToBeRemoved));\n        self::assertNotNull($this->_em->find(DDC3785Attribute::class, $attribute2->id));\n    }\n}\n\n#[Table(name: 'asset')]\n#[Entity]\nclass DDC3785Asset\n{\n    /** @phpstan-var Collection<int, DDC3785Attribute> */\n    #[JoinTable(name: 'asset_attributes')]\n    #[JoinColumn(name: 'asset_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'attribute_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC3785Attribute', cascade: ['persist'], orphanRemoval: true)]\n    private $attributes;\n\n    /** @phpstan-param list<DDC3785Attribute> $attributes */\n    public function __construct(\n        #[Id]\n        #[GeneratedValue(strategy: 'NONE')]\n        #[Column(type: 'ddc3785_asset_id')]\n        private DDC3785AssetId $id,\n        $attributes = [],\n    ) {\n        $this->attributes = new ArrayCollection();\n\n        foreach ($attributes as $attribute) {\n            $this->attributes->add($attribute);\n        }\n    }\n\n    public function getId(): DDC3785AssetId\n    {\n        return $this->id;\n    }\n\n    /** @phpstan-return Collection<int, DDC3785Attribute> */\n    public function getAttributes()\n    {\n        return $this->attributes;\n    }\n}\n\n#[Table(name: 'attribute')]\n#[Entity]\nclass DDC3785Attribute\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n        #[Column(type: 'string', length: 255)]\n        private string $value,\n    ) {\n    }\n}\n\n#[Embeddable]\nclass DDC3785AssetId implements Stringable\n{\n    public function __construct(\n        #[Column(type: 'guid')]\n        private string $id,\n    ) {\n    }\n\n    public function __toString(): string\n    {\n        return $this->id;\n    }\n}\n\nclass DDC3785AssetIdType extends Type\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getGuidTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        return (string) $value;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): DDC3785AssetId\n    {\n        return new DDC3785AssetId($value);\n    }\n\n    public function getName(): string\n    {\n        return 'ddc3785_asset_id';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC381Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function serialize;\nuse function unserialize;\n\nclass DDC381Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC381Entity::class);\n    }\n\n    public function testCallUnserializedProxyMethods(): void\n    {\n        $entity = new DDC381Entity();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n        $persistedId = $entity->getId();\n\n        $entity = $this->_em->getReference(DDC381Entity::class, $persistedId);\n\n        // explicitly load proxy (getId() does not trigger reload of proxy)\n        $id = $entity->getOtherMethod();\n\n        $data   = serialize($entity);\n        $entity = unserialize($data);\n\n        self::assertEquals($persistedId, $entity->getId());\n    }\n}\n\n#[Entity]\nclass DDC381Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getOtherMethod(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC3967Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\ORM\\Functional\\SecondLevelCacheFunctionalTestCase;\n\nuse function array_pop;\nuse function assert;\n\nclass DDC3967Test extends SecondLevelCacheFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->loadFixturesCountries();\n        $this->_em->getCache()->evictEntityRegion(Country::class);\n        $this->_em->clear();\n    }\n\n    public function testIdentifierCachedWithProperType(): void\n    {\n        $country = array_pop($this->countries);\n        $id      = $country->getId();\n\n        // First time, loaded from database\n        $this->_em->find(Country::class, (string) $id);\n        $this->_em->clear();\n\n        $country = $this->_em->find(Country::class, (string) $id);\n        assert($country instanceof Country);\n\n        // Identifier type should be integer\n        self::assertSame($country->getId(), $id);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC4003Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Cache\\Bar;\nuse Doctrine\\Tests\\ORM\\Functional\\SecondLevelCacheFunctionalTestCase;\n\nuse function assert;\nuse function uniqid;\n\nclass DDC4003Test extends SecondLevelCacheFunctionalTestCase\n{\n    public function testReadsThroughRepositorySameDataThatItWroteInCache(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        // Get the id of the first bar\n        $id = $this->attractions[0]->getId();\n\n        $repository = $this->_em->getRepository(Bar::class);\n\n        /**\n         * This instance is fresh new, no QueryCache, so the full entity gets loaded from DB.\n         * It will be saved in the WRONG KEY (notice the cache.bar at the end):\n         * doctrine_tests_models_cache_attraction[doctrine_tests_models_cache_attraction_doctrine.tests.models.cache.bar_1][1]\n         */\n        $bar = $repository->findOneBy(['id' => $id]);\n        assert($bar instanceof Bar);\n\n        // Let's change it so that we can compare its state\n        $bar->setName($newName = uniqid());\n        $this->_em->persist($bar);\n        $this->_em->flush();\n\n        /**\n         * Flush did 2 important things for us:\n         * 1: saved the entity in its right key (notice the cache.attraction at the end):\n         *     doctrine_tests_models_cache_attraction[doctrine_tests_models_cache_attraction_doctrine.tests.models.cache.attraction_1][1]\n         * 2: Updated the TimestampRegion cache, so that the QueryCache is actually discarded.\n         *\n         * So, first findOneBy will hit DB, and its entity will not be loaded from cache.\n         */\n        $repository->findOneBy(['id' => $id]);\n\n        // Lets clear EM so that we don't hit IdentityMap at all.\n        $this->_em->clear();\n\n        /**\n         * Here's the failing step:\n         * Right now QueryCache will HIT, as nothing changed between the last one and now.\n         * QueryCache holds a reference to the WRONG KEY, as we saw was formed in line 24 of this test.\n         * So this instance won't be updated and will have the original name (\"Boteco São Bento\"), and not the uniqid().\n         */\n        $cached = $repository->findOneBy(['id' => $id]);\n        assert($cached instanceof Bar);\n\n        self::assertEquals($newName, $cached->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC4024Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\NonUniqueResultException;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\n#[Group('DDC4024')]\nfinal class DDC4024Test extends TestCase\n{\n    public function testConstructorShouldUseProvidedMessage(): void\n    {\n        $exception = new NonUniqueResultException('Testing');\n\n        self::assertSame('Testing', $exception->getMessage());\n    }\n\n    public function testADefaultMessageShouldBeUsedWhenNothingWasProvided(): void\n    {\n        $exception = new NonUniqueResultException();\n\n        self::assertSame(NonUniqueResultException::DEFAULT_MESSAGE, $exception->getMessage());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC422Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC422Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC422Guest::class,\n            DDC422Customer::class,\n            DDC422Contact::class,\n        );\n    }\n\n    #[Group('DDC-422')]\n    public function testIssue(): void\n    {\n        $customer = new DDC422Customer();\n        $this->_em->persist($customer);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $customer = $this->_em->find($customer::class, $customer->id);\n\n        self::assertInstanceOf(PersistentCollection::class, $customer->contacts);\n        self::assertFalse($customer->contacts->isInitialized());\n        $contact = new DDC422Contact();\n        $customer->contacts->add($contact);\n        self::assertTrue($customer->contacts->isDirty());\n        self::assertFalse($customer->contacts->isInitialized());\n        $this->_em->flush();\n\n        self::assertEquals(1, $this->_em->getConnection()->fetchOne('select count(*) from ddc422_customers_contacts'));\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['guest' => 'DDC422Guest', 'customer' => 'DDC422Customer'])]\nclass DDC422Guest\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC422Customer extends DDC422Guest\n{\n    /** @var Collection<int, DDC422Contact> */\n    #[JoinTable(name: 'ddc422_customers_contacts')]\n    #[JoinColumn(name: 'customer_id', referencedColumnName: 'id', onDelete: 'cascade')]\n    #[InverseJoinColumn(name: 'contact_id', referencedColumnName: 'id', onDelete: 'cascade')]\n    #[ManyToMany(targetEntity: 'DDC422Contact', cascade: ['persist', 'remove'])]\n    public $contacts;\n\n    public function __construct()\n    {\n        $this->contacts = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass DDC422Contact\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC425Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC425Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC425Entity::class);\n    }\n\n    #[Group('DDC-425')]\n    public function testIssue(): void\n    {\n        $num = $this->_em->createQuery('DELETE ' . __NAMESPACE__ . '\\DDC425Entity e WHERE e.someDatetimeField > ?1')\n                ->setParameter(1, new DateTime(), Types::DATETIME_MUTABLE)\n                ->getResult();\n        self::assertEquals(0, $num);\n    }\n}\n\n#[Entity]\nclass DDC425Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    public $someDatetimeField;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC440Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC440Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC440Phone::class, DDC440Client::class);\n    }\n\n    #[Group('DDC-440')]\n    public function testOriginalEntityDataEmptyWhenProxyLoadedFromTwoAssociations(): void\n    {\n        /* The key of the problem is that the first phone is fetched via two association, mainPhone and phones.\n         *\n         * You will notice that the original_entity_datas are not loaded for the first phone. (They are for the second)\n         *\n         * In the Client entity definition, if you define the mainPhone relation after the phones relation, both assertions pass.\n         * (for the sake or this test, I defined the mainPhone relation before the phones relation)\n         *\n         */\n\n        //Initialize some data\n        $client = new DDC440Client();\n        $client->setName('Client1');\n\n        $phone = new DDC440Phone();\n        $phone->setId(1);\n        $phone->setNumber('418 111-1111');\n        $phone->setClient($client);\n\n        $phone2 = new DDC440Phone();\n        $phone2->setId(2);\n        $phone2->setNumber('418 222-2222');\n        $phone2->setClient($client);\n\n        $client->setMainPhone($phone);\n\n        $this->_em->persist($client);\n        $this->_em->flush();\n        $id = $client->getId();\n        $this->_em->clear();\n\n        $uw           = $this->_em->getUnitOfWork();\n        $client       = $this->_em->find(DDC440Client::class, $id);\n        $clientPhones = $client->getPhones();\n\n        $p1 = $clientPhones[1];\n        $p2 = $clientPhones[2];\n\n        // Test the first phone.  The assertion actually failed because original entity data is not set properly.\n        // This was because it is also set as MainPhone and that one is created as a proxy, not the\n        // original object when the find on Client is called. However loading proxies did not work correctly.\n        self::assertInstanceOf(DDC440Phone::class, $p1);\n        $originalData = $uw->getOriginalEntityData($p1);\n        self::assertEquals($phone->getNumber(), $originalData['number']);\n\n        //If you comment out previous test, this one should pass\n        self::assertInstanceOf(DDC440Phone::class, $p2);\n        $originalData = $uw->getOriginalEntityData($p2);\n        self::assertEquals($phone2->getNumber(), $originalData['number']);\n    }\n}\n\n#[Table(name: 'phone')]\n#[Entity]\nclass DDC440Phone\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var DDC440Client */\n    #[JoinColumn(name: 'client_id', referencedColumnName: 'id')]\n    #[ManyToOne(targetEntity: 'DDC440Client', inversedBy: 'phones')]\n    protected $client;\n\n    /** @var string */\n    #[Column(name: 'phonenumber', type: 'string', length: 255)]\n    protected $number;\n\n    public function setNumber(string $value): void\n    {\n        $this->number = $value;\n    }\n\n    public function getNumber(): string\n    {\n        return $this->number;\n    }\n\n    public function setClient(DDC440Client $value, bool $updateInverse = true): void\n    {\n        $this->client = $value;\n        if ($updateInverse) {\n            $value->addPhone($this);\n        }\n    }\n\n    public function getClient(): DDC440Client\n    {\n        return $this->client;\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $value): void\n    {\n        $this->id = $value;\n    }\n}\n\n#[Table(name: 'client')]\n#[Entity]\nclass DDC440Client\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id;\n\n    /** @var DDC440Phone */\n    #[JoinColumn(name: 'main_phone_id', referencedColumnName: 'id', onDelete: 'SET NULL')]\n    #[OneToOne(targetEntity: 'DDC440Phone', fetch: 'EAGER')]\n    protected $mainPhone;\n\n    /** @phpstan-var Collection<int, DDC440Phone> */\n    #[OneToMany(targetEntity: 'DDC440Phone', mappedBy: 'client', cascade: ['persist', 'remove'], fetch: 'EAGER', indexBy: 'id')]\n    #[OrderBy(['number' => 'ASC'])]\n    protected $phones;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255)]\n    protected $name;\n\n    public function __construct()\n    {\n    }\n\n    public function setName(string $value): void\n    {\n        $this->name = $value;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function addPhone(DDC440Phone $value): void\n    {\n        $this->phones[] = $value;\n        $value->setClient($this, false);\n    }\n\n    /** @phpstan-return Collection<int, DDC440Phone> */\n    public function getPhones(): Collection\n    {\n        return $this->phones;\n    }\n\n    public function setMainPhone(DDC440Phone $value): void\n    {\n        $this->mainPhone = $value;\n    }\n\n    public function getMainPhone(): DDC440Phone\n    {\n        return $this->mainPhone;\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId($value): void\n    {\n        $this->id = $value;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC444Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function sprintf;\n\nclass DDC444Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC444User::class);\n    }\n\n    public function testExplicitPolicy(): void\n    {\n        $classname = DDC444User::class;\n\n        $u       = new $classname();\n        $u->name = 'Initial value';\n\n        $this->_em->persist($u);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q = $this->_em->createQuery(sprintf('SELECT u FROM %s u', $classname));\n        $u = $q->getSingleResult();\n        self::assertEquals('Initial value', $u->name);\n\n        $u->name = 'Modified value';\n\n        // This should be NOOP as the change hasn't been persisted\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $u = $this->_em->createQuery(sprintf('SELECT u FROM %s u', $classname));\n        $u = $q->getSingleResult();\n\n        self::assertEquals('Initial value', $u->name);\n\n        $u->name = 'Modified value';\n        $this->_em->persist($u);\n        // Now we however persisted it, and this should have updated our friend\n        $this->_em->flush();\n\n        $q = $this->_em->createQuery(sprintf('SELECT u FROM %s u', $classname));\n        $u = $q->getSingleResult();\n\n        self::assertEquals('Modified value', $u->name);\n    }\n}\n\n\n#[Table(name: 'ddc444')]\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass DDC444User\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255)]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC448Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\HasLifecycleCallbacks;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strtolower;\n\nclass DDC448Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC448MainTable::class,\n            DDC448ConnectedClass::class,\n            DDC448SubTable::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $q = $this->_em->createQuery('select b from ' . __NAMESPACE__ . '\\\\DDC448SubTable b where b.connectedClassId = ?1');\n        self::assertEquals(\n            strtolower('SELECT d0_.id AS id_0, d0_.discr AS discr_1, d0_.connectedClassId AS connectedClassId_2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?'),\n            strtolower($q->getSQL()),\n        );\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'smallint')]\n#[DiscriminatorMap(['0' => 'DDC448MainTable', '1' => 'DDC448SubTable'])]\nclass DDC448MainTable\n{\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'DDC448ConnectedClass', cascade: ['all'], fetch: 'EAGER')]\n    #[JoinColumn(name: 'connectedClassId', referencedColumnName: 'id', onDelete: 'CASCADE', nullable: true)]\n    private DDC448ConnectedClass $connectedClassId;\n}\n\n#[Table(name: 'connectedClass')]\n#[Entity]\n#[HasLifecycleCallbacks]\nclass DDC448ConnectedClass\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $id; // connected with DDC448MainTable\n}\n\n#[Table(name: 'SubTable')]\n#[Entity]\nclass DDC448SubTable extends DDC448MainTable\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC493Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strtolower;\n\nclass DDC493Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC493Customer::class,\n            DDC493Distributor::class,\n            DDC493Contact::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $q = $this->_em->createQuery('select u, c.data from ' . __NAMESPACE__ . '\\\\DDC493Distributor u JOIN u.contact c');\n        self::assertEquals(\n            strtolower('SELECT d0_.id AS id_0, d1_.data AS data_1, d0_.discr AS discr_2, d0_.contact AS contact_3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id'),\n            strtolower($q->getSQL()),\n        );\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['distributor' => 'DDC493Distributor', 'customer' => 'DDC493Customer'])]\nclass DDC493Customer\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n    /** @var DDC493Contact */\n    #[OneToOne(targetEntity: 'DDC493Contact', cascade: ['remove', 'persist'])]\n    #[JoinColumn(name: 'contact', referencedColumnName: 'id')]\n    public $contact;\n}\n\n#[Entity]\nclass DDC493Distributor extends DDC493Customer\n{\n}\n\n#[Entity]\nclass DDC493Contact\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC512Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC512Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC512Customer::class,\n            DDC512OfferItem::class,\n            DDC512Item::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $customer1       = new DDC512Customer();\n        $item            = new DDC512OfferItem();\n        $customer1->item = $item;\n        $this->_em->persist($customer1);\n\n        $customer2 = new DDC512Customer();\n        $this->_em->persist($customer2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $q      = $this->_em->createQuery('select u,i from ' . __NAMESPACE__ . '\\\\DDC512Customer u left join u.item i');\n        $result = $q->getResult();\n\n        self::assertCount(2, $result);\n        self::assertInstanceOf(DDC512Customer::class, $result[0]);\n        self::assertInstanceOf(DDC512Customer::class, $result[1]);\n        if ($result[0]->id === $customer1->id) {\n            self::assertInstanceOf(DDC512OfferItem::class, $result[0]->item);\n            self::assertEquals($item->id, $result[0]->item->id);\n            self::assertNull($result[1]->item);\n        } else {\n            self::assertInstanceOf(DDC512OfferItem::class, $result[1]->item);\n            self::assertNull($result[0]->item);\n        }\n    }\n}\n\n#[Entity]\nclass DDC512Customer\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /**\n     * NOTE that we can currently not name the join column the same as the field\n     * (item = item), this currently confuses Doctrine.\n     *\n     * @var DDC512OfferItem\n     */\n    #[OneToOne(targetEntity: 'DDC512OfferItem', cascade: ['remove', 'persist'])]\n    #[JoinColumn(name: 'item_id', referencedColumnName: 'id')]\n    public $item;\n}\n\n#[Entity]\nclass DDC512OfferItem extends DDC512Item\n{\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['item' => 'DDC512Item', 'offerItem' => 'DDC512OfferItem'])]\nclass DDC512Item\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC513Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strtolower;\n\nclass DDC513Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC513OfferItem::class,\n            DDC513Item::class,\n            DDC513Price::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $q = $this->_em->createQuery('select u from ' . __NAMESPACE__ . '\\\\DDC513OfferItem u left join u.price p');\n        self::assertEquals(\n            strtolower('SELECT d0_.id AS id_0, d0_.discr AS discr_1, d0_.price AS price_2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id'),\n            strtolower($q->getSQL()),\n        );\n    }\n}\n\n#[Entity]\nclass DDC513OfferItem extends DDC513Item\n{\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['item' => 'DDC513Item', 'offerItem' => 'DDC513OfferItem'])]\nclass DDC513Item\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC513Price */\n    #[OneToOne(targetEntity: 'DDC513Price', cascade: ['remove', 'persist'])]\n    #[JoinColumn(name: 'price', referencedColumnName: 'id')]\n    public $price;\n}\n\n#[Entity]\nclass DDC513Price\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $data;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC522Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests that join columns (foreign keys) can be named the same as the association\n * fields they're used on without causing issues.\n */\nclass DDC522Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC522Customer::class,\n            DDC522Cart::class,\n            DDC522ForeignKeyTest::class,\n        );\n    }\n\n    #[Group('DDC-522')]\n    public function testJoinColumnWithSameNameAsAssociationField(): void\n    {\n        $cust           = new DDC522Customer();\n        $cust->name     = 'name';\n        $cart           = new DDC522Cart();\n        $cart->total    = 0;\n        $cust->cart     = $cart;\n        $cart->customer = $cust;\n        $this->_em->persist($cust);\n        $this->_em->persist($cart);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $r = $this->_em->createQuery('select ca,c from ' . DDC522Cart::class . ' ca join ca.customer c')\n                       ->getResult();\n\n        self::assertInstanceOf(DDC522Cart::class, $r[0]);\n        self::assertInstanceOf(DDC522Customer::class, $r[0]->customer);\n        self::assertFalse($this->isUninitializedObject($r[0]->customer));\n        self::assertEquals('name', $r[0]->customer->name);\n\n        $fkt         = new DDC522ForeignKeyTest();\n        $fkt->cartId = $r[0]->id; // ignored for persistence\n        $fkt->cart   = $r[0]; // must be set properly\n        $this->_em->persist($fkt);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $fkt2 = $this->_em->find($fkt::class, $fkt->id);\n        self::assertEquals($fkt->cart->id, $fkt2->cartId);\n        self::assertTrue($this->isUninitializedObject($fkt2->cart));\n    }\n\n    #[Group('DDC-522')]\n    #[Group('DDC-762')]\n    public function testJoinColumnWithNullSameNameAssociationField(): void\n    {\n        $fkCust       = new DDC522ForeignKeyTest();\n        $fkCust->cart = null;\n\n        $this->_em->persist($fkCust);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $expected = clone $fkCust;\n        // removing dynamic field (which is not persisted)\n        unset($expected->name);\n\n        self::assertEquals($expected, $this->_em->find(DDC522ForeignKeyTest::class, $fkCust->id));\n    }\n}\n\n#[Entity]\nclass DDC522Customer\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var mixed */\n    #[Column]\n    public $name;\n\n    /** @var DDC522Cart */\n    #[OneToOne(targetEntity: 'DDC522Cart', mappedBy: 'customer')]\n    public $cart;\n}\n\n#[Entity]\nclass DDC522Cart\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $total;\n\n    /** @var DDC522Customer */\n    #[OneToOne(targetEntity: 'DDC522Customer', inversedBy: 'cart')]\n    #[JoinColumn(name: 'customer', referencedColumnName: 'id')]\n    public $customer;\n}\n\n#[Entity]\nclass DDC522ForeignKeyTest\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int|null */\n    #[Column(type: 'integer', name: 'cart_id', nullable: true)]\n    public $cartId;\n\n    /** @var DDC522Cart|null */\n    #[OneToOne(targetEntity: 'DDC522Cart')]\n    #[JoinColumn(name: 'cart_id', referencedColumnName: 'id')]\n    public $cart;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC531Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC531Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC531Item::class,\n            DDC531SubItem::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $item1         = new DDC531Item();\n        $item2         = new DDC531Item();\n        $item2->parent = $item1;\n        $item1->getChildren()->add($item2);\n        $this->_em->persist($item1);\n        $this->_em->persist($item2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $item3 = $this->_em->find(DDC531Item::class, $item2->id); // Load child item first (id 2)\n        // parent will already be loaded, cannot be lazy because it has mapped subclasses and we would not\n        // know which proxy type to put in.\n        self::assertInstanceOf(DDC531Item::class, $item3->parent);\n        self::assertFalse($this->isUninitializedObject($item3->parent));\n        $item4 = $this->_em->find(DDC531Item::class, $item1->id); // Load parent item (id 1)\n        self::assertNull($item4->parent);\n        self::assertNotNull($item4->getChildren());\n        self::assertTrue($item4->getChildren()->contains($item3)); // lazy-loads children\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'integer')]\n#[DiscriminatorMap(['0' => 'DDC531Item', '1' => 'DDC531SubItem'])]\nclass DDC531Item\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC531Item> */\n    #[OneToMany(targetEntity: 'DDC531Item', mappedBy: 'parent')]\n    protected $children;\n\n    /** @var DDC531Item */\n    #[ManyToOne(targetEntity: 'DDC531Item', inversedBy: 'children')]\n    #[JoinColumn(name: 'parentId', referencedColumnName: 'id')]\n    public $parent;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n\n    public function getParent(): DDC531Item\n    {\n        return $this->parent;\n    }\n\n    /** @phpstan-return Collection<int, DDC531Item> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n}\n\n#[Entity]\nclass DDC531SubItem extends DDC531Item\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC5684Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types as DBALTypes;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\n/**\n * This test verifies that custom post-insert identifiers respect type conversion semantics.\n * The generated identifier must be converted via DBAL types before populating the entity\n * identifier field.\n */\n#[Group('5935')]\n#[Group('5684')]\n#[Group('6020')]\n#[Group('6152')]\nclass DDC5684Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (DBALTypes\\Type::hasType(DDC5684ObjectIdType::class)) {\n            DBALTypes\\Type::overrideType(DDC5684ObjectIdType::class, DDC5684ObjectIdType::class);\n        } else {\n            DBALTypes\\Type::addType(DDC5684ObjectIdType::class, DDC5684ObjectIdType::class);\n        }\n\n        $this->createSchemaForModels(DDC5684Object::class);\n    }\n\n    public function testAutoIncrementIdWithCustomType(): void\n    {\n        $object = new DDC5684Object();\n        $this->_em->persist($object);\n        $this->_em->flush();\n\n        self::assertInstanceOf(DDC5684ObjectId::class, $object->id);\n    }\n\n    public function testFetchObjectWithAutoIncrementedCustomType(): void\n    {\n        $object = new DDC5684Object();\n        $this->_em->persist($object);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $rawId  = $object->id->value;\n        $object = $this->_em->find(DDC5684Object::class, new DDC5684ObjectId($rawId));\n\n        self::assertInstanceOf(DDC5684ObjectId::class, $object->id);\n        self::assertEquals($rawId, $object->id->value);\n    }\n}\n\nclass DDC5684ObjectIdType extends DBALTypes\\Type\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): DDC5684ObjectId\n    {\n        return new DDC5684ObjectId($value);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed\n    {\n        return $value->value;\n    }\n\n    public function getName(): string\n    {\n        return self::class;\n    }\n\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getIntegerTypeDeclarationSQL($column);\n    }\n}\n\nclass DDC5684ObjectId implements Stringable\n{\n    public function __construct(public mixed $value)\n    {\n    }\n\n    public function __toString(): string\n    {\n        return (string) $this->value;\n    }\n}\n\n#[Table(name: 'ticket_5684_objects')]\n#[Entity]\nclass DDC5684Object\n{\n    /** @var DDC5684ObjectIdType */\n    #[Id]\n    #[Column(type: DDC5684ObjectIdType::class)]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC588Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\n\nclass DDC588Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC588Site::class);\n    }\n\n    #[DoesNotPerformAssertions]\n    public function testIssue(): void\n    {\n        $site = new DDC588Site('Foo');\n\n        $this->_em->persist($site);\n        $this->_em->flush();\n        // Following should not result in exception\n        $this->_em->refresh($site);\n    }\n}\n\n#[Entity]\nclass DDC588Site\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer', name: 'site_id')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 45)]\n        protected string $name = '',\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC599Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC599Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC599Item::class,\n            DDC599Subitem::class,\n            DDC599Child::class,\n        );\n    }\n\n    public function testCascadeRemoveOnInheritanceHierarchy(): void\n    {\n        $item          = new DDC599Subitem();\n        $item->elem    = 'foo';\n        $child         = new DDC599Child();\n        $child->parent = $item;\n        $item->getChildren()->add($child);\n        $this->_em->persist($item);\n        $this->_em->persist($child);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $item = $this->_em->find(DDC599Item::class, $item->id);\n\n        $this->_em->remove($item);\n        $this->_em->flush(); // Should not fail\n\n        self::assertFalse($this->_em->contains($item));\n        $children = $item->getChildren();\n        self::assertFalse($this->_em->contains($children[0]));\n\n        $this->_em->clear();\n\n        $item2       = new DDC599Subitem();\n        $item2->elem = 'bar';\n        $this->_em->persist($item2);\n        $this->_em->flush();\n\n        $child2         = new DDC599Child();\n        $child2->parent = $item2;\n        $item2->getChildren()->add($child2);\n        $this->_em->persist($child2);\n        $this->_em->flush();\n\n        $this->_em->remove($item2);\n        $this->_em->flush(); // should not fail\n\n        self::assertFalse($this->_em->contains($item));\n        $children = $item->getChildren();\n        self::assertFalse($this->_em->contains($children[0]));\n    }\n\n    public function testCascadeRemoveOnChildren(): void\n    {\n        $class = $this->_em->getClassMetadata(DDC599Subitem::class);\n\n        self::assertArrayHasKey('children', $class->associationMappings);\n        self::assertTrue($class->associationMappings['children']->isCascadeRemove());\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'integer')]\n#[DiscriminatorMap(['0' => 'DDC599Item', '1' => 'DDC599Subitem'])]\nclass DDC599Item\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @phpstan-var Collection<int, DDC599Child> */\n    #[OneToMany(targetEntity: 'DDC599Child', mappedBy: 'parent', cascade: ['remove'])]\n    protected $children;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n\n    /** @phpstan-return Collection<int, DDC599Child> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n}\n\n#[Entity]\nclass DDC599Subitem extends DDC599Item\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $elem;\n}\n\n#[Entity]\nclass DDC599Child\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var DDC599Item */\n    #[ManyToOne(targetEntity: 'DDC599Item', inversedBy: 'children')]\n    #[JoinColumn(name: 'parentId', referencedColumnName: 'id')]\n    public $parent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC618Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-618')]\nclass DDC618Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC618Author::class, DDC618Book::class);\n\n        try {\n            // Create author 10/Joe with two books 22/JoeA and 20/JoeB\n            $author       = new DDC618Author();\n            $author->id   = 10;\n            $author->name = 'Joe';\n            $this->_em->persist($author);\n\n            // Create author 11/Alice with two books 21/AliceA and 23/AliceB\n            $author       = new DDC618Author();\n            $author->id   = 11;\n            $author->name = 'Alice';\n            $author->addBook('In Wonderland');\n            $author->addBook('Reloaded');\n            $author->addBook('Test');\n\n            $this->_em->persist($author);\n\n            $this->_em->flush();\n            $this->_em->clear();\n        } catch (UniqueConstraintViolationException) {\n        }\n    }\n\n    public function testIndexByHydrateObject(): void\n    {\n        $dql    = 'SELECT A FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Author A INDEX BY A.name ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_OBJECT);\n\n        $joe   = $this->_em->find(DDC618Author::class, 10);\n        $alice = $this->_em->find(DDC618Author::class, 11);\n\n        self::assertArrayHasKey('Joe', $result, \"INDEX BY A.name should return an index by the name of 'Joe'.\");\n        self::assertArrayHasKey('Alice', $result, \"INDEX BY A.name should return an index by the name of 'Alice'.\");\n    }\n\n    public function testIndexByHydrateArray(): void\n    {\n        $dql    = 'SELECT A FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Author A INDEX BY A.name ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_ARRAY);\n\n        $joe   = $this->_em->find(DDC618Author::class, 10);\n        $alice = $this->_em->find(DDC618Author::class, 11);\n\n        self::assertArrayHasKey('Joe', $result, \"INDEX BY A.name should return an index by the name of 'Joe'.\");\n        self::assertArrayHasKey('Alice', $result, \"INDEX BY A.name should return an index by the name of 'Alice'.\");\n    }\n\n    #[Group('DDC-1018')]\n    public function testIndexByJoin(): void\n    {\n        $dql    = 'SELECT A, B FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Author A ' .\n               'INNER JOIN A.books B INDEX BY B.title ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_OBJECT);\n\n        self::assertCount(3, $result[0]->books); // Alice, Joe doesn't appear because he has no books.\n        self::assertEquals('Alice', $result[0]->name);\n        self::assertTrue(isset($result[0]->books['In Wonderland']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[0]->books['Reloaded']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[0]->books['Test']), 'Indexing by title should have books by title.');\n\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_ARRAY);\n\n        self::assertCount(3, $result[0]['books']); // Alice, Joe doesn't appear because he has no books.\n        self::assertEquals('Alice', $result[0]['name']);\n        self::assertTrue(isset($result[0]['books']['In Wonderland']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[0]['books']['Reloaded']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[0]['books']['Test']), 'Indexing by title should have books by title.');\n    }\n\n    #[Group('DDC-1018')]\n    public function testIndexByToOneJoinSilentlyIgnored(): void\n    {\n        $dql    = 'SELECT B, A FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Book B ' .\n               'INNER JOIN B.author A INDEX BY A.name ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_OBJECT);\n\n        self::assertInstanceOf(DDC618Book::class, $result[0]);\n        self::assertInstanceOf(DDC618Author::class, $result[0]->author);\n\n        $dql    = 'SELECT B, A FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Book B ' .\n               'INNER JOIN B.author A INDEX BY A.name ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_ARRAY);\n\n        self::assertEquals('Alice', $result[0]['author']['name']);\n    }\n\n    #[Group('DDC-1018')]\n    public function testCombineIndexBy(): void\n    {\n        $dql    = 'SELECT A, B FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC618Author A INDEX BY A.id ' .\n               'INNER JOIN A.books B INDEX BY B.title ORDER BY A.name ASC';\n        $result = $this->_em->createQuery($dql)->getResult(Query::HYDRATE_OBJECT);\n\n        self::assertArrayHasKey(11, $result); // Alice\n\n        self::assertCount(3, $result[11]->books); // Alice, Joe doesn't appear because he has no books.\n        self::assertEquals('Alice', $result[11]->name);\n        self::assertTrue(isset($result[11]->books['In Wonderland']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[11]->books['Reloaded']), 'Indexing by title should have books by title.');\n        self::assertTrue(isset($result[11]->books['Test']), 'Indexing by title should have books by title.');\n    }\n}\n\n#[Entity]\nclass DDC618Author\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @phpstan-var Collection<int, DDC618Book> */\n    #[OneToMany(targetEntity: 'DDC618Book', mappedBy: 'author', cascade: ['persist'])]\n    public $books;\n\n    public function __construct()\n    {\n        $this->books = new ArrayCollection();\n    }\n\n    public function addBook(string $title): void\n    {\n        $book          = new DDC618Book($title, $this);\n        $this->books[] = $book;\n    }\n}\n\n#[Entity]\nclass DDC618Book\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $title,\n        #[ManyToOne(targetEntity: 'DDC618Author', inversedBy: 'books')]\n        public DDC618Author $author,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC6303Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\Persistence\\Mapping\\MappingException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_keys;\nuse function array_walk;\nuse function count;\n\n#[Group('#6303')]\nclass DDC6303Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC6303BaseClass::class,\n            DDC6303ChildA::class,\n            DDC6303ChildB::class,\n        );\n    }\n\n    public function testMixedTypeHydratedCorrectlyInJoinedInheritance(): void\n    {\n        // DDC6303ChildA and DDC6303ChildB have an inheritance from DDC6303BaseClass,\n        // but one has a string originalData and the second has an array, since the fields\n        // are mapped differently\n        $this->assertHydratedEntitiesSameToPersistedOnes([\n            'a' => new DDC6303ChildA('a', 'authorized'),\n            'b' => new DDC6303ChildB('b', ['accepted', 'authorized']),\n        ]);\n    }\n\n    public function testEmptyValuesInJoinedInheritance(): void\n    {\n        $this->assertHydratedEntitiesSameToPersistedOnes([\n            'stringEmpty' => new DDC6303ChildA('stringEmpty', ''),\n            'stringZero'  => new DDC6303ChildA('stringZero', 0),\n            'arrayEmpty'  => new DDC6303ChildB('arrayEmpty', []),\n        ]);\n    }\n\n    /**\n     * @param DDC6303BaseClass[] $persistedEntities indexed by identifier\n     *\n     * @throws MappingException\n     * @throws ORMException\n     * @throws OptimisticLockException\n     */\n    private function assertHydratedEntitiesSameToPersistedOnes(array $persistedEntities): void\n    {\n        array_walk($persistedEntities, [$this->_em, 'persist']);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        /** @var DDC6303BaseClass[] $entities */\n        $entities = $this\n            ->_em\n            ->getRepository(DDC6303BaseClass::class)\n            ->createQueryBuilder('p')\n            ->where('p.id IN(:ids)')\n            ->setParameter('ids', array_keys($persistedEntities))\n            ->getQuery()->getResult();\n\n        self::assertCount(count($persistedEntities), $entities);\n\n        foreach ($entities as $entity) {\n            self::assertEquals($entity, $persistedEntities[$entity->id]);\n        }\n    }\n}\n\n/**\n * Note: discriminator map order *IS IMPORTANT* for this test\n */\n#[Table]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap([DDC6303ChildB::class => DDC6303ChildB::class, DDC6303ChildA::class => DDC6303ChildA::class])]\nabstract class DDC6303BaseClass\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $id;\n}\n\n#[Table]\n#[Entity]\nclass DDC6303ChildA extends DDC6303BaseClass\n{\n    public function __construct(\n        string $id,\n        #[Column(type: 'string', length: 255)]\n        private mixed $originalData,\n    ) {\n        $this->id = $id;\n    }\n}\n\n#[Table]\n#[Entity]\nclass DDC6303ChildB extends DDC6303BaseClass\n{\n    /** @param mixed[] $originalData */\n    public function __construct(\n        string $id,\n        #[Column(type: 'simple_array', nullable: true)]\n        private array $originalData,\n    ) {\n        $this->id = $id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC633Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC633Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC633Patient::class,\n            DDC633Appointment::class,\n        );\n    }\n\n    #[Group('DDC-633')]\n    #[Group('DDC-952')]\n    #[Group('DDC-914')]\n    public function testOneToOneEager(): void\n    {\n        $app              = new DDC633Appointment();\n        $pat              = new DDC633Patient();\n        $app->patient     = $pat;\n        $pat->appointment = $app;\n\n        $this->_em->persist($app);\n        $this->_em->persist($pat);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $eagerAppointment = $this->_em->find(DDC633Appointment::class, $app->id);\n\n        // Eager loading of one to one leads to fetch-join\n        self::assertFalse($this->isUninitializedObject($eagerAppointment->patient));\n        self::assertTrue($this->_em->contains($eagerAppointment->patient));\n    }\n\n    #[Group('DDC-633')]\n    #[Group('DDC-952')]\n    public function testDQLDeferredEagerLoad(): void\n    {\n        for ($i = 0; $i < 10; $i++) {\n            $app              = new DDC633Appointment();\n            $pat              = new DDC633Patient();\n            $app->patient     = $pat;\n            $pat->appointment = $app;\n\n            $this->_em->persist($app);\n            $this->_em->persist($pat);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $appointments = $this->_em->createQuery('SELECT a FROM ' . __NAMESPACE__ . '\\DDC633Appointment a')->getResult();\n\n        foreach ($appointments as $eagerAppointment) {\n            self::assertFalse($this->isUninitializedObject($eagerAppointment->patient), 'Proxy should already be initialized due to eager loading!');\n        }\n    }\n}\n\n#[Entity]\nclass DDC633Appointment\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC633Patient */\n    #[OneToOne(targetEntity: 'DDC633Patient', inversedBy: 'appointment', fetch: 'EAGER')]\n    public $patient;\n}\n\n#[Entity]\nclass DDC633Patient\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var DDC633Appointment */\n    #[OneToOne(targetEntity: 'DDC633Appointment', mappedBy: 'patient')]\n    public $appointment;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC6460Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Schema\\SchemaException;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC6460Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        try {\n            $this->setUpEntitySchema(\n                [\n                    DDC6460Entity::class,\n                    DDC6460ParentEntity::class,\n                ],\n            );\n        } catch (SchemaException) {\n        }\n    }\n\n    #[Group('DDC-6460')]\n    public function testInlineEmbeddable(): void\n    {\n        $isFieldMapped = $this->_em\n            ->getClassMetadata(DDC6460Entity::class)\n            ->hasField('embedded');\n\n        self::assertTrue($isFieldMapped);\n    }\n\n    #[Group('DDC-6460')]\n    public function testInlineEmbeddableProxyInitialization(): void\n    {\n        $entity                  = new DDC6460Entity();\n        $entity->id              = 1;\n        $entity->embedded        = new DDC6460Embeddable();\n        $entity->embedded->field = 'test';\n        $this->_em->persist($entity);\n\n        $second             = new DDC6460ParentEntity();\n        $second->id         = 1;\n        $second->lazyLoaded = $entity;\n        $this->_em->persist($second);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $secondEntityWithLazyParameter = $this->_em->getRepository(DDC6460ParentEntity::class)->findOneById(1);\n\n        self::assertInstanceOf(DDC6460Entity::class, $secondEntityWithLazyParameter->lazyLoaded);\n        self::assertTrue($this->isUninitializedObject($secondEntityWithLazyParameter->lazyLoaded));\n        self::assertEquals($secondEntityWithLazyParameter->lazyLoaded->embedded, $entity->embedded);\n        self::assertFalse($this->isUninitializedObject($secondEntityWithLazyParameter->lazyLoaded));\n    }\n}\n\n#[Embeddable]\nclass DDC6460Embeddable\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $field;\n}\n\n#[Entity]\nclass DDC6460Entity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'NONE')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC6460Embeddable */\n    #[Embedded(class: 'DDC6460Embeddable')]\n    public $embedded;\n}\n\n#[Entity]\nclass DDC6460ParentEntity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'NONE')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC6460Entity */\n    #[ManyToOne(targetEntity: 'DDC6460Entity', fetch: 'EXTRA_LAZY', cascade: ['persist'])]\n    public $lazyLoaded;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC6558Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-6558')]\nclass DDC6558Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->_schemaTool->createSchema([\n            $this->_em->getClassMetadata(DDC6558Person::class),\n            $this->_em->getClassMetadata(DDC6558Employee::class),\n            $this->_em->getClassMetadata(DDC6558Staff::class),\n            $this->_em->getClassMetadata(DDC6558Developer::class),\n            $this->_em->getClassMetadata(DDC6558Manager::class),\n        ]);\n    }\n\n    public function testEmployeeIsPopulated(): void\n    {\n        $developer               = new DDC6558Developer();\n        $developer->phoneNumber  = 1231231231;\n        $developer->emailAddress = 'email@address.com';\n\n        $this->_em->persist($developer);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedDeveloper = $this->_em->find(DDC6558Person::class, $developer->id);\n\n        self::assertNotNull($persistedDeveloper->phoneNumber);\n        self::assertNotNull($persistedDeveloper->emailAddress);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorColumn(name: 'discr', type: 'string')]\n#[ORM\\DiscriminatorMap(['manager' => DDC6558Manager::class, 'staff' => DDC6558Staff::class, 'developer' => DDC6558Developer::class])]\nabstract class DDC6558Person\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n}\n\n#[ORM\\Entity]\nclass DDC6558Manager extends DDC6558Person\n{\n}\n\n#[ORM\\Entity]\nabstract class DDC6558Employee extends DDC6558Person\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $phoneNumber;\n}\n\n#[ORM\\Entity]\nclass DDC6558Staff extends DDC6558Employee\n{\n}\n\n#[ORM\\Entity]\nclass DDC6558Developer extends DDC6558Employee\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $emailAddress;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC656Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function array_keys;\n\nclass DDC656Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC656Entity::class);\n    }\n\n    public function testRecomputeSingleEntityChangeSetPreservesFieldOrder(): void\n    {\n        $entity = new DDC656Entity();\n        $entity->setName('test1');\n        $entity->setType('type1');\n        $this->_em->persist($entity);\n\n        $this->_em->getUnitOfWork()->computeChangeSet($this->_em->getClassMetadata($entity::class), $entity);\n        $data1 = $this->_em->getUnitOfWork()->getEntityChangeSet($entity);\n        $entity->setType('type2');\n        $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet($this->_em->getClassMetadata($entity::class), $entity);\n        $data2 = $this->_em->getUnitOfWork()->getEntityChangeSet($entity);\n\n        self::assertEquals(array_keys($data1), array_keys($data2));\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedEntity = $this->_em->find($entity::class, $entity->specificationId);\n        self::assertEquals('type2', $persistedEntity->getType());\n        self::assertEquals('test1', $persistedEntity->getName());\n    }\n}\n\n#[Entity]\nclass DDC656Entity\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $type;\n\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $specificationId;\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getType(): string\n    {\n        return $this->type;\n    }\n\n    public function setType(string $type): void\n    {\n        $this->type = $type;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC6573Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\Tests\\Models\\DDC6573\\DDC6573Currency;\nuse Doctrine\\Tests\\Models\\DDC6573\\DDC6573Item;\nuse Doctrine\\Tests\\Models\\DDC6573\\DDC6573Money;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-6573')]\nfinal class DDC6573Test extends OrmFunctionalTestCase\n{\n    /** @var list<DDC6573Item> */\n    private $fixtures;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC6573Item::class,\n        );\n\n        $item1 = new DDC6573Item('Plate', new DDC6573Money(5, new DDC6573Currency('GBP')));\n        $item2 = new DDC6573Item('Iron', new DDC6573Money(50, new DDC6573Currency('EUR')));\n        $item3 = new DDC6573Item('Teapot', new DDC6573Money(10, new DDC6573Currency('GBP')));\n\n        $this->_em->persist($item1);\n        $this->_em->persist($item2);\n        $this->_em->persist($item3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->fixtures = [$item1, $item2, $item3];\n    }\n\n    protected function tearDown(): void\n    {\n        $this->_em->createQuery('DELETE FROM Doctrine\\Tests\\Models\\DDC6573\\DDC6573Item i')->execute();\n    }\n\n    public static function provideDataForHydrationMode(): iterable\n    {\n        yield [AbstractQuery::HYDRATE_ARRAY];\n        yield [AbstractQuery::HYDRATE_OBJECT];\n    }\n\n    #[DataProvider('provideDataForHydrationMode')]\n    public function testShouldSupportsMultipleNewOperator(int $hydrationMode): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\DDC6573\\DDC6573Money(\n                    i.priceAmount,\n                    new Doctrine\\Tests\\Models\\DDC6573\\DDC6573Currency(i.priceCurrency)\n                )\n            FROM\n                Doctrine\\Tests\\Models\\DDC6573\\DDC6573Item i\n            ORDER BY\n                i.priceAmount ASC';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult($hydrationMode);\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(DDC6573Money::class, $result[0]);\n        self::assertInstanceOf(DDC6573Money::class, $result[1]);\n        self::assertInstanceOf(DDC6573Money::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->getPrice(), $result[0]);\n        self::assertEquals($this->fixtures[2]->getPrice(), $result[1]);\n        self::assertEquals($this->fixtures[1]->getPrice(), $result[2]);\n    }\n\n    #[DataProvider('provideDataForHydrationMode')]\n    public function testShouldSupportsBasicUsage(int $hydrationMode): void\n    {\n        $dql = '\n            SELECT\n                new Doctrine\\Tests\\Models\\DDC6573\\DDC6573Currency(\n                    i.priceCurrency\n                )\n            FROM\n                Doctrine\\Tests\\Models\\DDC6573\\DDC6573Item i\n            ORDER BY\n                i.priceAmount';\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult($hydrationMode);\n\n        self::assertCount(3, $result);\n\n        self::assertInstanceOf(DDC6573Currency::class, $result[0]);\n        self::assertInstanceOf(DDC6573Currency::class, $result[1]);\n        self::assertInstanceOf(DDC6573Currency::class, $result[2]);\n\n        self::assertEquals($this->fixtures[0]->getPrice()->getCurrency(), $result[0]);\n        self::assertEquals($this->fixtures[1]->getPrice()->getCurrency(), $result[2]);\n        self::assertEquals($this->fixtures[2]->getPrice()->getCurrency(), $result[1]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC657Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse DateTimeZone;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-657')]\nclass DDC657Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('generic');\n\n        parent::setUp();\n\n        $this->loadFixtures();\n    }\n\n    public function testEntitySingleResult(): void\n    {\n        $query    = $this->_em->createQuery('SELECT d FROM ' . DateTimeModel::class . ' d');\n        $datetime = $query->setMaxResults(1)->getSingleResult();\n\n        self::assertInstanceOf(DateTimeModel::class, $datetime);\n\n        self::assertInstanceOf(DateTime::class, $datetime->datetime);\n        self::assertInstanceOf(DateTime::class, $datetime->time);\n        self::assertInstanceOf(DateTime::class, $datetime->date);\n    }\n\n    public function testScalarResult(): void\n    {\n        $query  = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . DateTimeModel::class . ' d ORDER BY d.date ASC');\n        $result = $query->getScalarResult();\n\n        self::assertCount(2, $result);\n\n        self::assertStringContainsString('11:11:11', $result[0]['time']);\n        self::assertStringContainsString('2010-01-01', $result[0]['date']);\n        self::assertStringContainsString('2010-01-01 11:11:11', $result[0]['datetime']);\n\n        self::assertStringContainsString('12:12:12', $result[1]['time']);\n        self::assertStringContainsString('2010-02-02', $result[1]['date']);\n        self::assertStringContainsString('2010-02-02 12:12:12', $result[1]['datetime']);\n    }\n\n    public function testaTicketEntityArrayResult(): void\n    {\n        $query  = $this->_em->createQuery('SELECT d FROM ' . DateTimeModel::class . ' d ORDER BY d.date ASC');\n        $result = $query->getArrayResult();\n\n        self::assertCount(2, $result);\n\n        self::assertInstanceOf(DateTime::class, $result[0]['datetime']);\n        self::assertInstanceOf(DateTime::class, $result[0]['time']);\n        self::assertInstanceOf(DateTime::class, $result[0]['date']);\n\n        self::assertInstanceOf(DateTime::class, $result[1]['datetime']);\n        self::assertInstanceOf(DateTime::class, $result[1]['time']);\n        self::assertInstanceOf(DateTime::class, $result[1]['date']);\n    }\n\n    public function testTicketSingleResult(): void\n    {\n        $query    = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . DateTimeModel::class . ' d ORDER BY d.date ASC');\n        $datetime = $query->setMaxResults(1)->getSingleResult();\n\n        self::assertIsArray($datetime);\n\n        self::assertInstanceOf(DateTime::class, $datetime['datetime']);\n        self::assertInstanceOf(DateTime::class, $datetime['time']);\n        self::assertInstanceOf(DateTime::class, $datetime['date']);\n    }\n\n    public function testTicketResult(): void\n    {\n        $query  = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . DateTimeModel::class . ' d ORDER BY d.date ASC');\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n\n        self::assertInstanceOf(DateTime::class, $result[0]['time']);\n        self::assertInstanceOf(DateTime::class, $result[0]['date']);\n        self::assertInstanceOf(DateTime::class, $result[0]['datetime']);\n\n        self::assertEquals('2010-01-01 11:11:11', $result[0]['datetime']->format('Y-m-d G:i:s'));\n\n        self::assertInstanceOf(DateTime::class, $result[1]['time']);\n        self::assertInstanceOf(DateTime::class, $result[1]['date']);\n        self::assertInstanceOf(DateTime::class, $result[1]['datetime']);\n\n        self::assertEquals('2010-02-02 12:12:12', $result[1]['datetime']->format('Y-m-d G:i:s'));\n    }\n\n    public function loadFixtures(): void\n    {\n        $timezone = new DateTimeZone('America/Sao_Paulo');\n\n        $dateTime1 = new DateTimeModel();\n        $dateTime2 = new DateTimeModel();\n\n        $dateTime1->date     = new DateTime('2010-01-01', $timezone);\n        $dateTime1->time     = new DateTime('2010-01-01 11:11:11', $timezone);\n        $dateTime1->datetime = new DateTime('2010-01-01 11:11:11', $timezone);\n\n        $dateTime2->date     = new DateTime('2010-02-02', $timezone);\n        $dateTime2->time     = new DateTime('2010-02-02 12:12:12', $timezone);\n        $dateTime2->datetime = new DateTime('2010-02-02 12:12:12', $timezone);\n\n        $this->_em->persist($dateTime1);\n        $this->_em->persist($dateTime2);\n\n        $this->_em->flush();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC698Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strtolower;\n\nclass DDC698Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC698Role::class, DDC698Privilege::class);\n    }\n\n    public function testTicket(): void\n    {\n        $qb = $this->_em->createQueryBuilder();\n        $qb->select('p', 'r')\n           ->from(__NAMESPACE__ . '\\DDC698Privilege', 'p')\n           ->leftJoin('p.roles', 'r');\n\n        $sql = $qb->getQuery()->getSQL();\n\n        self::assertEquals(\n            strtolower('SELECT p0_.privilegeID AS privilegeID_0, p0_.name AS name_1, r1_.roleID AS roleID_2, r1_.name AS name_3, r1_.shortName AS shortName_4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID'),\n            strtolower($sql),\n        );\n    }\n}\n\n#[Table(name: 'Roles')]\n#[Entity]\nclass DDC698Role\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'roleID', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $roleID;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 45)]\n    protected $name;\n\n    /** @var string */\n    #[Column(name: 'shortName', type: 'string', length: 45)]\n    protected $shortName;\n\n    /** @var Collection<int, DDC698Privilege> */\n    #[JoinTable(name: 'RolePrivileges')]\n    #[JoinColumn(name: 'roleID', referencedColumnName: 'roleID')]\n    #[InverseJoinColumn(name: 'privilegeID', referencedColumnName: 'privilegeID')]\n    #[ManyToMany(targetEntity: 'DDC698Privilege', inversedBy: 'roles')]\n    protected $privilege;\n}\n\n\n#[Table(name: 'Privileges')]\n#[Entity]\nclass DDC698Privilege\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'privilegeID', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    protected $privilegeID;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 45)]\n    protected $name;\n\n    /** @phpstan-var Collection<int, DDC698Role> */\n    #[ManyToMany(targetEntity: 'DDC698Role', mappedBy: 'privilege')]\n    protected $roles;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC69Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function assert;\n\n/**\n * Functional tests for the Single Table Inheritance mapping strategy.\n */\nclass DDC69Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        // some of these models are created in other tests, but not all\n        $this->createSchemaForModels(RelationType::class);\n        $this->createSchemaForModels(Relation::class);\n        $this->createSchemaForModels(Lemma::class);\n    }\n\n    public function testIssue(): void\n    {\n        //setup\n        $lemma1 = new Lemma();\n        $lemma1->setLemma('foo');\n\n        $lemma2 = new Lemma();\n        $lemma2->setLemma('bar');\n\n        $lemma3 = new Lemma();\n        $lemma3->setLemma('batz');\n\n        $lemma4 = new Lemma();\n        $lemma4->setLemma('bla');\n\n        $type1 = new RelationType();\n        $type1->setType('nonsense');\n        $type1->setAbbreviation('non');\n\n        $type2 = new RelationType();\n        $type2->setType('quatsch');\n        $type2->setAbbreviation('qu');\n\n        $relation1 = new Relation();\n        $relation1->setParent($lemma1);\n        $relation1->setChild($lemma2);\n        $relation1->setType($type1);\n\n        $relation2 = new Relation();\n        $relation2->setParent($lemma1);\n        $relation2->setChild($lemma3);\n        $relation2->setType($type1);\n\n        $relation3 = new Relation();\n        $relation3->setParent($lemma1);\n        $relation3->setChild($lemma4);\n        $relation3->setType($type2);\n\n        $lemma1->addRelation($relation1);\n        $lemma1->addRelation($relation2);\n        $lemma1->addRelation($relation3);\n\n        $this->_em->persist($type1);\n        $this->_em->persist($type2);\n        $this->_em->persist($lemma1);\n        $this->_em->persist($lemma2);\n        $this->_em->persist($lemma3);\n        $this->_em->persist($lemma4);\n\n        $this->_em->flush();\n        $this->_em->clear();\n        //end setup\n\n        // test One To Many\n        $query = $this->_em->createQuery(\"SELECT l FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\Lemma l Where l.lemma = 'foo'\");\n        $res   = $query->getResult();\n        $lemma = $res[0];\n\n        self::assertEquals('foo', $lemma->getLemma());\n        self::assertInstanceOf(Lemma::class, $lemma);\n        $relations = $lemma->getRelations();\n\n        foreach ($relations as $relation) {\n            self::assertInstanceOf(Relation::class, $relation);\n            self::assertTrue($relation->getType()->getType() !== '');\n        }\n\n        $this->_em->clear();\n    }\n}\n\n#[Table(name: 'lemma')]\n#[Entity]\nclass Lemma\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'lemma_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'lemma_name', unique: true, length: 255)]\n    private string|null $lemma = null;\n\n    /** @var Collection<int, Relation> */\n    #[OneToMany(targetEntity: 'Relation', mappedBy: 'parent', cascade: ['persist'])]\n    private Collection $relations;\n\n    public function __construct()\n    {\n        $this->relations = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setLemma(string $lemma): void\n    {\n        $this->lemma = $lemma;\n    }\n\n    public function getLemma(): string\n    {\n        return $this->lemma;\n    }\n\n    public function addRelation(Relation $relation): void\n    {\n        $this->relations[] = $relation;\n        $relation->setParent($this);\n    }\n\n    public function removeRelation(Relation $relation): void\n    {\n        $removed = $this->relations->removeElement($relation);\n        assert($removed instanceof Relation);\n        if ($removed !== null) {\n            $removed->removeParent();\n        }\n    }\n\n    /** @phpstan-return Collection<int, Relation> */\n    public function getRelations(): Collection\n    {\n        return $this->relations;\n    }\n}\n\n#[Table(name: 'relation')]\n#[Entity]\nclass Relation\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'relation_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'Lemma', inversedBy: 'relations')]\n    #[JoinColumn(name: 'relation_parent_id', referencedColumnName: 'lemma_id')]\n    private Lemma|null $parent = null;\n\n    #[OneToOne(targetEntity: 'Lemma')]\n    #[JoinColumn(name: 'relation_child_id', referencedColumnName: 'lemma_id')]\n    private Lemma|null $child = null;\n\n    #[ManyToOne(targetEntity: 'RelationType', inversedBy: 'relations')]\n    #[JoinColumn(name: 'relation_type_id', referencedColumnName: 'relation_type_id')]\n    private RelationType|null $type = null;\n\n    public function setParent(Lemma $parent): void\n    {\n        $this->parent = $parent;\n    }\n\n    public function getParent(): Phrase\n    {\n        return $this->parent;\n    }\n\n    public function removeParent(): void\n    {\n        if ($this->lemma !== null) {\n            $lemma        = $this->parent;\n            $this->parent = null;\n            $lemma->removeRelation($this);\n        }\n    }\n\n    public function setChild(Lemma $child): void\n    {\n        $this->child = $child;\n    }\n\n    public function getChild(): Lemma\n    {\n        return $this->child;\n    }\n\n    public function setType(RelationType $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): RelationType\n    {\n        return $this->type;\n    }\n\n    public function removeType(): void\n    {\n        if ($this->type !== null) {\n            $type       = $this->type;\n            $this->type = null;\n            $type->removeRelation($this);\n        }\n    }\n}\n\n#[Table(name: 'relation_type')]\n#[Entity]\nclass RelationType\n{\n    public const CLASS_NAME = self::class;\n\n    #[Id]\n    #[Column(type: 'integer', name: 'relation_type_id')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', name: 'relation_type_name', unique: true, length: 255)]\n    private string|null $type = null;\n\n    #[Column(type: 'string', name: 'relation_type_abbreviation', unique: true, length: 255)]\n    private string|null $abbreviation = null;\n\n    /** @var Collection<int, Relation> */\n    #[OneToMany(targetEntity: 'Relation', mappedBy: 'type', cascade: ['persist'])]\n    private $relations;\n\n    public function __construct()\n    {\n        $relations = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setType(string $type): void\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): string\n    {\n        return $this->type;\n    }\n\n    public function setAbbreviation(string $abbreviation): void\n    {\n        $this->abbreviation = $abbreviation;\n    }\n\n    public function getAbbreviation(): string\n    {\n        return $this->abbreviation;\n    }\n\n    public function addRelation(Relation $relation): void\n    {\n        $this->relations[] = $relation;\n        $relation->setType($this);\n    }\n\n    public function removeRelation(Relation $relation): void\n    {\n        $removed = $this->relations->removeElement($relation);\n        assert($removed instanceof Relation);\n        if ($removed !== null) {\n            $removed->removeLemma();\n        }\n    }\n\n    /** @phpstan-return Collection<int, Relation> */\n    public function getRelations(): Collection\n    {\n        return $this->relations;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC719Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function strtolower;\n\nclass DDC719Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC719Group::class);\n    }\n\n    public function testIsEmptySqlGeneration(): void\n    {\n        $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\DDC719Group g LEFT JOIN g.children c  WHERE g.parents IS EMPTY');\n\n        $referenceSQL = 'SELECT g0_.id AS id_0, g0_.name AS name_1, g0_.description AS description_2, g1_.id as id_3, g1_.name AS name_4, g1_.description AS description_5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0';\n\n        self::assertEquals(\n            strtolower($referenceSQL),\n            strtolower($q->getSQL()),\n        );\n    }\n}\n\n#[MappedSuperclass]\nclass MyEntity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    protected $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'groups')]\n#[Entity]\nclass DDC719Group extends MyEntity\n{\n    /** @var string */\n    #[Column(type: 'string', nullable: false)]\n    protected $name;\n\n    /** @var string */\n    #[Column(type: 'string', nullable: true)]\n    protected $description;\n\n    /** @phpstan-var Collection<int, DDC719Group> */\n    #[JoinTable(name: 'groups_groups')]\n    #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'child_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'DDC719Group', inversedBy: 'parents')]\n    protected $children = null;\n\n    /** @phpstan-var Collection<int, DDC719Group> */\n    #[ManyToMany(targetEntity: 'DDC719Group', mappedBy: 'children')]\n    protected $parents = null;\n\n    public function __construct()\n    {\n        $this->channels = new ArrayCollection();\n        $this->children = new ArrayCollection();\n        $this->parents  = new ArrayCollection();\n    }\n\n    /**\n     * adds group as new child\n     */\n    public function addGroup(Group $child): void\n    {\n        if (! $this->children->contains($child)) {\n            $this->children->add($child);\n            $child->addGroup($this);\n        }\n    }\n\n    /**\n     * adds channel as new child\n     */\n    public function addChannel(Channel $child): void\n    {\n        if (! $this->channels->contains($child)) {\n            $this->channels->add($child);\n        }\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n\n    public function setDescription(string $description): void\n    {\n        $this->description = $description;\n    }\n\n    /** @phpstan-return Collection<int, DDC719Group> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n\n    /** @phpstan-return Collection<int, DDC719Group> */\n    public function getParents(): Collection\n    {\n        return $this->parents;\n    }\n\n    /** @phpstan-return Collection<int, Channel> */\n    public function getChannels(): Collection\n    {\n        return $this->channels;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC735Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC735Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC735Product::class, DDC735Review::class);\n    }\n\n    public function testRemoveElementAppliesOrphanRemoval(): void\n    {\n        // Create a product and its first review\n        $product = new DDC735Product();\n        $review  = new DDC735Review($product);\n\n        // Persist and flush\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        // Now you see it\n        self::assertCount(1, $product->getReviews());\n\n        // Remove the review\n        $reviewId = $review->getId();\n        $product->removeReview($review);\n        $this->_em->flush();\n\n        // Now you don't\n        self::assertCount(0, $product->getReviews(), 'count($reviews) should be 0 after removing its only Review');\n\n        // Refresh\n        $this->_em->refresh($product);\n\n        // It should still be 0\n        self::assertCount(0, $product->getReviews(), 'count($reviews) should still be 0 after the refresh');\n\n        // Review should also not be available anymore\n        self::assertNull($this->_em->find(DDC735Review::class, $reviewId));\n    }\n}\n\n#[Entity]\nclass DDC735Product\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @phpstan-var Collection<int, DDC735Review> */\n    #[OneToMany(targetEntity: 'DDC735Review', mappedBy: 'product', cascade: ['persist'], orphanRemoval: true)]\n    protected $reviews;\n\n    public function __construct()\n    {\n        $this->reviews = new ArrayCollection();\n    }\n\n    /** @phpstan-return Collection<int, DDC735Review> */\n    public function getReviews(): Collection\n    {\n        return $this->reviews;\n    }\n\n    public function addReview(DDC735Review $review): void\n    {\n        $this->reviews->add($review);\n    }\n\n    public function removeReview(DDC735Review $review): void\n    {\n        $this->reviews->removeElement($review);\n    }\n}\n\n#[Entity]\nclass DDC735Review\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: 'DDC735Product', inversedBy: 'reviews')]\n        protected DDC735Product $product,\n    ) {\n        $product->addReview($this);\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC736Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\SelectExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass DDC736Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-736')]\n    public function testReorderEntityFetchJoinForHydration(): void\n    {\n        $cust = new ECommerceCustomer();\n        $cust->setName('roman');\n\n        $cart = new ECommerceCart();\n        $cart->setPayment('cash');\n        $cart->setCustomer($cust);\n\n        $this->_em->persist($cust);\n        $this->_em->persist($cart);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em->createQuery('select c, c.name, ca, ca.payment from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart ca join ca.customer c')\n            ->getSingleResult(/*\\Doctrine\\ORM\\Query::HYDRATE_ARRAY*/);\n\n        $cart2 = $result[0];\n        unset($result[0]);\n\n        self::assertInstanceOf(ECommerceCart::class, $cart2);\n        self::assertFalse($this->isUninitializedObject($cart2->getCustomer()));\n        self::assertInstanceOf(ECommerceCustomer::class, $cart2->getCustomer());\n        self::assertEquals(['name' => 'roman', 'payment' => 'cash'], $result);\n    }\n\n    #[Group('DDC-736')]\n    #[Group('DDC-925')]\n    #[Group('DDC-915')]\n    public function testDqlTreeWalkerReordering(): void\n    {\n        $cust = new ECommerceCustomer();\n        $cust->setName('roman');\n\n        $cart = new ECommerceCart();\n        $cart->setPayment('cash');\n        $cart->setCustomer($cust);\n\n        $this->_em->persist($cust);\n        $this->_em->persist($cart);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql    = 'select c, c.name, ca, ca.payment from Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart ca join ca.customer c';\n        $result = $this->_em->createQuery($dql)\n                            ->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [DisableFetchJoinTreeWalker::class])\n                            ->getResult();\n\n        $cart2 = $result[0][0];\n        assert($cart2 instanceof ECommerceCart);\n        self::assertTrue($this->isUninitializedObject($cart2->getCustomer()));\n    }\n}\n\nclass DisableFetchJoinTreeWalker extends TreeWalkerAdapter\n{\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        foreach ($selectStatement->selectClause->selectExpressions as $key => $selectExpr) {\n            assert($selectExpr instanceof SelectExpression);\n            if ($selectExpr->expression === 'c') {\n                unset($selectStatement->selectClause->selectExpressions[$key]);\n                break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC748Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC748Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testRefreshWithManyToOne(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'beberlei';\n        $user->status   = 'active';\n        $user->username = 'beberlei';\n\n        $article = new CmsArticle();\n        $article->setAuthor($user);\n        $article->text  = 'foo';\n        $article->topic = 'bar';\n\n        $this->_em->persist($user);\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        self::assertInstanceOf(Collection::class, $user->articles);\n        $this->_em->refresh($article);\n        self::assertNotSame($article, $user->articles, 'The article should not be replaced on the inverse side of the relation.');\n        self::assertInstanceOf(Collection::class, $user->articles);\n    }\n\n    public function testRefreshOneToOne(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'beberlei';\n        $user->status   = 'active';\n        $user->username = 'beberlei';\n\n        $address          = new CmsAddress();\n        $address->city    = 'Bonn';\n        $address->country = 'Germany';\n        $address->street  = 'A street';\n        $address->zip     = 12345;\n        $address->setUser($user);\n\n        $this->_em->persist($user);\n        $this->_em->persist($address);\n        $this->_em->flush();\n\n        $this->_em->refresh($address);\n        self::assertSame($user, $address->user);\n        self::assertSame($user->address, $address);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC767Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass DDC767Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-767')]\n    public function testCollectionChangesInsideTransaction(): void\n    {\n        $user           = new CmsUser();\n        $user->name     = 'beberlei';\n        $user->status   = 'active';\n        $user->username = 'beberlei';\n\n        $group1       = new CmsGroup();\n        $group1->name = 'foo';\n\n        $group2       = new CmsGroup();\n        $group2->name = 'bar';\n\n        $group3       = new CmsGroup();\n        $group3->name = 'baz';\n\n        $user->addGroup($group1);\n        $user->addGroup($group2);\n\n        $this->_em->persist($user);\n        $this->_em->persist($group1);\n        $this->_em->persist($group2);\n        $this->_em->persist($group3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $pUser = $this->_em->find($user::class, $user->id);\n        assert($pUser instanceof CmsUser);\n\n        self::assertNotNull($pUser, 'User not retrieved from database.');\n\n        $groups = [$group2->id, $group3->id];\n\n        try {\n            $this->_em->beginTransaction();\n\n            $pUser->groups->clear();\n\n            $this->_em->flush();\n\n            // Add new\n            foreach ($groups as $groupId) {\n                $pUser->addGroup($this->_em->find($group1::class, $groupId));\n            }\n\n            $this->_em->flush();\n            $this->_em->commit();\n        } catch (Exception) {\n            $this->_em->rollback();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC7969Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace tests\\Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\Bar;\nuse Doctrine\\Tests\\ORM\\Functional\\SecondLevelCacheFunctionalTestCase;\n\nuse function assert;\n\nclass DDC7969Test extends SecondLevelCacheFunctionalTestCase\n{\n    public function testChildEntityRetrievedFromCache(): void\n    {\n        $this->loadFixturesCountries();\n        $this->loadFixturesStates();\n        $this->loadFixturesCities();\n        $this->loadFixturesAttractions();\n\n        // Entities are already cached due to fixtures - hence flush before testing\n        $this->cache->getEntityCacheRegion(Attraction::class)->evictAll();\n\n        $bar = $this->attractions[0];\n        assert($bar instanceof Bar);\n\n        $repository = $this->_em->getRepository(Bar::class);\n\n        self::assertFalse($this->cache->containsEntity(Bar::class, $bar->getId()));\n        self::assertFalse($this->cache->containsEntity(Attraction::class, $bar->getId()));\n\n        $repository->findOneBy([\n            'name' => $bar->getName(),\n        ]);\n\n        self::assertTrue($this->cache->containsEntity(Bar::class, $bar->getId()));\n\n        $repository->findOneBy([\n            'name' => $bar->getName(),\n        ]);\n\n        // One hit for entity cache, one hit for query cache\n        self::assertEquals(2, $this->secondLevelCacheLogger->getHitCount());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC809Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC809Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC809Variant::class,\n            DDC809SpecificationValue::class,\n        );\n\n        $conn = $this->_em->getConnection();\n        $conn->insert('specification_value_test', ['specification_value_id' => 94589]);\n        $conn->insert('specification_value_test', ['specification_value_id' => 94593]);\n        $conn->insert('specification_value_test', ['specification_value_id' => 94606]);\n        $conn->insert('specification_value_test', ['specification_value_id' => 94607]);\n        $conn->insert('specification_value_test', ['specification_value_id' => 94609]);\n        $conn->insert('specification_value_test', ['specification_value_id' => 94711]);\n\n        $conn->insert('variant_test', ['variant_id' => 545208]);\n        $conn->insert('variant_test', ['variant_id' => 545209]);\n\n        $conn->insert('var_spec_value_test', ['variant_id' => 545208, 'specification_value_id' => 94606]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545208, 'specification_value_id' => 94607]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545208, 'specification_value_id' => 94609]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545208, 'specification_value_id' => 94711]);\n\n        $conn->insert('var_spec_value_test', ['variant_id' => 545209, 'specification_value_id' => 94589]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545209, 'specification_value_id' => 94593]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545209, 'specification_value_id' => 94606]);\n        $conn->insert('var_spec_value_test', ['variant_id' => 545209, 'specification_value_id' => 94607]);\n    }\n\n    #[Group('DDC-809')]\n    public function testIssue(): void\n    {\n        $result = $this->_em->createQueryBuilder()\n                        ->select('Variant, SpecificationValue')\n                        ->from(DDC809Variant::class, 'Variant')\n                        ->leftJoin('Variant.specificationValues', 'SpecificationValue')\n                        ->getQuery()\n                        ->getResult();\n\n        self::assertCount(4, $result[0]->getSpecificationValues(), 'Works in test-setup.');\n        self::assertCount(4, $result[1]->getSpecificationValues(), 'Only returns 2 in the case of the hydration bug.');\n    }\n}\n\n#[Table(name: 'variant_test')]\n#[Entity]\nclass DDC809Variant\n{\n    /** @var int */\n    #[Column(name: 'variant_id', type: 'integer')]\n    #[Id]\n    protected $variantId;\n\n    /** @phpstan-var Collection<int, DDC809SpecificationValue> */\n    #[JoinTable(name: 'var_spec_value_test')]\n    #[JoinColumn(name: 'variant_id', referencedColumnName: 'variant_id')]\n    #[InverseJoinColumn(name: 'specification_value_id', referencedColumnName: 'specification_value_id')]\n    #[ManyToMany(targetEntity: 'DDC809SpecificationValue', inversedBy: 'Variants')]\n    protected $specificationValues;\n\n    /** @phpstan-return Collection<int, DDC809SpecificationValue> */\n    public function getSpecificationValues(): Collection\n    {\n        return $this->specificationValues;\n    }\n}\n\n#[Table(name: 'specification_value_test')]\n#[Entity]\nclass DDC809SpecificationValue\n{\n    /** @var int */\n    #[Column(name: 'specification_value_id', type: 'integer')]\n    #[Id]\n    protected $specificationValueId;\n\n    /** @phpstan-var Collection<int,DDC809Variant> */\n    #[ManyToMany(targetEntity: 'DDC809Variant', mappedBy: 'SpecificationValues')]\n    protected $variants;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC812Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC812Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-812')]\n    public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity(): void\n    {\n        $article        = new CmsArticle();\n        $article->topic = 'hello';\n        $article->text  = 'talk talk talk';\n\n        $comment          = new CmsComment();\n        $comment->topic   = 'good!';\n        $comment->text    = 'stuff!';\n        $comment->article = $article;\n\n        $this->_em->persist($article);\n        $this->_em->persist($comment);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $article2 = $this->_em->find($article::class, $article->id);\n\n        $article2Again = $this->_em->createQuery(\n            'select a, c from Doctrine\\Tests\\Models\\CMS\\CmsArticle a join a.comments c where a.id = ?1',\n        )\n            ->setParameter(1, $article->id)\n            ->getSingleResult();\n\n        self::assertSame($article2Again, $article2);\n        self::assertTrue($article2Again->comments->isInitialized());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC832Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function method_exists;\n\nclass DDC832Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof OraclePlatform) {\n            self::markTestSkipped('Doesnt run on Oracle.');\n        }\n\n        $this->createSchemaForModels(\n            DDC832JoinedIndex::class,\n            DDC832JoinedTreeIndex::class,\n            DDC832Like::class,\n        );\n    }\n\n    public function tearDown(): void\n    {\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        $sm = $this->createSchemaManager();\n        $sm->dropTable($platform->quoteSingleIdentifier('TREE_INDEX'));\n        $sm->dropTable($platform->quoteSingleIdentifier('INDEX'));\n        $sm->dropTable($platform->quoteSingleIdentifier('LIKE'));\n\n        // DBAL 3\n        if ($platform instanceof PostgreSQLPlatform && method_exists($platform, 'getIdentitySequenceName')) {\n            $sm->dropSequence($platform->quoteSingleIdentifier('INDEX_id_seq'));\n            $sm->dropSequence($platform->quoteSingleIdentifier('LIKE_id_seq'));\n        }\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableBasicUpdate(): void\n    {\n        $like = new DDC832Like('test');\n        $this->_em->persist($like);\n        $this->_em->flush();\n\n        $like->word = 'test2';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals($like, $this->_em->find(DDC832Like::class, $like->id));\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableBasicRemove(): void\n    {\n        $like = new DDC832Like('test');\n        $this->_em->persist($like);\n        $this->_em->flush();\n\n        $idToBeRemoved = $like->id;\n\n        $this->_em->remove($like);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(DDC832Like::class, $idToBeRemoved));\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableJoinedUpdate(): void\n    {\n        $index = new DDC832JoinedIndex('test');\n        $this->_em->persist($index);\n        $this->_em->flush();\n\n        $index->name = 'asdf';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals($index, $this->_em->find(DDC832JoinedIndex::class, $index->id));\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableJoinedRemove(): void\n    {\n        $index = new DDC832JoinedIndex('test');\n        $this->_em->persist($index);\n        $this->_em->flush();\n\n        $idToBeRemoved = $index->id;\n\n        $this->_em->remove($index);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(DDC832JoinedIndex::class, $idToBeRemoved));\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableJoinedChildUpdate(): void\n    {\n        $index = new DDC832JoinedTreeIndex('test', 1, 2);\n        $this->_em->persist($index);\n        $this->_em->flush();\n\n        $index->name = 'asdf';\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals($index, $this->_em->find(DDC832JoinedTreeIndex::class, $index->id));\n    }\n\n    #[Group('DDC-832')]\n    public function testQuotedTableJoinedChildRemove(): void\n    {\n        $index = new DDC832JoinedTreeIndex('test', 1, 2);\n        $this->_em->persist($index);\n        $this->_em->flush();\n\n        $idToBeRemoved = $index->id;\n\n        $this->_em->remove($index);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertNull($this->_em->find(DDC832JoinedTreeIndex::class, $idToBeRemoved));\n    }\n}\n\n#[Table(name: '`LIKE`')]\n#[Entity]\nclass DDC832Like\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $word,\n    ) {\n    }\n}\n\n#[Table(name: '`INDEX`')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['like' => 'DDC832JoinedIndex', 'fuzzy' => 'DDC832JoinedTreeIndex'])]\nclass DDC832JoinedIndex\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Table(name: '`TREE_INDEX`')]\n#[Entity]\nclass DDC832JoinedTreeIndex extends DDC832JoinedIndex\n{\n    public function __construct(\n        string $name,\n        #[Column(type: 'integer')]\n        public int $lft,\n        #[Column(type: 'integer')]\n        public int $rgt,\n    ) {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC837Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC837Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC837Super::class,\n            DDC837Class1::class,\n            DDC837Class2::class,\n            DDC837Class3::class,\n            DDC837Aggregate::class,\n        );\n    }\n\n    #[Group('DDC-837')]\n    public function testIssue(): void\n    {\n        $c1              = new DDC837Class1();\n        $c1->title       = 'Foo';\n        $c1->description = 'Foo';\n        $aggregate1      = new DDC837Aggregate('test1');\n        $c1->aggregate   = $aggregate1;\n\n        $c2              = new DDC837Class2();\n        $c2->title       = 'Bar';\n        $c2->description = 'Bar';\n        $c2->text        = 'Bar';\n        $aggregate2      = new DDC837Aggregate('test2');\n        $c2->aggregate   = $aggregate2;\n\n        $c3          = new DDC837Class3();\n        $c3->apples  = 'Baz';\n        $c3->bananas = 'Baz';\n\n        $this->_em->persist($c1);\n        $this->_em->persist($aggregate1);\n        $this->_em->persist($c2);\n        $this->_em->persist($aggregate2);\n        $this->_em->persist($c3);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Test Class1\n        $e1 = $this->_em->find(DDC837Super::class, $c1->id);\n\n        self::assertInstanceOf(DDC837Class1::class, $e1);\n        self::assertEquals('Foo', $e1->title);\n        self::assertEquals('Foo', $e1->description);\n        self::assertInstanceOf(DDC837Aggregate::class, $e1->aggregate);\n        self::assertEquals('test1', $e1->aggregate->getSysname());\n\n        // Test Class 2\n        $e2 = $this->_em->find(DDC837Super::class, $c2->id);\n\n        self::assertInstanceOf(DDC837Class2::class, $e2);\n        self::assertEquals('Bar', $e2->title);\n        self::assertEquals('Bar', $e2->description);\n        self::assertEquals('Bar', $e2->text);\n        self::assertInstanceOf(DDC837Aggregate::class, $e2->aggregate);\n        self::assertEquals('test2', $e2->aggregate->getSysname());\n\n        $all = $this->_em->getRepository(DDC837Super::class)->findAll();\n\n        foreach ($all as $obj) {\n            if ($obj instanceof DDC837Class1) {\n                self::assertEquals('Foo', $obj->title);\n                self::assertEquals('Foo', $obj->description);\n            } elseif ($obj instanceof DDC837Class2) {\n                self::assertSame($e2, $obj);\n                self::assertEquals('Bar', $obj->title);\n                self::assertEquals('Bar', $obj->description);\n                self::assertEquals('Bar', $obj->text);\n            } elseif ($obj instanceof DDC837Class3) {\n                self::assertEquals('Baz', $obj->apples);\n                self::assertEquals('Baz', $obj->bananas);\n            } else {\n                self::fail('Instance of DDC837Class1, DDC837Class2 or DDC837Class3 expected.');\n            }\n        }\n    }\n}\n\n#[Table(name: 'DDC837Super')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['class1' => 'DDC837Class1', 'class2' => 'DDC837Class2', 'class3' => 'DDC837Class3'])]\nabstract class DDC837Super\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\nclass DDC837Class1 extends DDC837Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $title;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $description;\n\n    /** @var DDC837Aggregate */\n    #[OneToOne(targetEntity: 'DDC837Aggregate')]\n    public $aggregate;\n}\n\n#[Entity]\nclass DDC837Class2 extends DDC837Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $title;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $description;\n\n    /** @var string */\n    #[Column(name: 'text', type: 'text')]\n    public $text;\n\n    /** @var DDC837Aggregate */\n    #[OneToOne(targetEntity: 'DDC837Aggregate')]\n    public $aggregate;\n}\n\n/**\n * An extra class to demonstrate why title and description aren't in Super\n */\n#[Entity]\nclass DDC837Class3 extends DDC837Super\n{\n    /** @var string */\n    #[Column(name: 'title', type: 'string', length: 150)]\n    public $apples;\n\n    /** @var string */\n    #[Column(name: 'content', type: 'string', length: 500)]\n    public $bananas;\n}\n\n#[Entity]\nclass DDC837Aggregate\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[Column(name: 'sysname', type: 'string', length: 255)]\n        protected string $sysname,\n    ) {\n    }\n\n    public function getSysname(): string\n    {\n        return $this->sysname;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC849Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass DDC849Test extends OrmFunctionalTestCase\n{\n    private CmsUser|null $user;\n\n    private CmsGroup $group1;\n\n    private CmsGroup $group2;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $this->user           = new CmsUser();\n        $this->user->username = 'beberlei';\n        $this->user->name     = 'Benjamin';\n        $this->user->status   = 'active';\n\n        $this->group1       = new CmsGroup();\n        $this->group1->name = 'Group 1';\n        $this->group2       = new CmsGroup();\n        $this->group2->name = 'Group 2';\n\n        $this->user->addGroup($this->group1);\n        $this->user->addGroup($this->group2);\n\n        $this->_em->persist($this->user);\n        $this->_em->persist($this->group1);\n        $this->_em->persist($this->group2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->user = $this->_em->find(CmsUser::class, $this->user->getId());\n    }\n\n    public function testRemoveContains(): void\n    {\n        $group1 = $this->user->groups[0];\n        $group2 = $this->user->groups[1];\n\n        self::assertTrue($this->user->groups->contains($group1));\n        self::assertTrue($this->user->groups->contains($group2));\n\n        $this->user->groups->removeElement($group1);\n        $this->user->groups->remove(1);\n\n        self::assertFalse($this->user->groups->contains($group1));\n        self::assertFalse($this->user->groups->contains($group2));\n    }\n\n    public function testClearCount(): void\n    {\n        $this->user->addGroup(new CmsGroup());\n        self::assertCount(3, $this->user->groups);\n\n        $this->user->groups->clear();\n\n        self::assertEquals(0, $this->user->groups->count());\n        self::assertCount(0, $this->user->groups);\n    }\n\n    public function testClearContains(): void\n    {\n        $group1 = $this->user->groups[0];\n        $group2 = $this->user->groups[1];\n\n        self::assertTrue($this->user->groups->contains($group1));\n        self::assertTrue($this->user->groups->contains($group2));\n\n        $this->user->groups->clear();\n\n        self::assertFalse($this->user->groups->contains($group1));\n        self::assertFalse($this->user->groups->contains($group2));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC881Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC881Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC881User::class,\n            DDC881PhoneNumber::class,\n            DDC881PhoneCall::class,\n        );\n    }\n\n    #[Group('DDC-117')]\n    #[Group('DDC-881')]\n    public function testIssue(): void\n    {\n        /* Create two test users: albert and alfons */\n        $albert = new DDC881User();\n        $albert->setName('albert');\n        $this->_em->persist($albert);\n\n        $alfons = new DDC881User();\n        $alfons->setName('alfons');\n        $this->_em->persist($alfons);\n\n        $this->_em->flush();\n\n        /* Assign two phone numbers to each user */\n        $phoneAlbert1 = new DDC881PhoneNumber();\n        $phoneAlbert1->setUser($albert);\n        $phoneAlbert1->setId(1);\n        $phoneAlbert1->setPhoneNumber('albert home: 012345');\n        $this->_em->persist($phoneAlbert1);\n\n        $phoneAlbert2 = new DDC881PhoneNumber();\n        $phoneAlbert2->setUser($albert);\n        $phoneAlbert2->setId(2);\n        $phoneAlbert2->setPhoneNumber('albert mobile: 67890');\n        $this->_em->persist($phoneAlbert2);\n\n        $phoneAlfons1 = new DDC881PhoneNumber();\n        $phoneAlfons1->setId(1);\n        $phoneAlfons1->setUser($alfons);\n        $phoneAlfons1->setPhoneNumber('alfons home: 012345');\n        $this->_em->persist($phoneAlfons1);\n\n        $phoneAlfons2 = new DDC881PhoneNumber();\n        $phoneAlfons2->setId(2);\n        $phoneAlfons2->setUser($alfons);\n        $phoneAlfons2->setPhoneNumber('alfons mobile: 67890');\n        $this->_em->persist($phoneAlfons2);\n\n        /* We call alfons and albert once on their mobile numbers */\n        $call1 = new DDC881PhoneCall();\n        $call1->setPhoneNumber($phoneAlfons2);\n        $this->_em->persist($call1);\n\n        $call2 = new DDC881PhoneCall();\n        $call2->setPhoneNumber($phoneAlbert2);\n        $this->_em->persist($call2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // fetch-join that foreign-key/primary-key entity association\n        $dql   = 'SELECT c, p FROM ' . DDC881PhoneCall::class . ' c JOIN c.phonenumber p';\n        $calls = $this->_em->createQuery($dql)->getResult();\n\n        self::assertCount(2, $calls);\n        self::assertFalse($this->isUninitializedObject($calls[0]->getPhoneNumber()));\n        self::assertFalse($this->isUninitializedObject($calls[1]->getPhoneNumber()));\n\n        $dql     = 'SELECT p, c FROM ' . DDC881PhoneNumber::class . ' p JOIN p.calls c';\n        $numbers = $this->_em->createQuery($dql)->getResult();\n\n        self::assertCount(2, $numbers);\n        self::assertInstanceOf(PersistentCollection::class, $numbers[0]->getCalls());\n        self::assertTrue($numbers[0]->getCalls()->isInitialized());\n    }\n}\n\n#[Entity]\nclass DDC881User\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $name = null;\n\n    /** @phpstan-var Collection<int, DDC881PhoneNumber> */\n    #[OneToMany(targetEntity: 'DDC881PhoneNumber', mappedBy: 'id')]\n    private $phoneNumbers;\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n\n#[Entity]\nclass DDC881PhoneNumber\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    private int|null $id = null;\n\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC881User', cascade: ['all'])]\n    private DDC881User|null $user = null;\n\n    #[Column(type: 'string', length: 255)]\n    private string|null $phonenumber = null;\n\n    /** @phpstan-var Collection<int, DDC881PhoneCall> */\n    #[OneToMany(targetEntity: 'DDC881PhoneCall', mappedBy: 'phonenumber')]\n    private $calls;\n\n    public function __construct()\n    {\n        $this->calls = new ArrayCollection();\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function setUser(DDC881User $user): void\n    {\n        $this->user = $user;\n    }\n\n    public function setPhoneNumber(string $phoneNumber): void\n    {\n        $this->phonenumber = $phoneNumber;\n    }\n\n    /** @phpstan-var Collection<int, DDC881PhoneCall> */\n    public function getCalls(): Collection\n    {\n        return $this->calls;\n    }\n}\n\n#[Entity]\nclass DDC881PhoneCall\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[JoinColumn(name: 'phonenumber_id', referencedColumnName: 'id')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'user_id')]\n    #[ManyToOne(targetEntity: 'DDC881PhoneNumber', inversedBy: 'calls', cascade: ['all'])]\n    private DDC881PhoneNumber|null $phonenumber = null;\n\n    #[Column(type: 'string', nullable: true)]\n    private string $callDate;\n\n    public function setPhoneNumber(DDC881PhoneNumber $phoneNumber): void\n    {\n        $this->phonenumber = $phoneNumber;\n    }\n\n    public function getPhoneNumber(): DDC881PhoneNumber\n    {\n        return $this->phonenumber;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC933Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\TransactionRequiredException;\nuse Doctrine\\Persistence\\Mapping\\MappingException;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Doctrine\\Tests\\TestUtil;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass DDC933Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-933')]\n    public function testLockCTIClass(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('It should not run on in-memory databases');\n        }\n\n        $manager = new CompanyManager();\n        $manager->setName('beberlei');\n        $manager->setSalary(1234);\n        $manager->setTitle('Vice President of This Test');\n        $manager->setDepartment('Foo');\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n\n        $this->_em->beginTransaction();\n        $this->_em->lock($manager, LockMode::PESSIMISTIC_READ);\n        $this->_em->rollback();\n\n        // if lock hasn't been released we'd have an exception here\n        $this->assertManagerCanBeUpdatedOnAnotherConnection($manager->getId(), 'Master of This Test');\n    }\n\n    /**\n     * @throws MappingException\n     * @throws ORMException\n     * @throws OptimisticLockException\n     * @throws TransactionRequiredException\n     */\n    private function assertManagerCanBeUpdatedOnAnotherConnection(int $id, string $newName): void\n    {\n        $em = $this->getEntityManager(TestUtil::getConnection());\n\n        $manager = $em->find(CompanyManager::class, $id);\n        assert($manager instanceof CompanyManager);\n        $manager->setName($newName);\n\n        $em->flush();\n        $em->clear();\n\n        self::assertSame($newName, $em->find(CompanyManager::class, $id)->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC949Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC949Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('generic');\n\n        parent::setUp();\n    }\n\n    #[Group('DDC-949')]\n    public function testBooleanThroughRepository(): void\n    {\n        $true               = new BooleanModel();\n        $true->booleanField = true;\n\n        $false               = new BooleanModel();\n        $false->booleanField = false;\n\n        $this->_em->persist($true);\n        $this->_em->persist($false);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $true  = $this->_em->getRepository(BooleanModel::class)->findOneBy(['booleanField' => true]);\n        $false = $this->_em->getRepository(BooleanModel::class)->findOneBy(['booleanField' => false]);\n\n        self::assertInstanceOf(BooleanModel::class, $true, 'True model not found');\n        self::assertTrue($true->booleanField, 'True Boolean Model should be true.');\n\n        self::assertInstanceOf(BooleanModel::class, $false, 'False model not found');\n        self::assertFalse($false->booleanField, 'False Boolean Model should be false.');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC960Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass DDC960Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(DDC960Root::class, DDC960Child::class);\n    }\n\n    #[Group('DDC-960')]\n    public function testUpdateRootVersion(): void\n    {\n        $child = new DDC960Child('Test');\n        $this->_em->persist($child);\n        $this->_em->flush();\n\n        $child->setName('Test2');\n\n        $this->_em->flush();\n\n        self::assertEquals(2, $child->getVersion());\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap(['root' => 'DDC960Root', 'child' => 'DDC960Child'])]\nclass DDC960Root\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'integer')]\n    #[Version]\n    private int $version;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getVersion(): int\n    {\n        return $this->version;\n    }\n}\n\n#[Entity]\nclass DDC960Child extends DDC960Root\n{\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        private string $name,\n    ) {\n    }\n\n    public function setName($name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/DDC992Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-992')]\nclass DDC992Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC992Role::class,\n            DDC992Parent::class,\n            DDC992Child::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $role        = new DDC992Role();\n        $role->name  = 'Parent';\n        $child       = new DDC992Role();\n        $child->name = 'child';\n\n        $role->extendedBy[] = $child;\n        $child->extends[]   = $role;\n\n        $this->_em->persist($role);\n        $this->_em->persist($child);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $child = $this->_em->getRepository($role::class)->find($child->roleID);\n        self::assertCount(1, $child->extends);\n        foreach ($child->extends as $parent) {\n            self::assertEquals($role->getRoleID(), $parent->getRoleID());\n        }\n    }\n\n    public function testOneToManyChild(): void\n    {\n        $parent           = new DDC992Parent();\n        $child            = new DDC992Child();\n        $child->parent    = $parent;\n        $parent->childs[] = $child;\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $parentRepository = $this->_em->getRepository($parent::class);\n        $childRepository  = $this->_em->getRepository($child::class);\n\n        $parent = $parentRepository->find($parent->id);\n        self::assertCount(1, $parent->childs);\n        self::assertCount(0, $parent->childs[0]->childs());\n\n        $child = $parentRepository->findOneBy(['id' => $child->id]);\n        self::assertSame($parent->childs[0], $child);\n\n        $this->_em->clear();\n\n        $child = $parentRepository->find($child->id);\n        self::assertCount(0, $child->childs);\n\n        $this->_em->clear();\n\n        $child = $childRepository->find($child->id);\n        self::assertCount(0, $child->childs);\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap(['child' => 'DDC992Child', 'parent' => 'DDC992Parent'])]\nclass DDC992Parent\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC992Parent */\n    #[ManyToOne(targetEntity: 'DDC992Parent', inversedBy: 'childs')]\n    public $parent;\n\n    /** @var Collection<int, DDC992Child> */\n    #[OneToMany(targetEntity: 'DDC992Child', mappedBy: 'parent')]\n    public $childs;\n}\n\n#[Entity]\nclass DDC992Child extends DDC992Parent\n{\n    public function childs(): Collection\n    {\n        return $this->childs;\n    }\n}\n\n#[Entity]\nclass DDC992Role\n{\n    public function getRoleID(): int\n    {\n        return $this->roleID;\n    }\n\n    /** @var int */\n    #[Id]\n    #[Column(name: 'roleID', type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $roleID;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 45)]\n    public $name;\n\n    /** @phpstan-var Collection<int, DDC992Role> */\n    #[ManyToMany(targetEntity: 'DDC992Role', mappedBy: 'extends')]\n    public $extendedBy;\n\n    /** @phpstan-var Collection<int, DDC992Role> */\n    #[JoinTable(name: 'RoleRelations')]\n    #[JoinColumn(name: 'roleID', referencedColumnName: 'roleID')]\n    #[InverseJoinColumn(name: 'extendsRoleID', referencedColumnName: 'roleID')]\n    #[ManyToMany(targetEntity: 'DDC992Role', inversedBy: 'extendedBy')]\n    public $extends;\n\n    public function __construct()\n    {\n        $this->extends    = new ArrayCollection();\n        $this->extendedBy = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10049/GH10049Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10049;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\n\nclass GH10049Test extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            ReadOnlyPropertyOwner::class,\n            ReadOnlyPropertyInheritor::class,\n        );\n    }\n\n    #[DoesNotPerformAssertions]\n    public function testInheritedReadOnlyPropertyValueCanBeSet(): void\n    {\n        $child = new ReadOnlyPropertyInheritor(10049);\n        $this->_em->persist($child);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->_em->find(ReadOnlyPropertyInheritor::class, 10049);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyInheritor.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10049;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass ReadOnlyPropertyInheritor extends ReadOnlyPropertyOwner\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyOwner.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10049;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\MappedSuperclass]\nabstract class ReadOnlyPropertyOwner\n{\n    public function __construct(\n        #[ORM\\Id]\n        #[ORM\\Column(type: 'integer')]\n        public readonly int $id,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10132Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\nuse Doctrine\\Tests\\Models\\GH10132\\Complex;\nuse Doctrine\\Tests\\Models\\GH10132\\ComplexChild;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH10132Test extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            Complex::class,\n            ComplexChild::class,\n        );\n    }\n\n    public function testQueryBackedEnumInCompositeKeyJoin(): void\n    {\n        $complex = new Complex();\n        $complex->setType(Suit::Clubs);\n\n        $complexChild = new ComplexChild();\n        $complexChild->setComplex($complex);\n\n        $this->_em->persist($complex);\n        $this->_em->persist($complexChild);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $qb = $this->_em->createQueryBuilder();\n        $qb->select('s')\n            ->from(ComplexChild::class, 's')\n            ->where('s.complexType = :complexType');\n\n        $qb->setParameter('complexType', Suit::Clubs);\n\n        self::assertNotNull($qb->getQuery()->getOneOrNullResult());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10288Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\Tests\\Models\\GH10288\\GH10288People;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * This test makes sure that Discriminator columns can use both custom types using PHP enums as well as\n * enumType definition of enums.\n */\nclass GH10288Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (Type::hasType(GH10288PeopleType::NAME)) {\n            Type::overrideType(GH10288PeopleType::NAME, GH10288PeopleType::class);\n        } else {\n            Type::addType(GH10288PeopleType::NAME, GH10288PeopleType::class);\n        }\n\n        $this->createSchemaForModels(\n            GH10288PersonWithEnumType::class,\n            GH10288BossWithEnumType::class,\n            GH10288EmployeeWithEnumType::class,\n            GH10288PersonCustomEnumType::class,\n            GH10288BossCustomEnumType::class,\n            GH10288EmployeeCustomEnumType::class,\n        );\n    }\n\n    /** @param class-string $personType */\n    private function performEnumDiscriminatorTest(\n        GH10288BossWithEnumType|GH10288BossCustomEnumType $boss,\n        GH10288EmployeeWithEnumType|GH10288EmployeeCustomEnumType $employee,\n        string $personType,\n    ): void {\n        $boss->bossId = 1;\n\n        $this->_em->persist($boss);\n        $this->_em->persist($employee);\n        $this->_em->flush();\n        $bossId = $boss->id;\n        $this->_em->clear();\n\n        // Using DQL here to make sure that we'll use ObjectHydrator instead of SimpleObjectHydrator\n        $query = $this->_em->createQueryBuilder()\n            ->select('person')\n            ->from($personType, 'person')\n            ->where('person.name = :name')\n            ->setMaxResults(1)\n            ->getQuery();\n\n        $query->setParameter('name', 'John');\n\n        self::assertEquals($boss, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT));\n        self::assertEquals(\n            GH10288People::BOSS,\n            $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'],\n        );\n\n        $query->setParameter('name', 'Bob');\n        self::assertEquals($employee, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT));\n        self::assertEquals(\n            GH10288People::EMPLOYEE,\n            $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'],\n        );\n\n        $this->_em->clear();\n\n        // test SimpleObjectHydrator\n        $bossFetched = $this->_em->find($personType, $bossId);\n        self::assertEquals($boss, $bossFetched);\n    }\n\n    #[Group('GH10288')]\n    public function testEnumDiscriminatorWithEnumType(): void\n    {\n        $boss     = new GH10288BossWithEnumType('John');\n        $employee = new GH10288EmployeeWithEnumType('Bob');\n\n        $this->performEnumDiscriminatorTest($boss, $employee, GH10288PersonWithEnumType::class);\n    }\n\n    #[Group('GH10288')]\n    public function testEnumDiscriminatorWithCustomEnumType(): void\n    {\n        $boss     = new GH10288BossCustomEnumType('John');\n        $employee = new GH10288EmployeeCustomEnumType('Bob');\n\n        $this->performEnumDiscriminatorTest($boss, $employee, GH10288PersonCustomEnumType::class);\n    }\n}\n\nclass GH10288PeopleType extends StringType\n{\n    public const NAME = 'GH10288PeopleType';\n\n    public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string\n    {\n        if (! $value instanceof GH10288People) {\n            $value = GH10288People::from($value);\n        }\n\n        return $value->value;\n    }\n\n    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): GH10288People\n    {\n        return GH10288People::from($value);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', enumType: GH10288People::class)]\n#[DiscriminatorMap([\n    'boss'     => GH10288BossWithEnumType::class,\n    'employee' => GH10288EmployeeWithEnumType::class,\n])]\nabstract class GH10288PersonWithEnumType\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int|null $id = null;\n\n    public function __construct(\n        #[Column(length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH10288BossWithEnumType extends GH10288PersonWithEnumType\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $bossId;\n}\n\n#[Entity]\nclass GH10288EmployeeWithEnumType extends GH10288PersonWithEnumType\n{\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'GH10288PeopleType')]\n#[DiscriminatorMap([\n    'boss'     => GH10288BossCustomEnumType::class,\n    'employee' => GH10288EmployeeCustomEnumType::class,\n])]\nabstract class GH10288PersonCustomEnumType\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    public function __construct(string $name)\n    {\n        $this->name = $name;\n    }\n}\n\n#[Entity]\nclass GH10288BossCustomEnumType extends GH10288PersonCustomEnumType\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $bossId;\n}\n\n#[Entity]\nclass GH10288EmployeeCustomEnumType extends GH10288PersonCustomEnumType\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10334Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\GH10334\\GH10334Foo;\nuse Doctrine\\Tests\\Models\\GH10334\\GH10334FooCollection;\nuse Doctrine\\Tests\\Models\\GH10334\\GH10334Product;\nuse Doctrine\\Tests\\Models\\GH10334\\GH10334ProductType;\nuse Doctrine\\Tests\\Models\\GH10334\\GH10334ProductTypeId;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH10334Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH10334FooCollection::class, GH10334Foo::class, GH10334ProductType::class, GH10334Product::class]);\n    }\n\n    public function testTicket(): void\n    {\n        $collection = new GH10334FooCollection();\n        $foo        = new GH10334Foo($collection, GH10334ProductTypeId::Jean);\n        $foo2       = new GH10334Foo($collection, GH10334ProductTypeId::Short);\n\n        $this->_em->persist($collection);\n        $this->_em->persist($foo);\n        $this->_em->persist($foo2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $result = $this->_em\n            ->getRepository(GH10334FooCollection::class)\n            ->createQueryBuilder('collection')\n            ->leftJoin('collection.foos', 'foo')->addSelect('foo')\n            ->getQuery()\n            ->getResult();\n\n        $this->_em\n            ->getRepository(GH10334FooCollection::class)\n            ->createQueryBuilder('collection')\n            ->leftJoin('collection.foos', 'foo')->addSelect('foo')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertCount(1, $result);\n        $this->assertCount(2, $result[0]->getFoos());\n    }\n\n    public function testGetChildWithBackedEnumId(): void\n    {\n        $jean    = new GH10334ProductType(GH10334ProductTypeId::Jean, 23.5);\n        $short   = new GH10334ProductType(GH10334ProductTypeId::Short, 45.2);\n        $product = new GH10334Product('Extra Large Blue', $jean);\n\n        $jean->addProduct($product);\n\n        $this->_em->persist($jean);\n        $this->_em->persist($short);\n        $this->_em->persist($product);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity = $this->_em->find(GH10334Product::class, 1);\n\n        self::assertNotNull($entity);\n        self::assertSame($entity->getProductType()->getId(), GH10334ProductTypeId::Jean);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10336Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\GH10336\\GH10336Entity;\nuse Doctrine\\Tests\\Models\\GH10336\\GH10336Relation;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH10336Test extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10336Entity::class,\n            GH10336Relation::class,\n        );\n    }\n\n    public function testCanAccessRelationPropertyAfterClear(): void\n    {\n        $relation         = new GH10336Relation();\n        $relation->value  = 'foo';\n        $entity           = new GH10336Entity();\n        $entity->relation = $relation;\n\n        $this->_em->persist($entity);\n        $this->_em->persist($relation);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $entity = $this->_em->find(GH10336Entity::class, 1);\n\n        $this->_em->clear();\n\n        $this->assertSame('foo', $entity->relation->value);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10348Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH10348Test extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10348Person::class,\n            GH10348Company::class,\n        ]);\n    }\n\n    public function testTheORMRemovesReferencedEmployeeBeforeReferencingEmployee(): void\n    {\n        $person1         = new GH10348Person();\n        $person2         = new GH10348Person();\n        $person2->mentor = $person1;\n\n        $company = new GH10348Company();\n        $company->addEmployee($person1)->addEmployee($person2);\n\n        $this->_em->persist($company);\n        $this->_em->flush();\n\n        $company = $this->_em->find(GH10348Company::class, $company->id);\n\n        $this->_em->remove($company);\n        $this->_em->flush();\n\n        self::assertEmpty($this->_em->createQuery('SELECT c FROM ' . GH10348Company::class . ' c')->getResult());\n        self::assertEmpty($this->_em->createQuery('SELECT p FROM ' . GH10348Person::class . ' p')->getResult());\n    }\n}\n\n#[ORM\\Entity]\nclass GH10348Person\n{\n    /** @var ?int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id = null;\n\n    /** @var ?GH10348Company */\n    #[ORM\\ManyToOne(targetEntity: GH10348Company::class, inversedBy: 'employees')]\n    public $employer = null;\n\n    /** @var ?GH10348Person */\n    #[ORM\\ManyToOne(targetEntity: self::class, cascade: ['remove'])]\n    public $mentor = null;\n}\n\n#[ORM\\Entity]\nclass GH10348Company\n{\n    /** @var ?int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id = null;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(mappedBy: 'employer', targetEntity: GH10348Person::class, cascade: ['persist', 'remove'])]\n    private $employees;\n\n    public function __construct()\n    {\n        $this->employees = new ArrayCollection();\n    }\n\n    public function addEmployee(GH10348Person $person): self\n    {\n        $person->employer = $this;\n        $this->employees->add($person);\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10387Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_map;\n\n#[Group('GH-10387')]\nclass GH10387Test extends OrmTestCase\n{\n    #[DataProvider('classHierachies')]\n    public function testSchemaToolCreatesColumnForFieldInTheMiddleClass(array $classes): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $metadata   = array_map(static function (string $class) use ($em) {\n            return $em->getClassMetadata($class);\n        }, $classes);\n        $schema     = $schemaTool->getSchemaFromMetadata([$metadata[0]]);\n\n        self::assertNotNull($schema->getTable('root')->getColumn('middle_class_field'));\n        self::assertNotNull($schema->getTable('root')->getColumn('leaf_class_field'));\n    }\n\n    public static function classHierachies(): Generator\n    {\n        yield 'hierarchy with Entity classes only' => [[GH10387EntitiesOnlyRoot::class, GH10387EntitiesOnlyMiddle::class, GH10387EntitiesOnlyLeaf::class]];\n        yield 'MappedSuperclass in the middle of the hierarchy' => [[GH10387MappedSuperclassRoot::class, GH10387MappedSuperclassMiddle::class, GH10387MappedSuperclassLeaf::class]];\n        yield 'abstract entity at the root and in the middle of the hierarchy' => [[GH10387AbstractEntitiesRoot::class, GH10387AbstractEntitiesMiddle::class, GH10387AbstractEntitiesLeaf::class]];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'root')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap(['A' => GH10387EntitiesOnlyRoot::class, 'B' => GH10387EntitiesOnlyMiddle::class, 'C' => GH10387EntitiesOnlyLeaf::class])]\nclass GH10387EntitiesOnlyRoot\n{\n    /** @var string */\n    #[ORM\\Id]\n    #[ORM\\Column]\n    private $id;\n}\n\n#[ORM\\Entity]\nclass GH10387EntitiesOnlyMiddle extends GH10387EntitiesOnlyRoot\n{\n    /** @var string */\n    #[ORM\\Column(name: 'middle_class_field')]\n    private $parentValue;\n}\n\n#[ORM\\Entity]\nclass GH10387EntitiesOnlyLeaf extends GH10387EntitiesOnlyMiddle\n{\n    /** @var string */\n    #[ORM\\Column(name: 'leaf_class_field')]\n    private $childValue;\n}\n\n/** ↓ This DiscriminatorMap contains the Entity classes only, not the Mapped Superclass */\n#[ORM\\DiscriminatorMap(['A' => GH10387MappedSuperclassRoot::class, 'B' => GH10387MappedSuperclassLeaf::class])]\n#[ORM\\Entity]\n#[ORM\\Table(name: 'root')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\nclass GH10387MappedSuperclassRoot\n{\n    /** @var string */\n    #[ORM\\Id]\n    #[ORM\\Column]\n    private $id;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10387MappedSuperclassMiddle extends GH10387MappedSuperclassRoot\n{\n    /** @var string */\n    #[ORM\\Column(name: 'middle_class_field')]\n    private $parentValue;\n}\n\n#[ORM\\Entity]\nclass GH10387MappedSuperclassLeaf extends GH10387MappedSuperclassMiddle\n{\n    /** @var string */\n    #[ORM\\Column(name: 'leaf_class_field')]\n    private $childValue;\n}\n\n\n/** ↓ This DiscriminatorMap contains the single non-abstract Entity class only */\n#[ORM\\DiscriminatorMap(['A' => GH10387AbstractEntitiesLeaf::class])]\n#[ORM\\Entity]\n#[ORM\\Table(name: 'root')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\nabstract class GH10387AbstractEntitiesRoot\n{\n    /** @var string */\n    #[ORM\\Id]\n    #[ORM\\Column]\n    private $id;\n}\n\n#[ORM\\Entity]\nabstract class GH10387AbstractEntitiesMiddle extends GH10387AbstractEntitiesRoot\n{\n    /** @var string */\n    #[ORM\\Column(name: 'middle_class_field')]\n    private $parentValue;\n}\n\n#[ORM\\Entity]\nclass GH10387AbstractEntitiesLeaf extends GH10387AbstractEntitiesMiddle\n{\n    /** @var string */\n    #[ORM\\Column(name: 'leaf_class_field')]\n    private $childValue;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10450Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass GH10450Test extends OrmTestCase\n{\n    /** @param class-string $className */\n    #[DataProvider('classesThatOverrideFieldNames')]\n    public function testDuplicatePrivateFieldsShallBeRejected(string $className): void\n    {\n        $em = $this->getTestEntityManager();\n\n        $this->expectException(MappingException::class);\n\n        $em->getClassMetadata($className);\n    }\n\n    public static function classesThatOverrideFieldNames(): Generator\n    {\n        yield 'Entity class that redeclares a private field inherited from a base entity' => [GH10450EntityChildPrivate::class];\n        yield 'Entity class that redeclares a private field inherited from a mapped superclass' => [GH10450MappedSuperclassChildPrivate::class];\n        yield 'Entity class that redeclares a protected field inherited from a base entity' => [GH10450EntityChildProtected::class];\n        yield 'Entity class that redeclares a protected field inherited from a mapped superclass' => [GH10450MappedSuperclassChildProtected::class];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorMap(['base' => GH10450BaseEntityPrivate::class, 'child' => GH10450EntityChildPrivate::class])]\n#[ORM\\DiscriminatorColumn(name: 'type')]\nclass GH10450BaseEntityPrivate\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    private int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    private string $field;\n}\n\n#[ORM\\Entity]\nclass GH10450EntityChildPrivate extends GH10450BaseEntityPrivate\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    private string $field;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10450BaseMappedSuperclassPrivate\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    private int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    private string $field;\n}\n\n#[ORM\\Entity]\nclass GH10450MappedSuperclassChildPrivate extends GH10450BaseMappedSuperclassPrivate\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    private string $field;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorMap(['base' => GH10450BaseEntityProtected::class, 'child' => GH10450EntityChildProtected::class])]\n#[ORM\\DiscriminatorColumn(name: 'type')]\nclass GH10450BaseEntityProtected\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    protected int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    protected string $field;\n}\n\n#[ORM\\Entity]\nclass GH10450EntityChildProtected extends GH10450BaseEntityProtected\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    protected string $field;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10450BaseMappedSuperclassProtected\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    protected int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    protected string $field;\n}\n\n#[ORM\\Entity]\nclass GH10450MappedSuperclassChildProtected extends GH10450BaseMappedSuperclassProtected\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    protected string $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10454Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass GH10454Test extends OrmTestCase\n{\n    /** @param class-string $className */\n    #[DataProvider('classesThatOverrideFieldNames')]\n    public function testProtectedPropertyMustNotBeInheritedAndReconfigured(string $className): void\n    {\n        $em = $this->getTestEntityManager();\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessageMatches('/Property \"field\" .* was already declared, but it must be declared only once/');\n\n        $em->getClassMetadata($className);\n    }\n\n    public static function classesThatOverrideFieldNames(): Generator\n    {\n        yield 'Entity class that redeclares a protected field inherited from a base entity' => [GH10454EntityChildProtected::class];\n        yield 'Entity class that redeclares a protected field inherited from a mapped superclass' => [GH10454MappedSuperclassChildProtected::class];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorMap(['base' => GH10454BaseEntityProtected::class, 'child' => GH10454EntityChildProtected::class])]\n#[ORM\\DiscriminatorColumn(name: 'type')]\nclass GH10454BaseEntityProtected\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    protected int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    protected string $field;\n}\n\n#[ORM\\Entity]\nclass GH10454EntityChildProtected extends GH10454BaseEntityProtected\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    protected string $field;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10454BaseMappedSuperclassProtected\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    protected int $id;\n\n    #[ORM\\Column(type: 'text', name: 'base')]\n    protected string $field;\n}\n\n#[ORM\\Entity]\nclass GH10454MappedSuperclassChildProtected extends GH10454BaseMappedSuperclassProtected\n{\n    #[ORM\\Column(type: 'text', name: 'child')]\n    protected string $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10462Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function method_exists;\n\nclass GH10462Test extends OrmFunctionalTestCase\n{\n    public function testCharsetAndCollationOptionsOnDiscriminatedColumn(): void\n    {\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof MySQLPlatform) {\n            self::markTestSkipped('This test is useful for all databases, but designed only for mysql.');\n        }\n\n        if (method_exists(AbstractPlatform::class, 'getGuidExpression')) {\n            self::markTestSkipped('Test valid for doctrine/dbal:3.x only.');\n        }\n\n        $this->createSchemaForModels(GH10462Person::class, GH10462Employee::class);\n        $schemaManager = $this->createSchemaManager();\n        $personOptions = $schemaManager->introspectTable('gh10462_person')->getColumn('discr')->toArray();\n        self::assertSame('ascii', $personOptions['charset']);\n        self::assertSame('ascii_general_ci', $personOptions['collation']);\n    }\n}\n\n#[Entity]\n#[Table(name: 'gh10462_person')]\n#[InheritanceType(value: 'SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n#[DiscriminatorMap(['person' => GH10462Person::class, 'employee' => GH10462Employee::class])]\nclass GH10462Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH10462Employee extends GH10462Person\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10473Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Tools\\ResolveTargetEntityListener;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass GH10473Test extends OrmTestCase\n{\n    public function testMappedSuperclassAssociationsCanBeResolvedToEntities(): void\n    {\n        $em = $this->getTestEntityManager();\n\n        $resolveTargetEntity = new ResolveTargetEntityListener();\n\n        $resolveTargetEntity->addResolveTargetEntity(\n            GH10473BaseUser::class,\n            GH10473UserImplementation::class,\n            [],\n        );\n\n        $em->getEventManager()->addEventSubscriber($resolveTargetEntity);\n\n        $userMetadata = $em->getClassMetadata(GH10473UserImplementation::class);\n\n        self::assertFalse($userMetadata->isMappedSuperclass);\n        self::assertTrue($userMetadata->isInheritanceTypeNone());\n\n        $socialMediaAccountsMapping = $userMetadata->getAssociationMapping('socialMediaAccounts');\n        self::assertNull($socialMediaAccountsMapping->inherited);\n        self::assertTrue($socialMediaAccountsMapping->isToMany());\n        self::assertFalse($socialMediaAccountsMapping->isOwningSide());\n        self::assertSame(GH10473SocialMediaAccount::class, $socialMediaAccountsMapping->targetEntity);\n        self::assertSame('user', $socialMediaAccountsMapping->mappedBy);\n\n        $createdByMapping = $userMetadata->getAssociationMapping('createdBy');\n        self::assertNull($createdByMapping->inherited);\n        self::assertTrue($createdByMapping->isToOne());\n        self::assertTrue($createdByMapping->isOwningSide());\n        self::assertSame(GH10473UserImplementation::class, $createdByMapping->targetEntity);\n        self::assertSame('createdUsers', $createdByMapping->inversedBy);\n\n        $createdUsersMapping = $userMetadata->getAssociationMapping('createdUsers');\n        self::assertNull($createdUsersMapping->inherited);\n        self::assertTrue($createdUsersMapping->isToMany());\n        self::assertFalse($createdUsersMapping->isOwningSide());\n        self::assertSame(GH10473UserImplementation::class, $createdUsersMapping->targetEntity);\n        self::assertSame('createdBy', $createdUsersMapping->mappedBy);\n\n        $socialMediaAccountMetadata = $em->getClassMetadata(GH10473SocialMediaAccount::class);\n\n        self::assertFalse($socialMediaAccountMetadata->isMappedSuperclass);\n        self::assertTrue($socialMediaAccountMetadata->isInheritanceTypeNone());\n\n        $userMapping = $socialMediaAccountMetadata->getAssociationMapping('user');\n        self::assertNull($userMapping->inherited);\n        self::assertTrue($userMapping->isToOne());\n        self::assertTrue($userMapping->isOwningSide());\n        self::assertSame(GH10473UserImplementation::class, $userMapping->targetEntity);\n        self::assertSame('socialMediaAccounts', $userMapping->inversedBy);\n    }\n}\n\n#[ORM\\MappedSuperclass]\nabstract class GH10473BaseUser\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    private $id;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(targetEntity: GH10473SocialMediaAccount::class, mappedBy: 'user')]\n    private $socialMediaAccounts;\n\n    /** @var GH10473BaseUser */\n    #[ORM\\ManyToOne(targetEntity: self::class, inversedBy: 'createdUsers')]\n    private $createdBy;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(targetEntity: self::class, mappedBy: 'createdBy')]\n    private $createdUsers;\n}\n\n#[ORM\\Entity]\nclass GH10473SocialMediaAccount\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    private $id;\n\n    /** @var GH10473BaseUser */\n    #[ORM\\ManyToOne(targetEntity: GH10473BaseUser::class, inversedBy: 'socialMediaAccounts')]\n    private $user;\n}\n\n#[ORM\\Entity]\nclass GH10473UserImplementation extends GH10473BaseUser\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10531Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH10531Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10531A::class,\n            GH10531B::class,\n        );\n    }\n\n    public function tearDown(): void\n    {\n        $conn = static::$sharedConn;\n        $conn->executeStatement('DELETE FROM gh10531_b');\n        $conn->executeStatement('DELETE FROM gh10531_a');\n    }\n\n    public function testInserts(): void\n    {\n        $a  = new GH10531A();\n        $b1 = new GH10531B();\n        $b2 = new GH10531B();\n        $b3 = new GH10531B();\n\n        $b1->parent = $b2;\n        $b3->parent = $b2;\n        $b2->parent = $a;\n\n        /*\n         * The following would force a working commit order, but that's not what\n         * we want (the ORM shall sort this out internally).\n         *\n         * $this->_em->persist($a);\n         * $this->_em->persist($b2);\n         * $this->_em->flush();\n         * $this->_em->persist($b1);\n         * $this->_em->persist($b3);\n         * $this->_em->flush();\n         */\n\n        // Pass $b2 to persist() between $b1 and $b3, so that any potential reliance upon the\n        // order of persist() calls is spotted: No matter if it is in the order that persist()\n        // was called or the other way round, in both cases there is an entity that will come\n        // \"before\" $b2 but depend on its primary key, so the ORM must re-order the inserts.\n\n        $this->_em->persist($a);\n        $this->_em->persist($b1);\n        $this->_em->persist($b2);\n        $this->_em->persist($b3);\n        $this->_em->flush();\n\n        self::assertNotNull($a->id);\n        self::assertNotNull($b1->id);\n        self::assertNotNull($b2->id);\n        self::assertNotNull($b3->id);\n    }\n\n    public function testDeletes(): void\n    {\n        $this->expectNotToPerformAssertions();\n        $con = $this->_em->getConnection();\n\n        // The \"a\" entity\n        $con->insert('gh10531_a', ['id' => 1, 'discr' => 'A']);\n        $a = $this->_em->find(GH10531A::class, 1);\n\n        // The \"b2\" entity\n        $con->insert('gh10531_a', ['id' => 2, 'discr' => 'B']);\n        $con->insert('gh10531_b', ['id' => 2, 'parent_id' => 1]);\n        $b2 = $this->_em->find(GH10531B::class, 2);\n\n        // The \"b1\" entity\n        $con->insert('gh10531_a', ['id' => 3, 'discr' => 'B']);\n        $con->insert('gh10531_b', ['id' => 3, 'parent_id' => 2]);\n        $b1 = $this->_em->find(GH10531B::class, 3);\n\n        // The \"b3\" entity\n        $con->insert('gh10531_a', ['id' => 4, 'discr' => 'B']);\n        $con->insert('gh10531_b', ['id' => 4, 'parent_id' => 2]);\n        $b3 = $this->_em->find(GH10531B::class, 4);\n\n        /*\n         * The following would make the deletions happen in an order\n         * where the not-nullable foreign key constraints would not be\n         * violated. But, we want the ORM to be able to sort this out\n         * internally.\n         *\n         * $this->_em->remove($b1);\n         * $this->_em->remove($b3);\n         * $this->_em->remove($b2);\n         */\n\n        // As before, put $b2 in between $b1 and $b3 so that the order of the\n        // remove() calls alone (in either direction) does not solve the problem.\n        // The ORM will have to sort $b2 to be deleted last, after $b1 and $b3.\n        $this->_em->remove($b1);\n        $this->_em->remove($b2);\n        $this->_em->remove($b3);\n\n        $this->_em->flush();\n    }\n}\n\n/**\n * We are using JTI here, since STI would relax the not-nullable constraint for the \"parent\"\n * column (it has to be NULL when the row contains a GH10531A instance). Causes another error,\n * but not the constraint violation I'd like to point out.\n */\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10531_a')]\n#[ORM\\DiscriminatorColumn(name: 'discr', type: 'string')]\n#[ORM\\DiscriminatorMap(['A' => GH10531A::class, 'B' => GH10531B::class])]\n#[ORM\\InheritanceType('JOINED')]\nclass GH10531A\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10531_b')]\nclass GH10531B extends GH10531A\n{\n    /** @var GH10531A */\n    #[ORM\\ManyToOne(targetEntity: GH10531A::class)]\n    #[ORM\\JoinColumn(nullable: false, name: 'parent_id')]\n    public $parent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10532Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH10532Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10532A::class,\n            GH10532B::class,\n            GH10532C::class,\n            GH10532X::class,\n        );\n    }\n\n    public function tearDown(): void\n    {\n        $conn = static::$sharedConn;\n        $conn->executeStatement('DELETE FROM gh10532_c');\n        $conn->executeStatement('DELETE FROM gh10532_b');\n        $conn->executeStatement('DELETE FROM gh10532_a');\n        $conn->executeStatement('DELETE FROM gh10532_x');\n    }\n\n    public function testInserts(): void\n    {\n        // Dependencies are $a1 -> $b -> $a2 -> $c\n\n        $a1 = new GH10532A();\n        $b  = new GH10532B();\n        $a2 = new GH10532A();\n        $c  = new GH10532C();\n\n        $a1->x = $b;\n        $b->a  = $a2;\n        $a2->x = $c;\n\n        /*\n         * The following would force a working commit order, but that's not what\n         * we want (the ORM shall sort this out internally).\n         *\n         * $this->_em->persist($c);\n         * $this->_em->persist($a2);\n         * $this->_em->flush();\n         * $this->_em->persist($b);\n         * $this->_em->persist($a1);\n         * $this->_em->flush();\n         */\n\n        $this->_em->persist($a1);\n        $this->_em->persist($a2);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->flush();\n\n        self::assertNotNull($a1->id);\n        self::assertNotNull($b->id);\n        self::assertNotNull($a2->id);\n        self::assertNotNull($c->id);\n    }\n\n    public function testDeletes(): void\n    {\n        // Dependencies are $a1 -> $b -> $a2 -> $c\n\n        $this->expectNotToPerformAssertions();\n        $con = $this->_em->getConnection();\n\n        // The \"c\" entity\n        $con->insert('gh10532_x', ['id' => 1, 'discr' => 'C']);\n        $con->insert('gh10532_c', ['id' => 1]);\n        $c = $this->_em->find(GH10532C::class, 1);\n\n        // The \"a2\" entity\n        $con->insert('gh10532_a', ['id' => 2, 'gh10532x_id' => 1]);\n        $a2 = $this->_em->find(GH10532A::class, 2);\n\n        // The \"b\" entity\n        $con->insert('gh10532_x', ['id' => 3, 'discr' => 'B']);\n        $con->insert('gh10532_b', ['id' => 3, 'gh10532a_id' => 2]);\n        $b = $this->_em->find(GH10532B::class, 3);\n\n        // The \"a1\" entity\n        $con->insert('gh10532_a', ['id' => 4, 'gh10532x_id' => 3]);\n        $a1 = $this->_em->find(GH10532A::class, 4);\n\n        /*\n         * The following would make the deletions happen in an order\n         * where the not-nullable foreign key constraints would not be\n         * violated. But, we want the ORM to be able to sort this out\n         * internally.\n         *\n         * $this->_em->remove($a1);\n         * $this->_em->flush();\n         * $this->_em->remove($b);\n         * $this->_em->flush();\n         * $this->_em->remove($a2);\n         * $this->_em->remove($c);\n         * $this->_em->flush();\n         */\n\n        $this->_em->remove($a1);\n        $this->_em->remove($a2);\n        $this->_em->remove($b);\n        $this->_em->remove($c);\n\n        $this->_em->flush();\n    }\n}\n\n/**\n * We are using JTI here, since STI would relax the not-nullable constraint for the \"parent\"\n * column. Causes another error, but not the constraint violation I'd like to point out.\n */\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10532_x')]\n#[ORM\\DiscriminatorColumn(name: 'discr', type: 'string')]\n#[ORM\\DiscriminatorMap(['B' => GH10532B::class, 'C' => GH10532C::class])]\n#[ORM\\InheritanceType('JOINED')]\nabstract class GH10532X\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10532_b')]\nclass GH10532B extends GH10532X\n{\n    /** @var GH10532A */\n    #[ORM\\ManyToOne(targetEntity: GH10532A::class)]\n    #[ORM\\JoinColumn(nullable: false, name: 'gh10532a_id')]\n    public $a;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10532_c')]\nclass GH10532C extends GH10532X\n{\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10532_a')]\nclass GH10532A\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var GH10532X */\n    #[ORM\\ManyToOne(targetEntity: GH10532X::class)]\n    #[ORM\\JoinColumn(nullable: false, name: 'gh10532x_id')]\n    public $x;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10566Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nuse function is_a;\n\nclass GH10566Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10566A::class,\n            GH10566B::class,\n            GH10566C::class,\n        );\n    }\n\n    #[DataProvider('provideEntityClasses')]\n    public function testInsertion(string $startEntityClass): void\n    {\n        $a = new GH10566A();\n        $b = new GH10566B();\n        $c = new GH10566C();\n\n        $a->other = $b;\n        $b->other = $c;\n        $c->other = $a;\n\n        foreach ([$a, $b, $c] as $candidate) {\n            if (is_a($candidate, $startEntityClass)) {\n                $this->_em->persist($candidate);\n            }\n        }\n\n        // Since all associations are nullable, the ORM has no problem finding an insert order,\n        // it can always schedule \"deferred updates\" to fill missing foreign key values.\n        $this->_em->flush();\n\n        self::assertNotNull($a->id);\n        self::assertNotNull($b->id);\n        self::assertNotNull($c->id);\n    }\n\n    #[DataProvider('provideEntityClasses')]\n    public function testRemoval(string $startEntityClass): void\n    {\n        $a = new GH10566A();\n        $b = new GH10566B();\n        $c = new GH10566C();\n\n        $a->other = $b;\n        $b->other = $c;\n        $c->other = $a;\n\n        $this->_em->persist($a);\n        $this->_em->flush();\n\n        $aId = $a->id;\n        $bId = $b->id;\n        $cId = $c->id;\n\n        // In the removal case, the ORM currently does not schedule \"extra updates\"\n        // to break association cycles before entities are removed. So, we must not\n        // look at \"nullable\" for associations to find a delete commit order.\n        //\n        // To make it work, the user needs to have a database-level \"ON DELETE SET NULL\"\n        // on an association. That's where the cycle can be broken. Commit order computation\n        // for the removal case needs to look at this property.\n        //\n        // In this example, only A -> B can be used to break the cycle. So, regardless which\n        // entity we start with, the ORM-level cascade will always remove all three entities,\n        // and the order of database deletes always has to be (can only be) from B, then C, then A.\n\n        foreach ([$a, $b, $c] as $candidate) {\n            if (is_a($candidate, $startEntityClass)) {\n                $this->_em->remove($candidate);\n            }\n        }\n\n        $this->_em->flush();\n\n        self::assertFalse($this->_em->getConnection()->fetchOne('SELECT id FROM gh10566_a WHERE id = ?', [$aId]));\n        self::assertFalse($this->_em->getConnection()->fetchOne('SELECT id FROM gh10566_b WHERE id = ?', [$bId]));\n        self::assertFalse($this->_em->getConnection()->fetchOne('SELECT id FROM gh10566_c WHERE id = ?', [$cId]));\n    }\n\n    /** @return Generator<array{class-string}> */\n    public static function provideEntityClasses(): Generator\n    {\n        yield [GH10566A::class];\n        yield [GH10566B::class];\n        yield [GH10566C::class];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10566_a')]\nclass GH10566A\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH10566B */\n    #[ORM\\OneToOne(targetEntity: GH10566B::class, cascade: ['all'])]\n    #[ORM\\JoinColumn(nullable: true, onDelete: 'SET NULL')]\n    public $other;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10566_b')]\nclass GH10566B\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH10566C */\n    #[ORM\\OneToOne(targetEntity: GH10566C::class, cascade: ['all'])]\n    #[ORM\\JoinColumn(nullable: true, onDelete: 'SET NULL')]\n    public $other;\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh10566_c')]\nclass GH10566C\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH10566A */\n    #[ORM\\OneToOne(targetEntity: GH10566A::class, cascade: ['all'])]\n    #[ORM\\JoinColumn(nullable: true, onDelete: 'SET NULL')]\n    public $other;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10625Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-10625')]\nclass GH10625Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10625Root::class,\n            GH10625Middle::class,\n            GH10625Leaf::class,\n        );\n    }\n\n    #[DataProvider('queryClasses')]\n    public function testLoadFieldsFromAllClassesInHierarchy(string $queryClass): void\n    {\n        $entity = new GH10625Leaf();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $loadedEntity = $this->_em->find($queryClass, $entity->id);\n\n        self::assertNotNull($loadedEntity);\n        self::assertInstanceOf(GH10625Leaf::class, $loadedEntity);\n    }\n\n    public static function queryClasses(): array\n    {\n        return [\n            'query via root entity' => [GH10625Root::class],\n            'query via intermediate entity' => [GH10625Middle::class],\n            'query via leaf entity' => [GH10625Leaf::class],\n        ];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap([1 => 'GH10625Leaf'])] // <- This DiscriminatorMap contains the single non-abstract Entity class only\nabstract class GH10625Root\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n}\n\n#[ORM\\Entity]\nabstract class GH10625Middle extends GH10625Root\n{\n}\n\n#[ORM\\Entity]\nclass GH10625Leaf extends GH10625Middle\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Tests\\OrmTestCase;\n\nfinal class GH10661Test extends OrmTestCase\n{\n    /** @var EntityManagerInterface */\n    private $em;\n\n    protected function setUp(): void\n    {\n        $this->em = $this->getTestEntityManager();\n    }\n\n    public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void\n    {\n        $class = $this->em->getClassMetadata(InvalidEntity::class);\n        $ce    = $this->bootstrapValidator()->validateClass($class);\n\n        self::assertSame(\n            [\"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661\\InvalidEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type.\"],\n            $ce,\n        );\n    }\n\n    public function testPropertyTypeErrorsCanBeSilenced(): void\n    {\n        $class = $this->em->getClassMetadata(InvalidEntity::class);\n        $ce    = $this->bootstrapValidator(false)->validateClass($class);\n\n        self::assertSame([], $ce);\n    }\n\n    public function testMetadataFieldTypeNotCoherentWithEntityPropertyTypeWithInheritance(): void\n    {\n        $class = $this->em->getClassMetadata(InvalidChildEntity::class);\n        $ce    = $this->bootstrapValidator()->validateClass($class);\n\n        self::assertSame(\n            [\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661\\InvalidChildEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type.\",\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661\\InvalidChildEntity#property2' has the property type 'int' that differs from the metadata field type 'string' returned by the 'string' DBAL type.\",\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661\\InvalidChildEntity#anotherProperty' has the property type 'string' that differs from the metadata field type 'bool' returned by the 'boolean' DBAL type.\",\n            ],\n            $ce,\n        );\n    }\n\n    private function bootstrapValidator(bool $validatePropertyTypes = true): SchemaValidator\n    {\n        return new SchemaValidator($this->em, $validatePropertyTypes);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10661/InvalidChildEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass InvalidChildEntity extends InvalidEntity\n{\n    #[Column(type: 'string')]\n    protected int $property2;\n\n    #[Column(type: 'boolean')]\n    private string $anotherProperty;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10661/InvalidEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10661;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)]\n#[DiscriminatorMap(['root' => 'InvalidEntity', 'child' => 'InvalidChildEntity'])]\nclass InvalidEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $key;\n\n    #[Column(type: 'decimal')]\n    protected float $property1;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10747Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function str_replace;\n\n/**\n * Functional tests for asserting that orphaned children in a OneToMany relationship get removed with a custom identifier\n */\n#[Group('GH10747')]\nfinal class GH10747Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! DBALType::hasType(GH10747CustomIdObjectHashType::class)) {\n            DBALType::addType(GH10747CustomIdObjectHashType::class, GH10747CustomIdObjectHashType::class);\n        }\n\n        $this->setUpEntitySchema([GH10747Article::class, GH10747Credit::class]);\n    }\n\n    public function testOrphanedOneToManyDeletesCollection(): void\n    {\n        $object = new GH10747Article(\n            new CustomIdObject('article'),\n        );\n\n        $creditOne = new GH10747Credit(\n            $object,\n            'credit1',\n        );\n\n        $creditTwo = new GH10747Credit(\n            $object,\n            'credit2',\n        );\n\n        $object->setCredits(new ArrayCollection([$creditOne, $creditTwo]));\n\n        $this->_em->persist($object);\n        $this->_em->persist($creditOne);\n        $this->_em->persist($creditTwo);\n        $this->_em->flush();\n\n        $id = $object->id;\n\n        $object2 = $this->_em->find(GH10747Article::class, $id);\n\n        $creditThree = new GH10747Credit(\n            $object2,\n            'credit3',\n        );\n\n        $object2->setCredits(new ArrayCollection([$creditThree]));\n\n        $this->_em->persist($object2);\n        $this->_em->persist($creditThree);\n        $this->_em->flush();\n\n        $currentDatabaseCredits = $this->_em->createQueryBuilder()\n            ->select('c.id')\n            ->from(GH10747Credit::class, 'c')\n            ->getQuery()\n            ->execute();\n\n        self::assertCount(1, $currentDatabaseCredits);\n    }\n}\n\n#[Entity]\n#[Table]\nclass GH10747Article\n{\n    #[Id]\n    #[Column(type: GH10747CustomIdObjectHashType::class, length: 24)] // strlen(PHP_INT_MAX . '_test')\n    public CustomIdObject $id;\n\n    /** @var Collection<int, GH10747Credit> */\n    #[ORM\\OneToMany(mappedBy: 'article', targetEntity: GH10747Credit::class, orphanRemoval: true)]\n    public Collection $credits;\n\n    public function __construct(CustomIdObject $id)\n    {\n        $this->id      = $id;\n        $this->credits = new ArrayCollection();\n    }\n\n    public function setCredits(Collection $credits): void\n    {\n        $this->credits = $credits;\n    }\n\n    /** @return Collection<int, GH10747Credit> */\n    public function getCredits(): Collection\n    {\n        return $this->credits;\n    }\n}\n\n#[Entity]\n#[Table]\nclass GH10747Credit\n{\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    #[Id]\n    public int|null $id = null;\n\n    #[ORM\\ManyToOne(inversedBy: 'credits', targetEntity: GH10747Article::class)]\n    public GH10747Article $article;\n\n    public function __construct(GH10747Article $article, public string $name)\n    {\n        $this->article = $article;\n    }\n}\n\nclass GH10747CustomIdObjectHashType extends DBALType\n{\n    public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string\n    {\n        return $value->id . '_test';\n    }\n\n    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): CustomIdObject\n    {\n        return new CustomIdObject(str_replace('_test', '', $value));\n    }\n\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return self::class;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10752Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Exception\\ForeignKeyConstraintViolationException;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH10752')]\nclass GH10752Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10752Order::class,\n            GH10752Promotion::class,\n        ]);\n    }\n\n    public function testThrowExceptionWhenRemovingPromotionThatIsInUse(): void\n    {\n        $order     = new GH10752Order();\n        $promotion = new GH10752Promotion();\n\n        $order->addPromotion($promotion);\n\n        $this->_em->persist($order);\n        $this->_em->persist($promotion);\n        $this->_em->flush();\n\n        $this->_em->remove($promotion);\n\n        $this->expectException(ForeignKeyConstraintViolationException::class);\n        $this->_em->flush();\n    }\n\n    public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotInMemory(): void\n    {\n        $order     = new GH10752Order();\n        $promotion = new GH10752Promotion();\n\n        $order->addPromotion($promotion);\n\n        $this->_em->persist($order);\n        $this->_em->persist($promotion);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $promotion = $this->_em->find(GH10752Promotion::class, $promotion->id);\n        $this->_em->remove($promotion);\n\n        $this->expectException(ForeignKeyConstraintViolationException::class);\n        $this->_em->flush();\n    }\n}\n\n#[ORM\\Entity]\nclass GH10752Order\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n\n    #[ORM\\ManyToMany(targetEntity: GH10752Promotion::class, cascade: ['persist'])]\n    #[ORM\\JoinTable(name: 'order_promotion')]\n    #[ORM\\JoinColumn(name: 'order_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[ORM\\InverseJoinColumn(name: 'promotion_id', referencedColumnName: 'id')]\n    private Collection $promotions;\n\n    public function __construct()\n    {\n        $this->promotions = new ArrayCollection();\n    }\n\n    public function addPromotion(GH10752Promotion $promotion): void\n    {\n        if (! $this->promotions->contains($promotion)) {\n            $this->promotions->add($promotion);\n        }\n    }\n}\n\n#[ORM\\Entity]\nclass GH10752Promotion\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int|null $id = null;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10808Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionClass;\n\n#[Group('GH10808')]\nclass GH10808Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10808Appointment::class,\n            GH10808AppointmentChild::class,\n        );\n    }\n\n    public function testDQLDeferredEagerLoad(): void\n    {\n        $appointment = new GH10808Appointment();\n\n        $this->_em->persist($appointment);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery(\n            'SELECT appointment from Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH10808Appointment appointment\n               JOIN appointment.child appointment_child',\n        );\n\n        // By default, UnitOfWork::HINT_DEFEREAGERLOAD is set to 'true'\n        $deferredLoadResult = $query->getSingleResult();\n\n        // Clear the EM to prevent the recovery of the loaded instance, which would otherwise result in a proxy.\n        $this->_em->clear();\n\n        $eagerLoadResult = $query->setHint(UnitOfWork::HINT_DEFEREAGERLOAD, false)->getSingleResult();\n\n        $reflector = new ReflectionClass(GH10808AppointmentChild::class);\n        self::assertFalse(\n            $this->isUninitializedObject($deferredLoadResult->child),\n            '$deferredLoadResult->child should be a native lazy object',\n        );\n        self::assertFalse(\n            $this->isUninitializedObject($deferredLoadResult->child),\n            '$eagerLoadResult->child should not be a proxy',\n        );\n    }\n}\n\n#[Entity]\n#[Table(name: 'gh10808_appointment')]\nclass GH10808Appointment\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH10808AppointmentChild */\n    #[OneToOne(targetEntity: GH10808AppointmentChild::class, cascade: ['persist', 'remove'], fetch: 'EAGER')]\n    #[JoinColumn(name: 'child_id', referencedColumnName: 'id')]\n    public $child;\n\n    public function __construct()\n    {\n        $this->child = new GH10808AppointmentChild();\n    }\n}\n\n#[Entity]\n#[Table(name: 'gh10808_appointment_child')]\nclass GH10808AppointmentChild\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10869Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Event\\PostPersistEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH10869Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10869Entity::class,\n        ]);\n    }\n\n    public function testPostPersistListenerUpdatingObjectFieldWhileOtherInsertPending(): void\n    {\n        $entity1 = new GH10869Entity();\n        $this->_em->persist($entity1);\n\n        $entity2 = new GH10869Entity();\n        $this->_em->persist($entity2);\n\n        $this->_em->getEventManager()->addEventListener(Events::postPersist, new class {\n            public function postPersist(PostPersistEventArgs $args): void\n            {\n                $object = $args->getObject();\n\n                $objectManager = $args->getObjectManager();\n                $object->field = 'test ' . $object->id;\n                $objectManager->flush();\n            }\n        });\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertSame('test ' . $entity1->id, $entity1->field);\n        self::assertSame('test ' . $entity2->id, $entity2->field);\n\n        $entity1Reloaded = $this->_em->find(GH10869Entity::class, $entity1->id);\n        self::assertSame($entity1->field, $entity1Reloaded->field);\n\n        $entity2Reloaded = $this->_em->find(GH10869Entity::class, $entity2->id);\n        self::assertSame($entity2->field, $entity2Reloaded->field);\n    }\n}\n\n#[ORM\\Entity]\nclass GH10869Entity\n{\n    /** @var ?int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var ?string */\n    #[ORM\\Column(type: 'text', nullable: true)]\n    public $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10880Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function reset;\n\nclass GH10880Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10880BaseProcess::class,\n            GH10880Process::class,\n            GH10880ProcessOwner::class,\n        ]);\n    }\n\n    public function testProcessShouldBeUpdated(): void\n    {\n        $process              = new GH10880Process();\n        $process->description = 'first value';\n\n        $owner          = new GH10880ProcessOwner();\n        $owner->process = $process;\n\n        $this->_em->persist($process);\n        $this->_em->persist($owner);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $ownerLoaded                       = $this->_em->getRepository(GH10880ProcessOwner::class)->find($owner->id);\n        $ownerLoaded->process->description = 'other description';\n\n        $queryLog = $this->getQueryLog();\n        $queryLog->reset()->enable();\n        $this->_em->flush();\n\n        $this->removeTransactionCommandsFromQueryLog();\n\n        self::assertCount(1, $queryLog->queries);\n        $query = reset($queryLog->queries);\n        self::assertSame('UPDATE GH10880BaseProcess SET description = ? WHERE id = ?', $query['sql']);\n    }\n\n    private function removeTransactionCommandsFromQueryLog(): void\n    {\n        $log = $this->getQueryLog();\n\n        foreach ($log->queries as $key => $entry) {\n            if ($entry['sql'] === '\"START TRANSACTION\"' || $entry['sql'] === '\"COMMIT\"') {\n                unset($log->queries[$key]);\n            }\n        }\n    }\n}\n\n#[ORM\\Entity]\nclass GH10880ProcessOwner\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /**\n     * fetch=EAGER is important to reach the part of \\Doctrine\\ORM\\UnitOfWork::createEntity()\n     * that is important for this regression test\n     *\n     * @var GH10880Process\n     */\n    #[ORM\\ManyToOne(targetEntity: GH10880Process::class, fetch: 'EAGER')]\n    public $process;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap(['process' => GH10880Process::class])]\nabstract class GH10880BaseProcess\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[ORM\\Column(type: 'text')]\n    public $description;\n}\n\n#[ORM\\Entity]\nclass GH10880Process extends GH10880BaseProcess\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10889Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/** @see https://github.com/doctrine/orm/issues/10889 */\n#[Group('GH10889')]\nclass GH10889Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH10889Person::class,\n            GH10889Company::class,\n            GH10889Resume::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $person = new GH10889Person();\n        $resume = new GH10889Resume($person, null);\n\n        $this->_em->persist($person);\n        $this->_em->persist($resume);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        /** @var list<GH10889Resume> $resumes */\n        $resumes = $this->_em\n            ->getRepository(GH10889Resume::class)\n            ->createQueryBuilder('resume')\n            ->leftJoin('resume.currentCompany', 'company')->addSelect('company')\n            ->getQuery()\n            ->getResult();\n\n        $this->assertArrayHasKey(0, $resumes);\n        $this->assertEquals(1, $resumes[0]->person->id);\n        $this->assertNull($resumes[0]->currentCompany);\n    }\n}\n\n#[ORM\\Entity]\nclass GH10889Person\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n}\n\n#[ORM\\Entity]\nclass GH10889Company\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n}\n\n#[ORM\\Entity]\nclass GH10889Resume\n{\n    public function __construct(\n        #[ORM\\Id]\n        #[ORM\\OneToOne]\n        public GH10889Person $person,\n        #[ORM\\ManyToOne]\n        public GH10889Company|null $currentCompany,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10912Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function array_filter;\nuse function array_values;\nuse function strpos;\n\nclass GH10912Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10912User::class,\n            GH10912Profile::class,\n            GH10912Room::class,\n        ]);\n    }\n\n    public function testIssue(): void\n    {\n        $user    = new GH10912User();\n        $profile = new GH10912Profile();\n        $room    = new GH10912Room();\n\n        $user->rooms->add($room);\n        $user->profile = $profile;\n        $profile->user = $user;\n        $room->user    = $user;\n\n        $this->_em->persist($room);\n        $this->_em->persist($user);\n        $this->_em->persist($profile);\n        $this->_em->flush();\n\n        /*\n         * This issue is about finding a special deletion order:\n         * $user and $profile cross-reference each other with ON DELETE CASCADE.\n         * So, whichever one gets deleted first, the DBMS will immediately dispose\n         * of the other one as well.\n         *\n         * $user -> $room is the unproblematic (irrelevant) inverse side of\n         * a OneToMany association.\n         *\n         * $room -> $user is a not-nullable, no DBMS-level-cascade, owning side\n         * of ManyToOne. We *must* remove the $room _before_ the $user can be\n         * deleted. And remember, $user deletion happens either when we DELETE the\n         * user (direct deletion), or when we delete the $profile (ON DELETE CASCADE\n         * propagates to the user).\n         *\n         * In the original bug report, the ordering of fields in the entities was\n         * relevant, in combination with a cascade=persist configuration.\n         *\n         * But, for the sake of clarity, let's put these features away and create\n         * the problematic sequence in UnitOfWork::$entityDeletions directly:\n         */\n        $this->_em->remove($profile);\n        $this->_em->remove($user);\n        $this->_em->remove($room);\n\n        $queryLog = $this->getQueryLog();\n        $queryLog->reset()->enable();\n\n        $this->_em->flush();\n\n        $queries = array_values(array_filter($queryLog->queries, static function (array $entry): bool {\n            return strpos($entry['sql'], 'DELETE') === 0;\n        }));\n\n        self::assertCount(3, $queries);\n\n        // we do not care about the order of $user vs. $profile, so do not check them.\n        self::assertSame('DELETE FROM GH10912Room WHERE id = ?', $queries[0]['sql'], '$room deletion is the first query');\n\n        // The EntityManager is aware that all three entities have been deleted (sanity check)\n        $im = $this->_em->getUnitOfWork()->getIdentityMap();\n        self::assertEmpty($im[GH10912Profile::class]);\n        self::assertEmpty($im[GH10912User::class]);\n        self::assertEmpty($im[GH10912Room::class]);\n    }\n}\n\n#[ORM\\Entity]\nclass GH10912User\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, GH10912Room> */\n    #[ORM\\OneToMany(targetEntity: GH10912Room::class, mappedBy: 'user')]\n    public Collection $rooms;\n\n    #[ORM\\OneToOne(targetEntity: GH10912Profile::class)]\n    #[ORM\\JoinColumn(onDelete: 'cascade')]\n    public GH10912Profile $profile;\n\n    public function __construct()\n    {\n        $this->rooms = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH10912Profile\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    #[ORM\\OneToOne(targetEntity: GH10912User::class)]\n    #[ORM\\JoinColumn(onDelete: 'cascade')]\n    public GH10912User $user;\n}\n\n#[ORM\\Entity]\nclass GH10912Room\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\ManyToOne(targetEntity: GH10912User::class, inversedBy: 'rooms')]\n    #[ORM\\JoinColumn(nullable: false)]\n    public GH10912User $user;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10913Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Internal\\TopologicalSort\\CycleDetectedException;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function array_filter;\nuse function array_values;\nuse function strpos;\n\nclass GH10913Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH10913Entity::class,\n        ]);\n    }\n\n    public function testExample1(): void\n    {\n        [$a, $b, $c] = $this->createEntities(3);\n\n        $c->ref = $b;\n        $b->odc = $a;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->flush();\n\n        $this->_em->remove($a);\n        $this->_em->remove($b);\n        $this->_em->remove($c);\n\n        $this->flushAndAssertNumberOfDeleteQueries(3);\n    }\n\n    public function testExample2(): void\n    {\n        [$a, $b, $c] = $this->createEntities(3);\n\n        $a->odc = $b;\n        $b->odc = $a;\n        $c->ref = $b;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->flush();\n\n        $this->_em->remove($a);\n        $this->_em->remove($b);\n        $this->_em->remove($c);\n\n        $this->flushAndAssertNumberOfDeleteQueries(3);\n    }\n\n    public function testExample3(): void\n    {\n        [$a, $b, $c] = $this->createEntities(3);\n\n        $a->odc = $b;\n        $a->ref = $c;\n        $c->ref = $b;\n        $b->odc = $a;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->flush();\n\n        $this->_em->remove($a);\n        $this->_em->remove($b);\n        $this->_em->remove($c);\n\n        self::expectException(CycleDetectedException::class);\n\n        $this->_em->flush();\n    }\n\n    public function testExample4(): void\n    {\n        [$a, $b, $c, $d] = $this->createEntities(4);\n\n        $a->ref = $b;\n        $b->odc = $c;\n        $c->odc = $b;\n        $d->ref = $c;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->persist($d);\n        $this->_em->flush();\n\n        $this->_em->remove($b);\n        $this->_em->remove($c);\n        $this->_em->remove($d);\n        $this->_em->remove($a);\n\n        $this->flushAndAssertNumberOfDeleteQueries(4);\n    }\n\n    private function flushAndAssertNumberOfDeleteQueries(int $expectedCount): void\n    {\n        $queryLog = $this->getQueryLog();\n        $queryLog->reset()->enable();\n\n        $this->_em->flush();\n\n        $queries = array_values(array_filter($queryLog->queries, static function (array $entry): bool {\n            return strpos($entry['sql'], 'DELETE') === 0;\n        }));\n\n        self::assertCount($expectedCount, $queries);\n    }\n\n    /** @return list<GH10913Entity> */\n    private function createEntities(int $count = 1): array\n    {\n        $result = [];\n\n        for ($i = 0; $i < $count; $i++) {\n            $result[] = new GH10913Entity();\n        }\n\n        return $result;\n    }\n}\n\n#[ORM\\Entity]\nclass GH10913Entity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\ManyToOne(targetEntity: self::class)]\n    #[ORM\\JoinColumn(nullable: true, onDelete: 'CASCADE')]\n    public GH10913Entity $odc;\n\n    #[ORM\\ManyToOne(targetEntity: self::class)]\n    #[ORM\\JoinColumn(nullable: true)]\n    public GH10913Entity $ref;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH10927Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-10927')]\nclass GH10927Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n        if (! $platform instanceof PostgreSQLPlatform) {\n            self::markTestSkipped('The ' . self::class . ' requires the use of postgresql.');\n        }\n\n        $this->setUpEntitySchema([\n            GH10927RootMappedSuperclass::class,\n            GH10927InheritedMappedSuperclass::class,\n            GH10927EntityA::class,\n            GH10927EntityB::class,\n            GH10927EntityC::class,\n        ]);\n    }\n\n    public function testSequenceGeneratorDefinitionForRootMappedSuperclass(): void\n    {\n        $metadata = $this->_em->getClassMetadata(GH10927RootMappedSuperclass::class);\n\n        self::assertNull($metadata->sequenceGeneratorDefinition);\n    }\n\n    public function testSequenceGeneratorDefinitionForEntityA(): void\n    {\n        $metadata = $this->_em->getClassMetadata(GH10927EntityA::class);\n\n        self::assertSame('GH10927EntityA_id_seq', $metadata->sequenceGeneratorDefinition['sequenceName']);\n    }\n\n    public function testSequenceGeneratorDefinitionForInheritedMappedSuperclass(): void\n    {\n        $metadata = $this->_em->getClassMetadata(GH10927InheritedMappedSuperclass::class);\n\n        self::assertSame('GH10927InheritedMappedSuperclass_id_seq', $metadata->sequenceGeneratorDefinition['sequenceName']);\n    }\n\n    public function testSequenceGeneratorDefinitionForEntityB(): void\n    {\n        $metadata = $this->_em->getClassMetadata(GH10927EntityB::class);\n\n        self::assertSame('GH10927EntityB_id_seq', $metadata->sequenceGeneratorDefinition['sequenceName']);\n    }\n\n    public function testSequenceGeneratorDefinitionForEntityC(): void\n    {\n        $metadata = $this->_em->getClassMetadata(GH10927EntityC::class);\n\n        self::assertSame('GH10927EntityB_id_seq', $metadata->sequenceGeneratorDefinition['sequenceName']);\n    }\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10927RootMappedSuperclass\n{\n}\n\n#[ORM\\Entity]\nclass GH10927EntityA extends GH10927RootMappedSuperclass\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'SEQUENCE')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH10927InheritedMappedSuperclass extends GH10927RootMappedSuperclass\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'SEQUENCE')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorColumn(name: 'discr', type: 'string')]\n#[ORM\\DiscriminatorMap(['B' => GH10927EntityB::class, 'C' => GH10927EntityC::class])]\nclass GH10927EntityB extends GH10927InheritedMappedSuperclass\n{\n}\n\n#[ORM\\Entity]\nclass GH10927EntityC extends GH10927EntityB\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Entity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11017;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass GH11017Entity\n{\n    /** @var ?int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var GH11017Enum */\n    #[ORM\\Column(type: 'string', enumType: GH11017Enum::class)]\n    public $field;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Enum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11017;\n\nenum GH11017Enum: string\n{\n    case FIRST  = 'first';\n    case SECOND = 'second';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11017;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Query\\ResultSetMappingBuilder;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function sprintf;\n\nclass GH11017Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11017Entity::class,\n        ]);\n    }\n\n    public function testPostPersistListenerUpdatingObjectFieldWhileOtherInsertPending(): void\n    {\n        $entity1        = new GH11017Entity();\n        $entity1->field = GH11017Enum::FIRST;\n        $this->_em->persist($entity1);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);\n        $rsm->addRootEntityFromClassMetadata(GH11017Entity::class, 'e');\n\n        $tableName = $this->_em->getClassMetadata(GH11017Entity::class)->getTableName();\n        $sql       = sprintf('SELECT %s FROM %s e WHERE id = :id', $rsm->generateSelectClause(), $tableName);\n\n        $query = $this->_em->createNativeQuery($sql, $rsm)\n            ->setParameter('id', $entity1->id);\n\n        $entity1Reloaded = $query->getSingleResult(AbstractQuery::HYDRATE_ARRAY);\n        self::assertNotNull($entity1Reloaded);\n        self::assertSame($entity1->field, $entity1Reloaded['field']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/EntityStatus.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\ninterface EntityStatus\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/GH11037Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Tests\\OrmTestCase;\n\nfinal class GH11037Test extends OrmTestCase\n{\n    /** @var EntityManagerInterface */\n    private $em;\n\n    /** @var SchemaValidator */\n    private $validator;\n\n    protected function setUp(): void\n    {\n        $this->em        = $this->getTestEntityManager();\n        $this->validator = new SchemaValidator($this->em);\n    }\n\n    public function testMetadataFieldTypeCoherentWithEntityPropertyType(): void\n    {\n        $class = $this->em->getClassMetadata(ValidEntityWithTypedEnum::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals([], $ce);\n    }\n\n    public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void\n    {\n        $class = $this->em->getClassMetadata(InvalidEntityWithTypedEnum::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\InvalidEntityWithTypedEnum#status1' has the property type 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\StringEntityStatus' with a backing type of 'string' that differs from the metadata field type 'int'.\",\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\InvalidEntityWithTypedEnum#status2' has the property type 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\IntEntityStatus' that differs from the metadata enumType 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\StringEntityStatus'.\",\n                \"The field 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\InvalidEntityWithTypedEnum#status3' has the metadata enumType 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037\\StringEntityStatus' with a backing type of 'string' that differs from the metadata field type 'int'.\",\n            ],\n            $ce,\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/IntEntityStatus.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\nenum IntEntityStatus: int\n{\n    case ACTIVE   = 0;\n    case INACTIVE = 1;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/InvalidEntityWithTypedEnum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass InvalidEntityWithTypedEnum\n{\n    #[Id]\n    #[Column]\n    protected int $id;\n\n    #[Column(type: 'integer', enumType: StringEntityStatus::class)]\n    protected StringEntityStatus $status1;\n\n    #[Column(type: 'integer', enumType: StringEntityStatus::class)]\n    protected IntEntityStatus $status2;\n\n    #[Column(type: 'integer', enumType: StringEntityStatus::class)]\n    protected EntityStatus $status3;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/StringEntityStatus.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\nenum StringEntityStatus: string implements EntityStatus\n{\n    case ACTIVE   = 'active';\n    case INACTIVE = 'inactive';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11037/ValidEntityWithTypedEnum.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11037;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass ValidEntityWithTypedEnum\n{\n    #[Id]\n    #[Column]\n    protected int $id;\n\n    #[Column(type: 'string', enumType: StringEntityStatus::class)]\n    protected StringEntityStatus $status1;\n\n    #[Column(type: 'smallint', enumType: IntEntityStatus::class)]\n    protected IntEntityStatus $status2;\n\n    #[Column(type: 'string', enumType: StringEntityStatus::class)]\n    protected EntityStatus $status3;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11058Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11058Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11058Parent::class,\n            GH11058Child::class,\n        ]);\n    }\n\n    public function testChildrenInsertedInOrderOfPersistCalls1WhenParentPersistedLast(): void\n    {\n        [$parent, $child1, $child2] = $this->createParentWithTwoChildEntities();\n\n        $this->_em->persist($child1);\n        $this->_em->persist($child2);\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        self::assertTrue($child1->id < $child2->id);\n    }\n\n    public function testChildrenInsertedInOrderOfPersistCalls2WhenParentPersistedLast(): void\n    {\n        [$parent, $child1, $child2] = $this->createParentWithTwoChildEntities();\n\n        $this->_em->persist($child2);\n        $this->_em->persist($child1);\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        self::assertTrue($child2->id < $child1->id);\n    }\n\n    public function testChildrenInsertedInOrderOfPersistCalls1WhenParentPersistedFirst(): void\n    {\n        [$parent, $child1, $child2] = $this->createParentWithTwoChildEntities();\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child1);\n        $this->_em->persist($child2);\n        $this->_em->flush();\n\n        self::assertTrue($child1->id < $child2->id);\n    }\n\n    public function testChildrenInsertedInOrderOfPersistCalls2WhenParentPersistedFirst(): void\n    {\n        [$parent, $child1, $child2] = $this->createParentWithTwoChildEntities();\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child2);\n        $this->_em->persist($child1);\n        $this->_em->flush();\n\n        self::assertTrue($child2->id < $child1->id);\n    }\n\n    private function createParentWithTwoChildEntities(): array\n    {\n        $parent = new GH11058Parent();\n        $child1 = new GH11058Child();\n        $child2 = new GH11058Child();\n\n        $parent->addChild($child1);\n        $parent->addChild($child2);\n\n        return [$parent, $child1, $child2];\n    }\n}\n\n#[ORM\\Entity]\nclass GH11058Parent\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, GH11058Child> */\n    #[ORM\\OneToMany(mappedBy: 'parent', targetEntity: GH11058Child::class)]\n    public Collection $children;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n\n    public function addChild(GH11058Child $child): void\n    {\n        if (! $this->children->contains($child)) {\n            $this->children->add($child);\n            $child->setParent($this);\n        }\n    }\n}\n\n#[ORM\\Entity]\nclass GH11058Child\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH11058Parent */\n    #[ORM\\ManyToOne(inversedBy: 'children', targetEntity: GH11058Parent::class)]\n    public $parent;\n\n    public function setParent(GH11058Parent $parent): void\n    {\n        $this->parent = $parent;\n        $parent->addChild($this);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityAdvanced.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11072;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass GH11072EntityAdvanced\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    #[Column(type: 'json')]\n    public mixed $anything;\n\n    #[Column(type: 'json')]\n    public true $alwaysTrue = true;\n\n    #[Column(type: 'json')]\n    public false $alwaysFalse = false;\n\n    #[Column(type: 'json')]\n    public null $alwaysNull = null;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityBasic.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11072;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\n\n#[Entity]\nclass GH11072EntityBasic\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    #[Column(type: 'json')]\n    public string $jsonString = 'test';\n\n    #[Column(type: 'json')]\n    public int $age = 99;\n\n    #[Column(type: 'json')]\n    public float $score = 0.0;\n\n    #[Column(type: 'json', nullable: true)]\n    public bool|null $trinary = null;\n\n    #[Column(type: 'json')]\n    public array $metadata = [];\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11072/GH11072Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11072;\n\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\n\n#[RequiresPhp('8.2')]\nfinal class GH11072Test extends OrmFunctionalTestCase\n{\n    /** @var SchemaValidator */\n    private $validator;\n\n    protected function setUp(): void\n    {\n        $this->_em       = $this->getTestEntityManager();\n        $this->validator = new SchemaValidator($this->_em);\n    }\n\n    public function testAcceptsSubsetOfBuiltinTypesWithoutErrors(): void\n    {\n        $class = $this->_em->getClassMetadata(GH11072EntityBasic::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertSame([], $ce);\n    }\n\n    public function testAcceptsAdvancedSubsetOfBuiltinTypesWithoutErrors(): void\n    {\n        $class = $this->_em->getClassMetadata(GH11072EntityAdvanced::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertSame([], $ce);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11112Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nclass GH11112Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n        self::$queryCache = new ArrayAdapter();\n\n        parent::setUp();\n    }\n\n    public function testSimpleQueryHasLimitAndOffsetApplied(): void\n    {\n        $platform    = $this->_em->getConnection()->getDatabasePlatform();\n        $query       = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u');\n        $originalSql = $query->getSQL();\n\n        $query->setMaxResults(10);\n        $query->setFirstResult(20);\n        $sqlMax10First20 = $query->getSQL();\n\n        $query->setMaxResults(30);\n        $query->setFirstResult(40);\n        $sqlMax30First40 = $query->getSQL();\n\n        // The SQL is platform specific and may even be something with outer SELECTS being added. So,\n        // derive the expected value at runtime through the platform.\n        self::assertSame($platform->modifyLimitQuery($originalSql, 10, 20), $sqlMax10First20);\n        self::assertSame($platform->modifyLimitQuery($originalSql, 30, 40), $sqlMax30First40);\n\n        $cacheEntries = self::$queryCache->getValues();\n        self::assertCount(1, $cacheEntries);\n    }\n\n    public function testSubqueryLimitAndOffsetAreIgnored(): void\n    {\n        // Not sure what to do about this test. Basically, I want to make sure that\n        // firstResult/maxResult for subqueries are not relevant, they do not make it\n        // into the final query at all. That would give us the guarantee that the\n        // \"sql finalizer\" step is sufficient for the final, \"outer\" query and we\n        // do not need to run finalizers for the subqueries.\n\n        // This DQL/query makes no sense, it's just about creating a subquery in the first place\n        $queryBuilder = $this->_em->createQueryBuilder();\n        $queryBuilder\n            ->select('o')\n            ->from(CmsUser::class, 'o')\n            ->where($queryBuilder->expr()->exists(\n                $this->_em->createQueryBuilder()\n                            ->select('u')\n                            ->from(CmsUser::class, 'u')\n                            ->setFirstResult(10)\n                            ->setMaxResults(20),\n            ));\n\n        $query       = $queryBuilder->getQuery();\n        $originalSql = $query->getSQL();\n\n        $clone = clone $query;\n        $clone->setFirstResult(24);\n        $clone->setMaxResults(42);\n        $limitedSql = $clone->getSQL();\n\n        $platform = $this->_em->getConnection()->getDatabasePlatform();\n\n        // The SQL is platform specific and may even be something with outer SELECTS being added. So,\n        // derive the expected value at runtime through the platform.\n        self::assertSame($platform->modifyLimitQuery($originalSql, 42, 24), $limitedSql);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11135Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11135Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11135MappedSuperclass::class,\n            GH11135EntityWithOverride::class,\n            GH11135EntityWithoutOverride::class,\n        ]);\n    }\n\n    public function testOverrideInheritsDeclaringClass(): void\n    {\n        $cm1 = $this->_em->getClassMetadata(GH11135EntityWithOverride::class);\n        $cm2 = $this->_em->getClassMetadata(GH11135EntityWithoutOverride::class);\n\n        self::assertSame($cm1->getFieldMapping('id')->declared, $cm2->getFieldMapping('id')->declared);\n        self::assertSame($cm1->getAssociationMapping('ref')->declared, $cm2->getAssociationMapping('ref')->declared);\n    }\n}\n\n#[ORM\\MappedSuperclass]\nclass GH11135MappedSuperclass\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    private int $id;\n\n    #[ORM\\ManyToOne(targetEntity: GH11135EntityWithoutOverride::class)]\n    private GH11135EntityWithoutOverride $ref;\n}\n\n#[ORM\\Entity]\n#[ORM\\AttributeOverrides([\n    new ORM\\AttributeOverride(name: 'id', column: new ORM\\Column(name: 'id_overridden')),\n])]\n#[ORM\\AssociationOverrides([\n    new ORM\\AssociationOverride(name: 'ref', joinColumns: [new ORM\\JoinColumn(name: 'ref_overridden', referencedColumnName: 'id')]),\n])]\nclass GH11135EntityWithOverride extends GH11135MappedSuperclass\n{\n}\n\n#[ORM\\Entity]\nclass GH11135EntityWithoutOverride extends GH11135MappedSuperclass\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11149;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table('gh11149_eager_product')]\nclass EagerProduct\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    public int $id;\n\n    /** @var Collection<string, EagerProductTranslation> */\n    #[ORM\\OneToMany(\n        targetEntity: EagerProductTranslation::class,\n        mappedBy: 'product',\n        fetch: 'EAGER',\n        indexBy: 'locale_code',\n    )]\n    public Collection $translations;\n\n    public function __construct(int $id)\n    {\n        $this->id           = $id;\n        $this->translations = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11149/EagerProductTranslation.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11149;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table('gh11149_eager_product_translation')]\nclass EagerProductTranslation\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    private int $id;\n\n    #[ORM\\ManyToOne(inversedBy: 'translations')]\n    #[ORM\\JoinColumn(nullable: false)]\n    public EagerProduct $product;\n\n    #[ORM\\ManyToOne]\n    #[ORM\\JoinColumn(name: 'locale_code', referencedColumnName: 'code', nullable: false)]\n    public Locale $locale;\n\n    public function __construct(int $id, EagerProduct $product, Locale $locale)\n    {\n        $this->id      = $id;\n        $this->product = $product;\n        $this->locale  = $locale;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11149/GH11149Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11149;\n\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Persistence\\Proxy;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11149Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            Locale::class,\n            EagerProduct::class,\n            EagerProductTranslation::class,\n        ]);\n    }\n\n    public function testFetchEagerModeWithIndexBy(): void\n    {\n        // Load entities into database\n        $this->_em->persist($product = new EagerProduct(11149));\n        $this->_em->persist($locale = new Locale('fr_FR'));\n        $this->_em->persist(new EagerProductTranslation(11149, $product, $locale));\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Fetch entity from database\n        $product = $this->_em->find(EagerProduct::class, 11149);\n\n        // Assert associated entity is loaded eagerly\n        static::assertInstanceOf(EagerProduct::class, $product);\n        static::assertInstanceOf(PersistentCollection::class, $product->translations);\n        static::assertTrue($product->translations->isInitialized());\n        static::assertCount(1, $product->translations);\n\n        // Assert associated entity is indexed by given property\n        $translation = $product->translations->get('fr_FR');\n        static::assertInstanceOf(EagerProductTranslation::class, $translation);\n        static::assertNotInstanceOf(Proxy::class, $translation);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11149;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table('gh11149_locale')]\nclass Locale\n{\n    #[ORM\\Id]\n    #[ORM\\Column(length: 5)]\n    public string $code;\n\n    public function __construct(string $code)\n    {\n        $this->code = $code;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11163Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11163Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11163Bucket::class,\n            GH11163BucketItem::class,\n        ]);\n    }\n\n    public function tearDown(): void\n    {\n        parent::tearDown();\n\n        $conn = static::$sharedConn;\n        $conn->executeStatement('DELETE FROM GH11163BucketItem');\n        $conn->executeStatement('DELETE FROM GH11163Bucket');\n    }\n\n    public function testFetchEagerModeWithOrderBy(): void\n    {\n        // Load entities into database\n        $this->_em->persist($bucket = new GH11163Bucket(11163));\n        $this->_em->persist(new GH11163BucketItem(1, $bucket, 2));\n        $this->_em->persist(new GH11163BucketItem(2, $bucket, 3));\n        $this->_em->persist(new GH11163BucketItem(3, $bucket, 1));\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Fetch entity from database\n        $dql    = 'SELECT bucket FROM ' . GH11163Bucket::class . ' bucket WHERE bucket.id = :id';\n        $bucket = $this->_em->createQuery($dql)\n            ->setParameter('id', 11163)\n            ->getSingleResult();\n\n        // Assert associated entity is loaded eagerly\n        static::assertInstanceOf(GH11163Bucket::class, $bucket);\n        static::assertInstanceOf(PersistentCollection::class, $bucket->items);\n        static::assertTrue($bucket->items->isInitialized());\n\n        static::assertCount(3, $bucket->items);\n\n        // Assert order of entities\n        static::assertSame(1, $bucket->items[0]->position);\n        static::assertSame(3, $bucket->items[0]->id);\n\n        static::assertSame(2, $bucket->items[1]->position);\n        static::assertSame(1, $bucket->items[1]->id);\n\n        static::assertSame(3, $bucket->items[2]->position);\n        static::assertSame(2, $bucket->items[2]->id);\n    }\n}\n\n#[ORM\\Entity]\nclass GH11163Bucket\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    private int $id;\n\n    /** @var Collection<int, GH11163BucketItem> */\n    #[ORM\\OneToMany(\n        targetEntity: GH11163BucketItem::class,\n        mappedBy: 'bucket',\n        fetch: 'EAGER',\n    )]\n    #[ORM\\OrderBy(['position' => 'ASC'])]\n    public Collection $items;\n\n    public function __construct(int $id)\n    {\n        $this->id    = $id;\n        $this->items = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH11163BucketItem\n{\n    #[ORM\\ManyToOne(targetEntity: GH11163Bucket::class, inversedBy: 'items')]\n    #[ORM\\JoinColumn(nullable: false)]\n    private GH11163Bucket $bucket;\n\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\Column(type: 'integer')]\n    public int $position;\n\n    public function __construct(int $id, GH11163Bucket $bucket, int $position)\n    {\n        $this->id       = $id;\n        $this->bucket   = $bucket;\n        $this->position = $position;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11199Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass GH11199Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11199Root::class,\n            GH11199Parent::class,\n            GH11199Foo::class,\n            GH11199Baz::class,\n            GH11199AbstractLeaf::class,\n        ]);\n    }\n\n    public static function dqlStatements(): Generator\n    {\n        yield ['SELECT e FROM ' . GH11199Root::class . ' e', \"/WHERE g0_.asset_type IN \\('root', 'foo', 'baz'\\)$/\"];\n        yield ['SELECT e FROM ' . GH11199Parent::class . ' e', \"/WHERE g0_.asset_type IN \\('foo'\\)$/\"];\n        yield ['SELECT e FROM ' . GH11199Foo::class . ' e', \"/WHERE g0_.asset_type IN \\('foo'\\)$/\"];\n        yield ['SELECT e FROM ' . GH11199Baz::class . ' e', \"/WHERE g0_.asset_type IN \\('baz'\\)$/\"];\n        yield ['SELECT e FROM ' . GH11199AbstractLeaf::class . ' e', '/WHERE 1=0/'];\n    }\n\n    #[DataProvider('dqlStatements')]\n    public function testGH11199(string $dql, string $expectedDiscriminatorValues): void\n    {\n        $query = $this->_em->createQuery($dql);\n        $sql   = $query->getSQL();\n\n        self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'gh11199')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'asset_type', type: 'string')]\n#[ORM\\DiscriminatorMap([\n    'root' => GH11199Root::class,\n    'foo'  => GH11199Foo::class,\n    'baz'  => GH11199Baz::class,\n])]\nclass GH11199Root\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'IDENTITY')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\Entity]\nabstract class GH11199Parent extends GH11199Root\n{\n}\n\n#[ORM\\Entity]\nclass GH11199Foo extends GH11199Parent\n{\n}\n\n#[ORM\\Entity]\nclass GH11199Baz extends GH11199Root\n{\n}\n\n#[ORM\\Entity]\nabstract class GH11199AbstractLeaf extends GH11199Root\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11341Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass GH11341Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            IntegerBaseClass::class,\n            IntegerFooEntity::class,\n            IntegerBarEntity::class,\n            StringAsIntBaseClass::class,\n            StringAsIntFooEntity::class,\n            StringAsIntBarEntity::class,\n            StringBaseClass::class,\n            StringFooEntity::class,\n            StringBarEntity::class,\n        ]);\n    }\n\n    public static function dqlStatements(): Generator\n    {\n        yield ['SELECT e FROM ' . IntegerBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \\(1, 2\\)$/'];\n        yield ['SELECT e FROM ' . IntegerFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(1\\)$/'];\n        yield ['SELECT e FROM ' . IntegerBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(2\\)$/'];\n        yield ['SELECT e FROM ' . StringAsIntBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'1\\', \\'2\\'\\)$/'];\n        yield ['SELECT e FROM ' . StringAsIntFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'1\\'\\)$/'];\n        yield ['SELECT e FROM ' . StringAsIntBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'2\\'\\)$/'];\n        yield ['SELECT e FROM ' . StringBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'1\\', \\'2\\'\\)$/'];\n        yield ['SELECT e FROM ' . StringFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'1\\'\\)$/'];\n        yield ['SELECT e FROM ' . StringBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \\(\\'2\\'\\)$/'];\n    }\n\n    #[DataProvider('dqlStatements')]\n    public function testDiscriminatorValue(string $dql, string $expectedDiscriminatorValues): void\n    {\n        $query = $this->_em->createQuery($dql);\n        $sql   = $query->getSQL();\n\n        self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql);\n    }\n\n    public static function dqlStatementsForInstanceOf(): Generator\n    {\n        yield [IntegerBaseClass::class, IntegerFooEntity::class];\n        yield [StringBaseClass::class, StringFooEntity::class];\n        yield [StringAsIntBaseClass::class, StringAsIntFooEntity::class];\n    }\n\n    /**\n     * @phpstan-param class-string $baseClass\n     * @phpstan-param class-string $inheritedClass\n     */\n    #[DataProvider('dqlStatementsForInstanceOf')]\n    public function testInstanceOf(string $baseClass, string $inheritedClass): void\n    {\n        $this->_em->persist(new $inheritedClass());\n        $this->_em->flush();\n\n        $dql = 'SELECT p FROM ' . $baseClass . ' p WHERE p INSTANCE OF ' . $baseClass;\n\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertContainsOnlyInstancesOf($baseClass, $result);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'integer_discriminator')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'integer')]\n#[ORM\\DiscriminatorMap([\n    1 => IntegerFooEntity::class,\n    2 => IntegerBarEntity::class,\n])]\nclass IntegerBaseClass\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'IDENTITY')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\Entity]\nclass IntegerFooEntity extends IntegerBaseClass\n{\n}\n\n#[ORM\\Entity]\nclass IntegerBarEntity extends IntegerBaseClass\n{\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'string_as_int_discriminator')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap([\n    1 => StringAsIntFooEntity::class,\n    2 => StringAsIntBarEntity::class,\n])]\nclass StringAsIntBaseClass\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'IDENTITY')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\Entity]\nclass StringAsIntFooEntity extends StringAsIntBaseClass\n{\n}\n\n#[ORM\\Entity]\nclass StringAsIntBarEntity extends StringAsIntBaseClass\n{\n}\n\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'string_discriminator')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap([\n    '1' => StringFooEntity::class,\n    '2' => StringBarEntity::class,\n])]\nclass StringBaseClass\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'IDENTITY')]\n    #[ORM\\Column(type: 'integer')]\n    private int|null $id = null;\n}\n\n#[ORM\\Entity]\nclass StringFooEntity extends StringBaseClass\n{\n}\n\n#[ORM\\Entity]\nclass StringBarEntity extends StringBaseClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCart.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11386;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass GH11386EntityCart\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    private int|null $id = null;\n\n    #[Column]\n    private int|null $amount = null;\n\n    #[OneToOne(inversedBy: 'cart', cascade: ['persist', 'remove'], orphanRemoval: true)]\n    private GH11386EntityCustomer|null $customer = null;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getAmount(): int|null\n    {\n        return $this->amount;\n    }\n\n    public function setAmount(int $amount): static\n    {\n        $this->amount = $amount;\n\n        return $this;\n    }\n\n    public function getCustomer(): GH11386EntityCustomer|null\n    {\n        return $this->customer;\n    }\n\n    public function setCustomer(GH11386EntityCustomer|null $customer): self\n    {\n        $this->customer = $customer;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCustomer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11386;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\n\n#[Entity]\nclass GH11386EntityCustomer\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column]\n    private int|null $id = null;\n\n    #[Column]\n    private string|null $name = null;\n\n    #[Column(type: 'smallint', nullable: true, enumType: GH11386EnumType::class, options: ['unsigned' => true])]\n    private GH11386EnumType|null $type = null;\n\n    #[OneToOne(mappedBy: 'customer')]\n    private GH11386EntityCart|null $cart = null;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): static\n    {\n        $this->name = $name;\n\n        return $this;\n    }\n\n    public function getType(): GH11386EnumType|null\n    {\n        return $this->type;\n    }\n\n    public function setType(GH11386EnumType $type): static\n    {\n        $this->type = $type;\n\n        return $this;\n    }\n\n    public function getCart(): GH11386EntityCart|null\n    {\n        return $this->cart;\n    }\n\n    public function setCart(GH11386EntityCart|null $cart): self\n    {\n        // unset the owning side of the relation if necessary\n        if ($cart === null && $this->cart !== null) {\n            $this->cart->setCustomer(null);\n        }\n\n        // set the owning side of the relation if necessary\n        if ($cart !== null && $cart->getCustomer() !== $this) {\n            $cart->setCustomer($this);\n        }\n\n        $this->cart = $cart;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EnumType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11386;\n\nenum GH11386EnumType: int\n{\n    case MALE   = 1;\n    case FEMALE = 2;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11386/GH11386Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH11386;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH11386Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH11386EntityCart::class,\n            GH11386EntityCustomer::class,\n        );\n    }\n\n    public function testInitializeClonedProxy(): void\n    {\n        $cart = new GH11386EntityCart();\n        $cart->setAmount(1000);\n\n        $customer = new GH11386EntityCustomer();\n        $customer->setName('John Doe')\n            ->setType(GH11386EnumType::MALE)\n            ->setCart($cart);\n\n        $this->_em->persist($cart);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $cart     = $this->_em->find(GH11386EntityCart::class, 1);\n        $customer = clone $cart->getCustomer();\n        self::assertEquals('John Doe', $customer->getName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11487Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11487Test extends OrmFunctionalTestCase\n{\n    public function testItThrowsASyntaxErrorOnUnfinishedQuery(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Syntax Error');\n        $this->_em->createQuery('UPDATE Doctrine\\Tests\\ORM\\Functional\\Ticket\\TaxType t SET t.default =')->execute();\n    }\n}\n\n#[Entity]\nclass TaxType\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue]\n    public int|null $id = null;\n\n    #[Column]\n    public bool $default = false;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11500Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11500Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11500AbstractTestEntity::class,\n            GH11500TestEntityOne::class,\n            GH11500TestEntityTwo::class,\n            GH11500TestEntityHolder::class,\n        ]);\n    }\n\n    /** @throws ORMException */\n    public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void\n    {\n        $testEntityOne    = new GH11500TestEntityOne();\n        $testEntityTwo    = new GH11500TestEntityTwo();\n        $testEntityHolder = new GH11500TestEntityHolder();\n\n        $testEntityOne->testEntityHolder = $testEntityHolder;\n        $testEntityHolder->testEntityOnes->add($testEntityOne);\n\n        $testEntityTwo->testEntityHolder = $testEntityHolder;\n        $testEntityHolder->testEntityTwos->add($testEntityTwo);\n\n        $em = $this->getEntityManager();\n        $em->persist($testEntityOne);\n        $em->persist($testEntityTwo);\n        $em->persist($testEntityHolder);\n        $em->flush();\n\n        $testEntityTwosBeforeRemovalOfTestEntityOnes = $testEntityHolder->testEntityTwos->toArray();\n\n        $testEntityHolder->testEntityOnes = new ArrayCollection();\n        $em->persist($testEntityHolder);\n        $em->flush();\n        $em->refresh($testEntityHolder);\n\n        static::assertEmpty($testEntityHolder->testEntityOnes->toArray(), 'All records should have been deleted');\n        static::assertEquals($testEntityTwosBeforeRemovalOfTestEntityOnes, $testEntityHolder->testEntityTwos->toArray(), 'Different Entity\\'s records should not have been deleted');\n    }\n}\n\n\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'one_to_many_single_table_inheritance_test_entities')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap(['test_entity_one' => 'GH11500TestEntityOne', 'test_entity_two' => 'GH11500TestEntityTwo'])]\nclass GH11500AbstractTestEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n}\n\n\n#[ORM\\Entity]\nclass GH11500TestEntityOne extends GH11500AbstractTestEntity\n{\n    #[ORM\\ManyToOne(inversedBy:'testEntityOnes')]\n    #[ORM\\JoinColumn(name:'test_entity_holder_id', referencedColumnName:'id')]\n    public GH11500TestEntityHolder|null $testEntityHolder = null;\n}\n\n#[ORM\\Entity]\nclass GH11500TestEntityTwo extends GH11500AbstractTestEntity\n{\n    #[ORM\\ManyToOne(inversedBy:'testEntityTwos')]\n    #[ORM\\JoinColumn(name:'test_entity_holder_id', referencedColumnName:'id')]\n    public GH11500TestEntityHolder|null $testEntityHolder = null;\n}\n\n#[ORM\\Entity]\nclass GH11500TestEntityHolder\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    #[ORM\\GeneratedValue]\n    public int|null $id = null;\n\n    #[ORM\\OneToMany(targetEntity: 'GH11500TestEntityOne', mappedBy: 'testEntityHolder', orphanRemoval: true)]\n    public Collection $testEntityOnes;\n\n    #[ORM\\OneToMany(targetEntity: 'GH11500TestEntityTwo', mappedBy: 'testEntityHolder', orphanRemoval: true)]\n    public Collection $testEntityTwos;\n\n    public function __construct()\n    {\n        $this->testEntityOnes = new ArrayCollection();\n        $this->testEntityTwos = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11501Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11501Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11501AbstractTestEntity::class,\n            GH11501TestEntityOne::class,\n            GH11501TestEntityTwo::class,\n            GH11501TestEntityHolder::class,\n        ]);\n    }\n\n    /** @throws ORMException */\n    public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void\n    {\n        $testEntityOne    = new GH11501TestEntityOne();\n        $testEntityTwo    = new GH11501TestEntityTwo();\n        $testEntityHolder = new GH11501TestEntityHolder();\n\n        $testEntityOne->testEntityHolder = $testEntityHolder;\n        $testEntityHolder->testEntities->add($testEntityOne);\n\n        $testEntityTwo->testEntityHolder = $testEntityHolder;\n        $testEntityHolder->testEntities->add($testEntityTwo);\n\n        $em = $this->getEntityManager();\n        $em->persist($testEntityOne);\n        $em->persist($testEntityTwo);\n        $em->persist($testEntityHolder);\n        $em->flush();\n\n        $testEntityHolder->testEntities = new ArrayCollection();\n        $em->persist($testEntityHolder);\n        $em->flush();\n        $em->refresh($testEntityHolder);\n\n        static::assertEmpty($testEntityHolder->testEntities->toArray(), 'All records should have been deleted');\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'one_to_many_single_table_inheritance_test_entities_parent_join')]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap([\n    'test_entity_one' => 'GH11501TestEntityOne',\n    'test_entity_two' => 'GH11501TestEntityTwo',\n])]\nclass GH11501AbstractTestEntity\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\ManyToOne(targetEntity: 'GH11501TestEntityHolder', inversedBy: 'testEntities')]\n    #[ORM\\JoinColumn(name: 'test_entity_holder_id', referencedColumnName: 'id')]\n    public GH11501TestEntityHolder $testEntityHolder;\n}\n\n\n#[ORM\\Entity]\nclass GH11501TestEntityOne extends GH11501AbstractTestEntity\n{\n}\n\n#[ORM\\Entity]\nclass GH11501TestEntityTwo extends GH11501AbstractTestEntity\n{\n}\n\n#[ORM\\Entity]\nclass GH11501TestEntityHolder\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public int $id;\n\n    #[ORM\\OneToMany(\n        targetEntity: 'GH11501AbstractTestEntity',\n        mappedBy: 'testEntityHolder',\n        orphanRemoval: true,\n    )]\n    public Collection $testEntities;\n\n    public function __construct()\n    {\n        $this->testEntities = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11524Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\Tests\\Models\\GH11524\\GH11524Entity;\nuse Doctrine\\Tests\\Models\\GH11524\\GH11524Listener;\nuse Doctrine\\Tests\\Models\\GH11524\\GH11524Relation;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH11524Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH11524Entity::class,\n            GH11524Relation::class,\n        );\n\n        $this->_em->getEventManager()->addEventListener(Events::postLoad, new GH11524Listener());\n    }\n\n    public function testPostLoadCalledOnProxy(): void\n    {\n        $relation       = new GH11524Relation();\n        $relation->name = 'test';\n        $this->_em->persist($relation);\n\n        $entity           = new GH11524Entity();\n        $entity->relation = $relation;\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $reloadedEntity = $this->_em->find(GH11524Entity::class, $entity->id);\n\n        $reloadedRelation = $reloadedEntity->relation;\n\n        $this->assertTrue($this->isUninitializedObject($reloadedRelation));\n\n        $this->assertSame('fake', $reloadedRelation->getTranslation(), 'The property set by the postLoad listener must get initialized on usage.');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH11982Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Schema\\Index;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function method_exists;\nuse function reset;\n\nclass GH11982Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH11982ColumnIndex::class,\n        ]);\n    }\n\n    #[Group('GH-11982')]\n    public function testSchema(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSQLPlatform) {\n            self::markTestSkipped('This test does not work on psql.');\n        }\n\n        if (! method_exists(Index::class, 'getIndexedColumns')) {\n            self::markTestSkipped('This test requires doctrine/dbal >=4.3');\n        }\n\n        $indexes = $this->createSchemaManager()\n            ->introspectTable('GH11982ColumnIndex')\n            ->getIndexes();\n\n        self::assertCount(3, $indexes); // primary + 2 custom indexes\n        self::assertArrayHasKey('class_idx', $indexes);\n\n        unset($indexes['primary']);\n        unset($indexes['class_idx']);\n        $unnamedIndexColumns = reset($indexes)->getIndexedColumns();\n        self::assertCount(1, $unnamedIndexColumns);\n        self::assertEquals(\n            'indexTrue',\n            $unnamedIndexColumns[0]->getColumnName()->toString(),\n        );\n    }\n\n    #[Group('GH-11982')]\n    public function testSchemaLegacyDbal(): void\n    {\n        if ($this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSQLPlatform) {\n            self::markTestSkipped('This test does not work on psql.');\n        }\n\n        if (method_exists(Index::class, 'getIndexedColumns')) {\n            self::markTestSkipped('This test requires doctrine/dbal <4.3');\n        }\n\n        $indexes = $this->createSchemaManager()\n            ->introspectTable('GH11982ColumnIndex')\n            ->getIndexes();\n\n        self::assertCount(3, $indexes); // primary + 2 custom indexes\n        self::assertArrayHasKey('class_idx', $indexes);\n\n        unset($indexes['primary']);\n        unset($indexes['class_idx']);\n        $unnamedIndexColumns = reset($indexes)->getColumns();\n        self::assertCount(1, $unnamedIndexColumns);\n        self::assertEquals('indexTrue', $unnamedIndexColumns[0]);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Index(\n    name: 'class_idx',\n    fields: ['classIndex'],\n    flags: ['fulltext'],\n    options: ['test'],\n)]\nclass GH11982ColumnIndex\n{\n    #[ORM\\Id]\n    #[ORM\\Column]\n    public string $noIndex;\n\n    #[ORM\\Column(index: true)]\n    public string $indexTrue;\n\n    #[ORM\\Column]\n    public string $classIndex;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH12166/GH12166Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH12166;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH12166Test extends OrmFunctionalTestCase\n{\n    public function testProxyWithReadonlyIdIsNotInitializedImmediately(): void\n    {\n        $this->createSchemaForModels(LazyEntityWithReadonlyId::class);\n        $this->_em->persist(new LazyEntityWithReadonlyId(123, 'Test Name'));\n        $this->_em->flush();\n        $this->_em->clear();\n        $proxy = $this->_em->getReference(LazyEntityWithReadonlyId::class, 123);\n\n        $reflClass = $this->_em->getClassMetadata(LazyEntityWithReadonlyId::class)->reflClass;\n        self::assertTrue(\n            $this->isUninitializedObject($proxy),\n            'Proxy should remain uninitialized after creation',\n        );\n\n        $id = $proxy->getId();\n        self::assertSame(123, $id);\n        self::assertTrue(\n            $this->isUninitializedObject($proxy),\n            'Proxy should remain uninitialized after accessing ID',\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH12166/LazyEntityWithReadonlyId.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH12166;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'gh12166_lazy_entity')]\nclass LazyEntityWithReadonlyId\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    private readonly int $id;\n\n    #[Column(type: 'string')]\n    private string $name;\n\n    public function __construct(int $id, string $name)\n    {\n        $this->id   = $id;\n        $this->name = $name;\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH12174Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Tools\\ResolveTargetEntityListener;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH12174Test extends OrmFunctionalTestCase\n{\n    use VerifyDeprecations;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10431'); // make test fail on 2.x; in 3.x, it would even throw\n\n        $resolveTargetEntity = new ResolveTargetEntityListener();\n\n        $resolveTargetEntity->addResolveTargetEntity(GH12174Smurf::class, GH12174BlueSmurf::class, []);\n\n        $this->_em->getEventManager()->addEventSubscriber($resolveTargetEntity);\n\n        $this->createSchemaForModels(\n            GH12174Smurf::class,\n            GH12174BlueSmurf::class,\n            GH12174PapaSmurf::class,\n        );\n    }\n\n    public function testMappedSuperclassNameCanBeUsedToResolveTargetEntityClass(): void\n    {\n        $smurf = $this->_em->getClassMetadata(GH12174Smurf::class);\n        self::assertTrue($smurf->isMappedSuperclass);\n        self::assertSame(GH12174Smurf::class, $smurf->getName());\n        self::assertSame(GH12174BlueSmurf::class, $smurf->getAssociationMapping('children')['targetEntity']);\n\n        $blue = $this->_em->getClassMetadata(GH12174BlueSmurf::class);\n        self::assertFalse($blue->isMappedSuperclass);\n        self::assertSame(GH12174BlueSmurf::class, $blue->getName());\n\n        $papa = $this->_em->getClassMetadata(GH12174PapaSmurf::class);\n        self::assertFalse($papa->isMappedSuperclass);\n        self::assertSame(GH12174PapaSmurf::class, $papa->getName());\n    }\n}\n\n#[ORM\\MappedSuperclass]\nclass GH12174Smurf\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: Types::INTEGER)]\n    public int $id;\n\n    #[ManyToOne(inversedBy: 'children', targetEntity: self::class)]\n    private GH12174Smurf $parent;\n\n    /** @var Collection<self::class> */\n    #[OneToMany(targetEntity: self::class, mappedBy: 'parent')]\n    private Collection $children;\n}\n\n#[ORM\\Entity]\nclass GH12174BlueSmurf extends GH12174Smurf\n{\n}\n\n#[ORM\\Entity]\nclass GH12174PapaSmurf extends GH12174Smurf\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH12183Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH12183Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $article = new CmsArticle();\n\n        $article->topic = 'Loomings';\n        $article->text  = 'Call me Ishmael.';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testPaginatorCountWithTreeWalkerAfterQueryHasBeenExecuted(): void\n    {\n        $query = $this->_em->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a');\n\n        // Paginator::count is right when the query has not yet been executed\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers(false);\n        self::assertSame(1, $paginator->count());\n\n        // Execute the query\n        $result = $query->getResult();\n        self::assertCount(1, $result);\n\n        $paginator = new Paginator($query);\n        $paginator->setUseOutputWalkers(false);\n        self::assertSame(1, $paginator->count());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH12254Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH12254Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH12254EntityA::class,\n        ]);\n\n        $this->_em->persist(new GH12254EntityA());\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testFindByEmptyArrayShouldReturnEmptyArray(): void\n    {\n        // pretend we are starting afresh\n        $this->_em = $this->getEntityManager();\n        $result    = $this->_em->getRepository(GH12254EntityA::class)->findBy(['id' => []]);\n        $this->assertEmpty($result);\n    }\n\n    public function testFindByInNullableField(): void\n    {\n        $this->_em = $this->getEntityManager();\n        $result    = $this->_em->getRepository(GH12254EntityA::class)->findBy(['name' => []]);\n        $this->assertEmpty($result);\n    }\n}\n\n#[Entity]\nclass GH12254EntityA\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[Column(type: 'string', nullable: true)]\n    public string|null $name = null;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH2947Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\n#[Group('GH-2947')]\nclass GH2947Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->resultCache = new ArrayAdapter();\n\n        parent::setUp();\n\n        $this->createSchemaForModels(GH2947Car::class);\n    }\n\n    public function testIssue(): void\n    {\n        $this->createData();\n        $this->getQueryLog()->reset()->enable();\n\n        $query = $this->createQuery();\n        self::assertEquals('BMW', (string) $query->getSingleResult());\n        $this->assertQueryCount(1);\n\n        $this->updateData();\n        self::assertEquals('BMW', (string) $query->getSingleResult());\n        $this->assertQueryCount(2);\n\n        $query->expireResultCache(true);\n        self::assertEquals('Dacia', (string) $query->getSingleResult());\n        $this->assertQueryCount(3);\n\n        $query->expireResultCache(false);\n        self::assertEquals('Dacia', (string) $query->getSingleResult());\n        $this->assertQueryCount(3);\n    }\n\n    private function createQuery(): Query\n    {\n        return $this->_em->createQueryBuilder()\n                         ->select('car')\n                         ->from(GH2947Car::class, 'car')\n                         ->getQuery()\n                         ->enableResultCache(3600, 'foo-cache-id');\n    }\n\n    private function createData(): void\n    {\n        $this->_em->persist(new GH2947Car('BMW'));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    private function updateData(): void\n    {\n        $this->_em->createQueryBuilder()\n                  ->update(GH2947Car::class, 'car')\n                  ->set('car.brand', ':newBrand')\n                  ->where('car.brand = :oldBrand')\n                  ->setParameter('newBrand', 'Dacia')\n                  ->setParameter('oldBrand', 'BMW')\n                  ->getQuery()\n                  ->execute();\n    }\n}\n\n#[Table(name: 'GH2947_car')]\n#[Entity]\nclass GH2947Car implements Stringable\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 25)]\n        #[GeneratedValue(strategy: 'NONE')]\n        public string $brand,\n    ) {\n    }\n\n    public function __toString(): string\n    {\n        return $this->brand;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5562Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function mt_getrandmax;\nuse function random_int;\n\nfinal class GH5562Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH5562User::class,\n            GH5562Manager::class,\n            GH5562Merchant::class,\n        );\n    }\n\n    #[Group('GH-5562')]\n    public function testCacheShouldBeUpdatedWhenAssociationChanges(): void\n    {\n        $manager  = new GH5562Manager();\n        $merchant = new GH5562Merchant();\n\n        $manager->username = 'username';\n        $manager->merchant = $merchant;\n        $merchant->manager = $manager;\n\n        $merchant->name = 'Merchant';\n\n        $this->_em->persist($merchant);\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $merchant = $this->_em->find(GH5562Merchant::class, $merchant->id);\n\n        $merchant->name              = random_int(0, mt_getrandmax());\n        $merchant->manager->username = 'usernameUPDATE';\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $merchant = $this->_em->find(GH5562Merchant::class, $merchant->id);\n\n        self::assertEquals('usernameUPDATE', $merchant->manager->username);\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'NONSTRICT_READ_WRITE')]\nclass GH5562Merchant\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    public $id;\n\n    /** @var GH5562Manager */\n    #[OneToOne(targetEntity: GH5562Manager::class, mappedBy: 'merchant')]\n    #[Cache(usage: 'NONSTRICT_READ_WRITE')]\n    public $manager;\n\n    /** @var string */\n    #[Column(name: 'name', type: 'string', length: 255, nullable: false)]\n    public $name;\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['MANAGER' => GH5562Manager::class])]\nabstract class GH5562User\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id', type: 'integer')]\n    #[GeneratedValue(strategy: 'IDENTITY')]\n    public $id;\n}\n\n#[Entity]\n#[Cache(usage: 'NONSTRICT_READ_WRITE')]\nclass GH5562Manager extends GH5562User\n{\n    /** @var string */\n    #[Column]\n    public $username;\n\n    /** @var GH5562Merchant */\n    #[OneToOne(targetEntity: GH5562Merchant::class, inversedBy: 'manager')]\n    public $merchant;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5742Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH5742Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH5742Person::class,\n            GH5742Toothbrush::class,\n            GH5742ToothpasteBrand::class,\n        );\n    }\n\n    public function testUpdateOneToOneToNewEntityBeforePreviousEntityCanBeRemoved(): void\n    {\n        $person             = new GH5742Person();\n        $oldToothbrush      = new GH5742Toothbrush();\n        $person->toothbrush = $oldToothbrush;\n\n        $this->_em->persist($person);\n        $this->_em->persist($oldToothbrush);\n        $this->_em->flush();\n\n        $oldToothbrushId = $oldToothbrush->id;\n\n        $newToothbrush      = new GH5742Toothbrush();\n        $person->toothbrush = $newToothbrush;\n\n        $this->_em->remove($oldToothbrush);\n        $this->_em->persist($newToothbrush);\n\n        // The flush operation will have to make sure the new toothbrush\n        // has been written to the database\n        // _before_ the person can be updated to refer to it.\n        // Likewise, the update must have happened _before_ the old\n        // toothbrush can be removed (non-nullable FK constraint).\n\n        $this->_em->flush();\n\n        $this->_em->clear();\n        self::assertSame($newToothbrush->id, $this->_em->find(GH5742Person::class, $person->id)->toothbrush->id);\n        self::assertNull($this->_em->find(GH5742Toothbrush::class, $oldToothbrushId));\n    }\n\n    public function testManyToManyCollectionUpdateBeforeRemoval(): void\n    {\n        $person             = new GH5742Person();\n        $person->toothbrush = new GH5742Toothbrush(); // to satisfy not-null constraint\n        $this->_em->persist($person);\n\n        $oldMice = new GH5742ToothpasteBrand();\n        $this->_em->persist($oldMice);\n\n        $person->preferredBrands->set(1, $oldMice);\n        $this->_em->flush();\n\n        $oldBrandId = $oldMice->id;\n\n        $newSpice = new GH5742ToothpasteBrand();\n        $this->_em->persist($newSpice);\n\n        $person->preferredBrands->set(1, $newSpice);\n\n        $this->_em->remove($oldMice);\n\n        // The flush operation will have to make sure the new brand\n        // has been written to the database _before_ it can be referred\n        // to from the m2m join table.\n        // Likewise, the old join table entry must have been removed\n        // _before_ the old brand can be removed.\n\n        $this->_em->flush();\n\n        $this->_em->clear();\n        self::assertCount(1, $this->_em->find(GH5742Person::class, $person->id)->preferredBrands);\n        self::assertNull($this->_em->find(GH5742ToothpasteBrand::class, $oldBrandId));\n    }\n}\n\n#[ORM\\Entity]\nclass GH5742Person\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\OneToOne(targetEntity: 'GH5742Toothbrush', cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    public GH5742Toothbrush $toothbrush;\n\n    /** @var Collection<GH5742ToothpasteBrand> */\n    #[ORM\\ManyToMany(targetEntity: 'GH5742ToothpasteBrand')]\n    #[ORM\\JoinTable('gh5742person_gh5742toothpastebrand')]\n    #[ORM\\JoinColumn(name: 'person_id', referencedColumnName: 'id', onDelete: 'CASCADE')]\n    #[ORM\\InverseJoinColumn(name: 'brand_id', referencedColumnName: 'id')]\n    public Collection $preferredBrands;\n\n    public function __construct()\n    {\n        $this->preferredBrands = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH5742Toothbrush\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n}\n\n#[ORM\\Entity]\nclass GH5742ToothpasteBrand\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5762Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_unique;\nuse function count;\n\n#[Group('GH-5762')]\nclass GH5762Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH5762Driver::class,\n            GH5762DriverRide::class,\n            GH5762Car::class,\n        );\n    }\n\n    public function testIssue(): void\n    {\n        $result = $this->fetchData();\n\n        self::assertInstanceOf(GH5762Driver::class, $result);\n        self::assertInstanceOf(PersistentCollection::class, $result->driverRides);\n        self::assertInstanceOf(GH5762DriverRide::class, $result->driverRides->get(0));\n        self::assertInstanceOf(GH5762Car::class, $result->driverRides->get(0)->car);\n\n        $cars = [];\n        foreach ($result->driverRides as $ride) {\n            $cars[] = $ride->car->brand;\n        }\n\n        self::assertEquals(count($cars), count(array_unique($cars)));\n\n        self::assertContains('BMW', $cars);\n        self::assertContains('Crysler', $cars);\n        self::assertContains('Dodge', $cars);\n        self::assertContains('Mercedes', $cars);\n        self::assertContains('Volvo', $cars);\n    }\n\n    private function fetchData(): mixed\n    {\n        $this->createData();\n\n        $qb = $this->_em->createQueryBuilder();\n        $qb->select('d, dr, c')\n            ->from(GH5762Driver::class, 'd')\n            ->leftJoin('d.driverRides', 'dr')\n            ->leftJoin('dr.car', 'c')\n            ->where('d.id = 1');\n\n        return $qb->getQuery()->getSingleResult();\n    }\n\n    private function createData(): void\n    {\n        $car1 = new GH5762Car('BMW', '7 Series');\n        $car2 = new GH5762Car('Crysler', '300');\n        $car3 = new GH5762Car('Dodge', 'Dart');\n        $car4 = new GH5762Car('Mercedes', 'C-Class');\n        $car5 = new GH5762Car('Volvo', 'XC90');\n\n        $driver = new GH5762Driver(1, 'John Doe');\n\n        $ride1 = new GH5762DriverRide($driver, $car1);\n        $ride2 = new GH5762DriverRide($driver, $car2);\n        $ride3 = new GH5762DriverRide($driver, $car3);\n        $ride4 = new GH5762DriverRide($driver, $car4);\n        $ride5 = new GH5762DriverRide($driver, $car5);\n\n        $this->_em->persist($car1);\n        $this->_em->persist($car2);\n        $this->_em->persist($car3);\n        $this->_em->persist($car4);\n        $this->_em->persist($car5);\n\n        $this->_em->persist($driver);\n\n        $this->_em->persist($ride1);\n        $this->_em->persist($ride2);\n        $this->_em->persist($ride3);\n        $this->_em->persist($ride4);\n        $this->_em->persist($ride5);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n\n#[Table(name: 'driver')]\n#[Entity]\nclass GH5762Driver\n{\n    /** @phpstan-var Collection<int, GH5762DriverRide> */\n    #[OneToMany(targetEntity: 'GH5762DriverRide', mappedBy: 'driver')]\n    public $driverRides;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        #[GeneratedValue(strategy: 'NONE')]\n        public int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n        $this->driverRides = new ArrayCollection();\n    }\n}\n\n#[Table(name: 'driver_ride')]\n#[Entity]\nclass GH5762DriverRide\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'GH5762Driver', inversedBy: 'driverRides')]\n        #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')]\n        public GH5762Driver $driver,\n        #[Id]\n        #[ManyToOne(targetEntity: 'GH5762Car', inversedBy: 'carRides')]\n        #[JoinColumn(name: 'car', referencedColumnName: 'brand')]\n        public GH5762Car $car,\n    ) {\n        $this->driver->driverRides->add($this);\n        $this->car->carRides->add($this);\n    }\n}\n\n#[Table(name: 'car')]\n#[Entity]\nclass GH5762Car\n{\n    /** @phpstan-var Collection<int, GH5762DriverRide> */\n    #[OneToMany(targetEntity: 'GH5762DriverRide', mappedBy: 'car')]\n    public $carRides;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 25)]\n        #[GeneratedValue(strategy: 'NONE')]\n        public string $brand,\n        #[Column(type: 'string', length: 255)]\n        public string $model,\n    ) {\n        $this->carRides = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5804Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Id\\AbstractIdGenerator;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\CustomIdGenerator;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-5804')]\nfinal class GH5804Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType(GH5804Type::NAME, GH5804Type::class);\n\n        $this->createSchemaForModels(GH5804Article::class);\n    }\n\n    public function testTextColumnSaveAndRetrieve2(): void\n    {\n        $firstArticle       = new GH5804Article();\n        $firstArticle->text = 'Max';\n        $this->_em->persist($firstArticle);\n        $this->_em->flush();\n\n        self::assertSame(1, $firstArticle->version);\n\n        $firstArticle->text = 'Moritz';\n        $this->_em->persist($firstArticle);\n        $this->_em->flush();\n\n        self::assertSame(2, $firstArticle->version);\n    }\n}\n\nfinal class GH5804Generator extends AbstractIdGenerator\n{\n    public function generateId(EntityManagerInterface $em, object|null $entity): string\n    {\n        return 'test5804';\n    }\n}\n\nfinal class GH5804Type extends Type\n{\n    public const NAME = 'GH5804Type';\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string|null\n    {\n        if (empty($value)) {\n            return null;\n        }\n\n        return 'testGh5804DbValue';\n    }\n}\n\n#[Entity]\nclass GH5804Article\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'GH5804Type', length: 255)]\n    #[GeneratedValue(strategy: 'CUSTOM')]\n    #[CustomIdGenerator(class: GH5804Generator::class)]\n    public $id;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    /** @var string */\n    #[Column(type: 'text')]\n    public $text;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5887Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\nuse function assert;\n\n#[Group('GH-5887')]\nclass GH5887Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType(GH5887CustomIdObjectType::NAME, GH5887CustomIdObjectType::class);\n\n        $this->createSchemaForModels(GH5887Cart::class, GH5887Customer::class);\n    }\n\n    public function testLazyLoadsForeignEntitiesInOneToOneRelationWhileHavingCustomIdObject(): void\n    {\n        $customerId = new GH5887CustomIdObject(1);\n        $customer   = new GH5887Customer();\n        $customer->setId($customerId);\n\n        $cartId = 2;\n        $cart   = new GH5887Cart();\n        $cart->setId($cartId);\n        $cart->setCustomer($customer);\n\n        $this->_em->persist($customer);\n        $this->_em->persist($cart);\n        $this->_em->flush();\n\n        // Clearing cached entities\n        $this->_em->clear();\n\n        $customerRepository = $this->_em->getRepository(GH5887Customer::class);\n        $customer           = $customerRepository->createQueryBuilder('c')\n            ->where('c.id = :id')\n            ->setParameter('id', $customerId->getId())\n            ->getQuery()\n            ->getOneOrNullResult();\n        assert($customer instanceof GH5887Customer);\n\n        self::assertInstanceOf(GH5887Cart::class, $customer->getCart());\n    }\n}\n\n#[Entity]\nclass GH5887Cart\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'NONE')]\n    private int|null $id = null;\n\n    /**\n     * One Cart has One Customer.\n     */\n    #[OneToOne(targetEntity: 'GH5887Customer', inversedBy: 'cart')]\n    #[JoinColumn(name: 'customer_id', referencedColumnName: 'id')]\n    private GH5887Customer|null $customer = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setId(int $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getCustomer(): GH5887Customer\n    {\n        return $this->customer;\n    }\n\n    public function setCustomer(GH5887Customer $customer): void\n    {\n        if ($this->customer !== $customer) {\n            $this->customer = $customer;\n            $customer->setCart($this);\n        }\n    }\n}\n\n#[Entity]\nclass GH5887Customer\n{\n    #[Id]\n    #[Column(type: 'GH5887CustomIdObject', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    private GH5887CustomIdObject|null $id = null;\n\n    /**\n     * One Customer has One Cart.\n     */\n    #[OneToOne(targetEntity: 'GH5887Cart', mappedBy: 'customer')]\n    private GH5887Cart|null $cart = null;\n\n    public function getId(): GH5887CustomIdObject\n    {\n        return $this->id;\n    }\n\n    public function setId(GH5887CustomIdObject $id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getCart(): GH5887Cart\n    {\n        return $this->cart;\n    }\n\n    public function setCart(GH5887Cart $cart): void\n    {\n        if ($this->cart !== $cart) {\n            $this->cart = $cart;\n            $cart->setCustomer($this);\n        }\n    }\n}\n\nclass GH5887CustomIdObject implements Stringable\n{\n    public function __construct(private int $id)\n    {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function __toString(): string\n    {\n        return 'non existing id';\n    }\n}\n\nclass GH5887CustomIdObjectType extends StringType\n{\n    public const NAME = 'GH5887CustomIdObject';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed\n    {\n        return $value->getId();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): GH5887CustomIdObject\n    {\n        return new GH5887CustomIdObject((int) $value);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5988Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function str_replace;\n\n/**\n * Functional tests for the Class Table Inheritance mapping strategy with custom id object types.\n */\n#[Group('GH5988')]\nfinal class GH5988Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! DBALType::hasType(GH5988CustomIdObjectHashType::class)) {\n            DBALType::addType(GH5988CustomIdObjectHashType::class, GH5988CustomIdObjectHashType::class);\n        }\n\n        $this->setUpEntitySchema([GH5988CustomIdObjectTypeParent::class, GH5988CustomIdObjectTypeChild::class]);\n    }\n\n    public function testDelete(): void\n    {\n        $object = new GH5988CustomIdObjectTypeChild(new CustomIdObject('foo'), 'Test');\n\n        $this->_em->persist($object);\n        $this->_em->flush();\n\n        $id = $object->id;\n\n        $object2 = $this->_em->find(GH5988CustomIdObjectTypeChild::class, $id);\n\n        $this->_em->remove($object2);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(GH5988CustomIdObjectTypeChild::class, $id));\n    }\n}\n\n\nclass GH5988CustomIdObjectHashType extends DBALType\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        return $value->id . '_test';\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): CustomIdObject\n    {\n        return new CustomIdObject(str_replace('_test', '', $value));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return self::class;\n    }\n}\n\n#[Table]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['child' => GH5988CustomIdObjectTypeChild::class])]\nabstract class GH5988CustomIdObjectTypeParent\n{\n    /** @var CustomIdObject */\n    #[Id]\n    #[Column(type: 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH5988CustomIdObjectHashType', length: 255)]\n    public $id;\n}\n\n\n#[Table]\n#[Entity]\nclass GH5988CustomIdObjectTypeChild extends GH5988CustomIdObjectTypeParent\n{\n    public function __construct(CustomIdObject $id, public string $name)\n    {\n        $this->id = $id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH5998Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-5998')]\nclass GH5998Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->_schemaTool->createSchema([\n            $this->_em->getClassMetadata(GH5998JTI::class),\n            $this->_em->getClassMetadata(GH5998JTIChild::class),\n            $this->_em->getClassMetadata(GH5998STI::class),\n            $this->_em->getClassMetadata(GH5998Basic::class),\n            $this->_em->getClassMetadata(GH5998Related::class),\n        ]);\n    }\n\n    /**\n     * Verifies that MappedSuperclasses work within an inheritance hierarchy.\n     */\n    public function testIssue(): void\n    {\n        // Test JTI\n        $this->classTests(GH5998JTIChild::class);\n        // Test STI\n        $this->classTests(GH5998STIChild::class);\n        // Test Basic\n        $this->classTests(GH5998Basic::class);\n    }\n\n    private function classTests($className): void\n    {\n        // Test insert\n        $child      = new $className('Sam', 0, 1);\n        $child->rel = new GH5998Related();\n        $this->_em->persist($child);\n        $this->_em->persist($child->rel);\n        $this->_em->flush();\n        $this->_em->clear();\n        $id = $child->id;\n\n        // Test find by rel\n        $child = $this->_em->getRepository($className)->findOneBy(['rel' => $child->rel]);\n        self::assertNotNull($child);\n        $this->_em->clear();\n\n        // Test query by id with fetch join\n        $child = $this->_em->createQuery('SELECT t, r FROM ' . $className . ' t JOIN t.rel r WHERE t.id = ?0')->setParameter(0, $id)->getOneOrNullResult();\n        self::assertNotNull($child);\n\n        // Test lock and update\n        $this->_em->wrapInTransaction(static function ($em) use ($child): void {\n            $em->lock($child, LockMode::NONE);\n            $child->firstName = 'Bob';\n            $child->status    = 0;\n        });\n        $this->_em->clear();\n        $child = $this->_em->getRepository($className)->find($id);\n        self::assertNotNull($child);\n        self::assertEquals($child->firstName, 'Bob');\n        self::assertEquals($child->status, 0);\n\n        // Test delete\n        $this->_em->remove($child);\n        $this->_em->flush();\n        $child = $this->_em->getRepository($className)->find($id);\n        self::assertNull($child);\n    }\n}\n\n#[ORM\\MappedSuperclass]\nclass GH5998Common\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH5998Related */\n    #[ORM\\ManyToOne(targetEntity: GH5998Related::class)]\n    #[ORM\\JoinColumn(name: 'related_id', referencedColumnName: 'id')]\n    public $rel;\n\n    /** @var int */\n    #[ORM\\Version]\n    #[ORM\\Column(type: 'integer')]\n    public $version;\n\n    /** @var mixed */\n    public $other;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorMap(['child' => GH5998JTIChild::class])]\nabstract class GH5998JTI extends GH5998Common\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string', length: 255)]\n    public $firstName;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH5998JTICommon extends GH5998JTI\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $status;\n}\n\n#[ORM\\Entity]\nclass GH5998JTIChild extends GH5998JTICommon\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $type;\n\n    public function __construct(string $firstName, int $type, int $status)\n    {\n        $this->firstName = $firstName;\n        $this->type      = $type;\n        $this->status    = $status;\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap(['child' => GH5998STIChild::class])]\nabstract class GH5998STI extends GH5998Common\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string', length: 255)]\n    public $firstName;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH5998STICommon extends GH5998STI\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $status;\n}\n\n#[ORM\\Entity]\nclass GH5998STIChild extends GH5998STICommon\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $type;\n\n    public function __construct(string $firstName, int $type, int $status)\n    {\n        $this->firstName = $firstName;\n        $this->type      = $type;\n        $this->status    = $status;\n    }\n}\n\n#[ORM\\Entity]\nclass GH5998Basic extends GH5998Common\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string', length: 255)]\n    public $firstName;\n\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $status;\n\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    public $type;\n\n    public function __construct(string $firstName, int $type, int $status)\n    {\n        $this->firstName = $firstName;\n        $this->type      = $type;\n        $this->status    = $status;\n    }\n}\n\n#[ORM\\Entity]\nclass GH5998Related\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6029Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function sprintf;\n\nfinal class GH6029Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH6029User::class,\n                GH6029Group::class,\n                GH6029Group2::class,\n                GH6029Product::class,\n                GH6029Feature::class,\n            ],\n        );\n    }\n\n    /**\n     * Verifies that when wrong entity is persisted via relationship field, the error message does not correctly state\n     * the expected class name.\n     */\n    #[Group('GH-6029')]\n    public function testManyToManyAssociation(): void\n    {\n        $user = new GH6029User();\n        $user->groups->add(new GH6029Group2());\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage(\n            sprintf(\n                'Expected value of type \"%s\" for association field \"%s#$groups\", got \"%s\" instead.',\n                GH6029Group::class,\n                GH6029User::class,\n                GH6029Group2::class,\n            ),\n        );\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n    }\n\n    /**\n     * Verifies that when wrong entity is persisted via relationship field, the error message does not correctly state\n     * the expected class name.\n     */\n    #[Group('GH-6029')]\n    public function testOneToManyAssociation(): void\n    {\n        $product = new GH6029Product();\n        $product->features->add(new GH6029Group2());\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage(\n            sprintf(\n                'Expected value of type \"%s\" for association field \"%s#$features\", got \"%s\" instead.',\n                GH6029Feature::class,\n                GH6029Product::class,\n                GH6029Group2::class,\n            ),\n        );\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n    }\n}\n\n#[Entity]\nclass GH6029User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, GH6029Group> */\n    #[ManyToMany(targetEntity: GH6029Group::class, cascade: ['all'])]\n    public $groups;\n\n    public function __construct()\n    {\n        $this->groups = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass GH6029Group\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH6029Group2\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH6029Product\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int,GH6029Feature> */\n    #[OneToMany(targetEntity: GH6029Feature::class, mappedBy: 'product', cascade: ['all'])]\n    public $features;\n\n    public function __construct()\n    {\n        $this->features = new ArrayCollection();\n    }\n}\n\n#[Entity]\nclass GH6029Feature\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH6029Product */\n    #[ManyToOne(targetEntity: GH6029Product::class, inversedBy: 'features')]\n    #[JoinColumn(name: 'product_id', referencedColumnName: 'id')]\n    public $product;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6123Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH6123Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH6123Entity::class,\n        );\n    }\n\n    public function testLoadingRemovedEntityFromDatabaseDoesNotCreateNewManagedEntityInstance(): void\n    {\n        $entity = new GH6123Entity();\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));\n\n        $this->_em->remove($entity);\n\n        $freshEntity = $this->loadEntityFromDatabase($entity->id);\n        self::assertSame($entity, $freshEntity);\n\n        self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($freshEntity));\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($freshEntity));\n    }\n\n    public function testRemovedEntityCanBePersistedAgain(): void\n    {\n        $entity = new GH6123Entity();\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->remove($entity);\n        self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($entity));\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($entity));\n\n        $this->loadEntityFromDatabase($entity->id);\n\n        $this->_em->persist($entity);\n        self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));\n\n        $this->_em->flush();\n    }\n\n    private function loadEntityFromDatabase(int $id): GH6123Entity|null\n    {\n        return $this->_em->createQueryBuilder()\n            ->select('e')\n            ->from(GH6123Entity::class, 'e')\n            ->where('e.id = :id')\n            ->setParameter('id', $id)\n            ->getQuery()\n            ->getOneOrNullResult();\n    }\n}\n\n#[ORM\\Entity]\nclass GH6123Entity\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: Types::INTEGER)]\n    public int $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6141Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Stringable;\n\nuse function in_array;\n\nclass GH6141Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        Type::addType(GH6141PeopleType::NAME, GH6141PeopleType::class);\n\n        $this->createSchemaForModels(\n            GH6141Person::class,\n            GH6141Boss::class,\n            GH6141Employee::class,\n        );\n    }\n\n    /**\n     * The intent of this test is to ensure that the ORM is capable\n     * of using objects as discriminators (which makes things a bit\n     * more dynamic as you can see on the mapping of `GH6141Person`)\n     */\n    #[Group('GH-6141')]\n    public function testEnumDiscriminatorsShouldBeConvertedToString(): void\n    {\n        $boss     = new GH6141Boss('John');\n        $employee = new GH6141Employee('Bob');\n\n        $this->_em->persist($boss);\n        $this->_em->persist($employee);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Using DQL here to make sure that we'll use ObjectHydrator instead of SimpleObjectHydrator\n        $query = $this->_em->createQueryBuilder()\n            ->select('person')\n            ->from(GH6141Person::class, 'person')\n            ->where('person.name = :name')\n            ->setMaxResults(1)\n            ->getQuery();\n\n        $query->setParameter('name', 'John');\n        self::assertEquals($boss, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT));\n        self::assertEquals(\n            GH6141People::get(GH6141People::BOSS),\n            $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'],\n        );\n\n        $query->setParameter('name', 'Bob');\n        self::assertEquals($employee, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT));\n        self::assertEquals(\n            GH6141People::get(GH6141People::EMPLOYEE),\n            $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'],\n        );\n    }\n}\n\nclass GH6141PeopleType extends StringType\n{\n    public const NAME = 'gh6141people';\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): string\n    {\n        if (! $value instanceof GH6141People) {\n            $value = GH6141People::get($value);\n        }\n\n        return (string) $value;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): GH6141People\n    {\n        return GH6141People::get($value);\n    }\n\n    public function getName(): string\n    {\n        return self::NAME;\n    }\n}\n\nclass GH6141People implements Stringable\n{\n    public const BOSS     = 'boss';\n    public const EMPLOYEE = 'employee';\n\n    /** @throws InvalidArgumentException */\n    public static function get(string $value): GH6141People\n    {\n        if (! self::isValid($value)) {\n            throw new InvalidArgumentException();\n        }\n\n        return new self($value);\n    }\n\n    private static function isValid(string $valid): bool\n    {\n        return in_array($valid, [self::BOSS, self::EMPLOYEE], true);\n    }\n\n    private function __construct(private string $value)\n    {\n    }\n\n    public function getValue(): string\n    {\n        return $this->value;\n    }\n\n    public function __toString(): string\n    {\n        return $this->value;\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'gh6141people')]\n#[DiscriminatorMap([GH6141People::BOSS => GH6141Boss::class, GH6141People::EMPLOYEE => GH6141Employee::class])]\nabstract class GH6141Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH6141Boss extends GH6141Person\n{\n}\n\n#[Entity]\nclass GH6141Employee extends GH6141Person\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6217Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function uniqid;\n\n#[Group('#6217')]\nfinal class GH6217Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH6217AssociatedEntity::class,\n            GH6217FetchedEntity::class,\n        );\n    }\n\n    public function testLoadingOfSecondLevelCacheOnEagerAssociations(): void\n    {\n        $lazy    = new GH6217AssociatedEntity();\n        $eager   = new GH6217AssociatedEntity();\n        $fetched = new GH6217FetchedEntity($lazy, $eager);\n\n        $this->_em->persist($eager);\n        $this->_em->persist($lazy);\n        $this->_em->persist($fetched);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(GH6217FetchedEntity::class);\n        $filters    = ['eager' => $eager->id];\n\n        self::assertCount(1, $repository->findBy($filters));\n        $this->getQueryLog()->reset()->enable();\n\n        /** @var GH6217FetchedEntity[] $found */\n        $found = $repository->findBy($filters);\n\n        self::assertCount(1, $found);\n        self::assertInstanceOf(GH6217FetchedEntity::class, $found[0]);\n        self::assertSame($lazy->id, $found[0]->lazy->id);\n        self::assertSame($eager->id, $found[0]->eager->id);\n        $this->assertQueryCount(0, 'No queries were executed in `findBy`');\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'NONSTRICT_READ_WRITE')]\nclass GH6217AssociatedEntity\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'NONSTRICT_READ_WRITE')]\nclass GH6217FetchedEntity\n{\n    public function __construct(\n        #[Id]\n        #[Cache('NONSTRICT_READ_WRITE')]\n        #[ManyToOne(targetEntity: GH6217AssociatedEntity::class)]\n        public GH6217AssociatedEntity $lazy,\n        #[Id]\n        #[Cache('NONSTRICT_READ_WRITE')]\n        #[ManyToOne(targetEntity: GH6217AssociatedEntity::class, fetch: 'EAGER')]\n        public GH6217AssociatedEntity $eager,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6362Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH6362Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH6362Start::class,\n            GH6362Base::class,\n            GH6362Child::class,\n            GH6362Join::class,\n        );\n    }\n\n    /**\n     * SELECT a as base, b, c, d\n     * FROM Start a\n     * LEFT JOIN a.bases b\n     * LEFT JOIN Child c ON b.id = c.id\n     * LEFT JOIN c.joins d\n     */\n    #[Group('GH-6362')]\n    public function testInheritanceJoinAlias(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(GH6362Start::class, 'a', 'base');\n        $rsm->addJoinedEntityResult(GH6362Base::class, 'b', 'a', 'bases');\n        $rsm->addEntityResult(GH6362Child::class, 'c');\n        $rsm->addJoinedEntityResult(GH6362Join::class, 'd', 'c', 'joins');\n\n        $rsm->addFieldResult('a', 'id_0', 'id');\n        $rsm->addFieldResult('b', 'id_1', 'id');\n        $rsm->addFieldResult('c', 'id_2', 'id');\n        $rsm->addFieldResult('d', 'id_3', 'id');\n\n        $rsm->addMetaResult('a', 'bases_id_4', 'bases_id', false, 'integer');\n        $rsm->addMetaResult('b', 'type_5', 'type');\n        $rsm->addMetaResult('c', 'type_6', 'type');\n        $rsm->addMetaResult('d', 'child_id_7', 'child_id', false, 'integer');\n\n        $rsm->setDiscriminatorColumn('b', 'type_5');\n        $rsm->setDiscriminatorColumn('c', 'type_6');\n\n        $resultSet = [\n            [\n                'id_0' => '1',\n                'id_1' => '1',\n                'id_2' => '1',\n                'id_3' => '1',\n                'bases_id_4' => '1',\n                'type_5' => 'child',\n                'type_6' => 'child',\n                'child_id_7' => '1',\n            ],\n        ];\n\n        $stmt     = ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class));\n        $hydrator = new ObjectHydrator($this->_em);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertInstanceOf(GH6362Start::class, $result[0]['base']);\n        self::assertInstanceOf(GH6362Child::class, $result[1][0]);\n    }\n}\n\n#[Entity]\nclass GH6362Start\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    protected $id;\n\n    #[ManyToOne(targetEntity: 'GH6362Base', inversedBy: 'starts')]\n    private GH6362Base $bases;\n}\n\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string')]\n#[DiscriminatorMap(['child' => 'GH6362Child'])]\n#[Entity]\nabstract class GH6362Base\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @phpstan-var Collection<int, GH6362Start> */\n    #[OneToMany(targetEntity: 'GH6362Start', mappedBy: 'bases')]\n    private $starts;\n}\n\n#[Entity]\nclass GH6362Child extends GH6362Base\n{\n    /** @phpstan-var Collection<int, GH6362Join> */\n    #[OneToMany(targetEntity: 'GH6362Join', mappedBy: 'child')]\n    private $joins;\n}\n\n#[Entity]\nclass GH6362Join\n{\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'GH6362Child', inversedBy: 'joins')]\n    private GH6362Child $child;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6394Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass GH6394Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(A::class, B::class);\n    }\n\n    /**\n     * Test the version of an entity can be fetched, when the id field and\n     * the id column are different.\n     */\n    #[Group('6393')]\n    public function testFetchVersionValueForDifferentIdFieldAndColumn(): void\n    {\n        $a = new A(1);\n        $this->_em->persist($a);\n\n        $b = new B($a, 'foo');\n        $this->_em->persist($b);\n        $this->_em->flush();\n\n        self::assertSame(1, $b->version);\n\n        $b->something = 'bar';\n        $this->_em->flush();\n\n        self::assertSame(2, $b->version);\n    }\n}\n\n#[Entity]\nclass A\n{\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n    ) {\n    }\n}\n\n#[Entity]\nclass B\n{\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    public $version;\n\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'A')]\n        #[JoinColumn(name: 'aid', referencedColumnName: 'id')]\n        public A $a,\n        #[Column(type: 'string', length: 255)]\n        public string $something,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6402Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Quote\\Address;\nuse Doctrine\\Tests\\Models\\Quote\\City;\nuse Doctrine\\Tests\\Models\\Quote\\FullAddress;\nuse Doctrine\\Tests\\Models\\Quote\\User;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-6402')]\nclass GH6402Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('quote');\n\n        parent::setUp();\n    }\n\n    public function testFind(): void\n    {\n        $id = $this->createAddress();\n\n        $address = $this->_em->find(Address::class, $id);\n        self::assertNotNull($address->user);\n    }\n\n    public function testQuery(): void\n    {\n        $id = $this->createAddress();\n\n        $addresses = $this->_em->createQuery('SELECT a FROM ' . Address::class . ' a WHERE a.id = :id')\n            ->setParameter('id', $id)\n            ->getResult();\n\n        self::assertCount(1, $addresses);\n        self::assertNotNull($addresses[0]->user);\n    }\n\n    public function testFindWithSubClass(): void\n    {\n        $id = $this->createFullAddress();\n\n        $address = $this->_em->find(FullAddress::class, $id);\n        self::assertNotNull($address->user);\n    }\n\n    public function testQueryWithSubClass(): void\n    {\n        $id = $this->createFullAddress();\n\n        $addresses = $this->_em->createQuery('SELECT a FROM ' . FullAddress::class . ' a WHERE a.id = :id')\n            ->setParameter('id', $id)\n            ->getResult();\n\n        self::assertCount(1, $addresses);\n        self::assertNotNull($addresses[0]->user);\n    }\n\n    private function createAddress(): int\n    {\n        $address      = new Address();\n        $address->zip = 'bar';\n\n        $this->persistAddress($address);\n\n        return $address->id;\n    }\n\n    private function createFullAddress(): int\n    {\n        $address       = new FullAddress();\n        $address->zip  = 'bar';\n        $address->city = new City('London');\n\n        $this->persistAddress($address);\n\n        return $address->id;\n    }\n\n    private function persistAddress(Address $address): void\n    {\n        $user       = new User();\n        $user->name = 'foo';\n        $user->setAddress($address);\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6464Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-6464')]\nclass GH6464Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH6464Post::class,\n            GH6464User::class,\n            GH6464Author::class,\n        );\n    }\n\n    /**\n     * Verifies that SqlWalker generates valid SQL for an INNER JOIN to CTI table\n     *\n     * SqlWalker needs to generate nested INNER JOIN statements, otherwise there would be INNER JOIN\n     * statements without an ON clause, which are valid on e.g. MySQL but rejected by PostgreSQL.\n     */\n    public function testIssue(): void\n    {\n        $query = $this->_em->createQueryBuilder()\n            ->select('p')\n            ->from(GH6464Post::class, 'p')\n            ->innerJoin(GH6464Author::class, 'a', 'ON', 'p.authorId = a.id')\n            ->getQuery();\n\n        self::assertDoesNotMatchRegularExpression(\n            '/INNER JOIN \\w+ \\w+ INNER JOIN/',\n            $query->getSQL(),\n            'As of GH-6464, every INNER JOIN should have an ON clause, which is missing here',\n        );\n\n        // Query shouldn't yield a result, yet it shouldn't crash (anymore)\n        self::assertEquals([], $query->getResult());\n    }\n}\n\n#[Entity]\nclass GH6464Post\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $authorId;\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['author' => 'GH6464Author'])]\nabstract class GH6464User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH6464Author extends GH6464User\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6499OneToManyRelationshipTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH6499')]\nclass GH6499OneToManyRelationshipTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(Application::class, Person::class, ApplicationPerson::class);\n    }\n\n    /**\n     * Test for the bug described in issue #6499.\n     */\n    public function testIssue(): void\n    {\n        $person = new Person();\n        $this->_em->persist($person);\n\n        $application = new Application();\n        $this->_em->persist($application);\n\n        $applicationPerson = new ApplicationPerson($person, $application);\n\n        $this->_em->persist($applicationPerson);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $personFromDatabase      = $this->_em->find(Person::class, $person->id);\n        $applicationFromDatabase = $this->_em->find(Application::class, $application->id);\n\n        self::assertEquals($personFromDatabase->id, $person->id, 'Issue #6499 will result in an integrity constraint violation before reaching this point.');\n        self::assertFalse($personFromDatabase->getApplicationPeople()->isEmpty());\n\n        self::assertEquals($applicationFromDatabase->id, $application->id, 'Issue #6499 will result in an integrity constraint violation before reaching this point.');\n        self::assertFalse($applicationFromDatabase->getApplicationPeople()->isEmpty());\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'GH6499OTM_application')]\nclass Application\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(mappedBy: 'application', targetEntity: ApplicationPerson::class, orphanRemoval: true, cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    private $applicationPeople;\n\n    public function __construct()\n    {\n        $this->applicationPeople = new ArrayCollection();\n    }\n\n    public function getApplicationPeople(): Collection\n    {\n        return $this->applicationPeople;\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'GH6499OTM_person')]\nclass Person\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(mappedBy: 'person', targetEntity: ApplicationPerson::class, orphanRemoval: true, cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    private $applicationPeople;\n\n    public function __construct()\n    {\n        $this->applicationPeople = new ArrayCollection();\n    }\n\n    public function getApplicationPeople(): Collection\n    {\n        return $this->applicationPeople;\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'GH6499OTM_application_person')]\nclass ApplicationPerson\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var Application */\n    #[ORM\\ManyToOne(targetEntity: Application::class, inversedBy: 'applicationPeople', cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    public $application;\n\n    /** @var Person */\n    #[ORM\\ManyToOne(targetEntity: Person::class, inversedBy: 'applicationPeople', cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    public $person;\n\n    public function __construct(Person $person, Application $application)\n    {\n        $this->person      = $person;\n        $this->application = $application;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6499OneToOneRelationshipTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH6499')]\nclass GH6499OneToOneRelationshipTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH6499OTOA::class, GH6499OTOB::class);\n    }\n\n    /**\n     * Test for the bug described in issue #6499.\n     */\n    public function testIssue(): void\n    {\n        $a = new GH6499OTOA();\n\n        $this->_em->persist($a);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        self::assertEquals(\n            $this->_em->find(GH6499OTOA::class, $a->id)->b->id,\n            $a->b->id,\n            'Issue #6499 will result in an integrity constraint violation before reaching this point.',\n        );\n    }\n}\n\n#[ORM\\Entity]\nclass GH6499OTOA\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH6499OTOB */\n    #[ORM\\OneToOne(targetEntity: GH6499OTOB::class, cascade: ['persist'])]\n    #[ORM\\JoinColumn(nullable: false)]\n    public $b;\n\n    public function __construct()\n    {\n        $this->b = new GH6499OTOB();\n    }\n}\n\n#[ORM\\Entity]\nclass GH6499OTOB\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6499Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Specifically, GH6499B has a dependency on GH6499A, and GH6499A\n * has a dependency on GH6499B. Since GH6499A#b is not nullable,\n * the database row for GH6499B should be inserted first.\n */\n#[Group('GH6499')]\nclass GH6499Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH6499A::class, GH6499B::class);\n    }\n\n    public function testIssue(): void\n    {\n        $b = new GH6499B();\n        $a = new GH6499A();\n\n        $this->_em->persist($a);\n\n        $a->b = $b;\n\n        $this->_em->persist($b);\n\n        $this->_em->flush();\n\n        self::assertIsInt($a->id);\n        self::assertIsInt($b->id);\n    }\n\n    public function testIssueReversed(): void\n    {\n        $b = new GH6499B();\n        $a = new GH6499A();\n\n        $a->b = $b;\n\n        $this->_em->persist($b);\n        $this->_em->persist($a);\n\n        $this->_em->flush();\n\n        self::assertIsInt($a->id);\n        self::assertIsInt($b->id);\n    }\n}\n\n#[ORM\\Entity]\nclass GH6499A\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH6499B */\n    #[ORM\\JoinColumn(nullable: false)]\n    #[ORM\\OneToOne(targetEntity: GH6499B::class)]\n    public $b;\n}\n\n#[ORM\\Entity]\nclass GH6499B\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH6499A */\n    #[ORM\\ManyToOne(targetEntity: GH6499A::class)]\n    private $a;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6531Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH6531Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH6531User::class,\n                GH6531Address::class,\n                GH6531Article::class,\n                GH6531ArticleAttribute::class,\n                GH6531Order::class,\n                GH6531OrderItem::class,\n                GH6531Product::class,\n            ],\n        );\n    }\n\n    #[Group('GH-6531')]\n    public function testSimpleDerivedIdentity(): void\n    {\n        $user          = new GH6531User();\n        $address       = new GH6531Address();\n        $address->user = $user;\n\n        $this->_em->persist($user);\n        $this->_em->persist($address);\n        $this->_em->flush();\n\n        self::assertSame($user, $this->_em->find(GH6531User::class, $user->id));\n        self::assertSame($address, $this->_em->find(GH6531Address::class, $user));\n    }\n\n    #[Group('GH-6531')]\n    public function testDynamicAttributes(): void\n    {\n        $article = new GH6531Article();\n        $article->addAttribute('name', 'value');\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n\n        self::assertSame(\n            $article->attributes['name'],\n            $this->_em->find(GH6531ArticleAttribute::class, ['article' => $article, 'attribute' => 'name']),\n        );\n    }\n\n    #[Group('GH-6531')]\n    public function testJoinTableWithMetadata(): void\n    {\n        $product = new GH6531Product();\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $order = new GH6531Order();\n        $order->addItem($product, 2);\n\n        $this->_em->persist($order);\n        $this->_em->flush();\n\n        self::assertSame(\n            $order->items->first(),\n            $this->_em->find(GH6531OrderItem::class, ['product' => $product, 'order' => $order]),\n        );\n    }\n}\n\n#[Entity]\nclass GH6531User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH6531Address\n{\n    /** @var GH6531User */\n    #[Id]\n    #[OneToOne(targetEntity: GH6531User::class)]\n    public $user;\n}\n\n#[Entity]\nclass GH6531Article\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<string, GH6531ArticleAttribute> */\n    #[OneToMany(targetEntity: GH6531ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')]\n    public $attributes;\n\n    public function addAttribute(string $name, string $value): void\n    {\n        $this->attributes[$name] = new GH6531ArticleAttribute($name, $value, $this);\n    }\n}\n\n#[Entity]\nclass GH6531ArticleAttribute\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        public string $attribute,\n        #[Column(type: 'string', length: 255)]\n        public string $value,\n        #[Id]\n        #[ManyToOne(targetEntity: GH6531Article::class, inversedBy: 'attributes')]\n        public GH6531Article $article,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH6531Order\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, GH6531OrderItem> */\n    #[OneToMany(targetEntity: GH6531OrderItem::class, mappedBy: 'order', cascade: ['ALL'])]\n    public $items;\n\n    public function __construct()\n    {\n        $this->items = new ArrayCollection();\n    }\n\n    public function addItem(GH6531Product $product, int $amount): void\n    {\n        $this->items->add(new GH6531OrderItem($this, $product, $amount));\n    }\n}\n\n#[Entity]\nclass GH6531Product\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH6531OrderItem\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: GH6531Order::class)]\n        public GH6531Order $order,\n        #[Id]\n        #[ManyToOne(targetEntity: GH6531Product::class)]\n        public GH6531Product $product,\n        #[Column(type: 'integer')]\n        public int $amount = 1,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6682Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Test\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH6682Test extends OrmFunctionalTestCase\n{\n    #[Group('GH-6682')]\n    public function testIssue(): void\n    {\n        $parsedDefinition = [\n            'sequenceName'   => 'test_sequence',\n            'allocationSize' => '',\n            'initialValue'   => '',\n        ];\n\n        $classMetadata = new ClassMetadata('test_entity');\n        $classMetadata->setSequenceGeneratorDefinition($parsedDefinition);\n\n        self::assertSame(\n            ['sequenceName' => 'test_sequence', 'allocationSize' => '1', 'initialValue' => '1'],\n            $classMetadata->sequenceGeneratorDefinition,\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6699Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-6699')]\nfinal class GH6699Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testMixedParametersWithZeroNumber(): void\n    {\n        $query = $this->_em->createQueryBuilder()\n                           ->select('u')\n                           ->from(CmsUser::class, 'u')\n                           ->andWhere('u.username = :username')\n                           ->andWhere('u.id = ?0')\n                           ->getQuery();\n\n        $query->setParameter('username', 'bar');\n        $query->setParameter(0, 0);\n\n        $query->execute();\n\n        self::assertCount(2, $query->getParameters());\n        self::assertSame(0, $query->getParameter(0)->getValue());\n        self::assertSame('bar', $query->getParameter('username')->getValue());\n    }\n\n    public function testMixedParametersWithZeroNumberOnQueryBuilder(): void\n    {\n        $query = $this->_em->createQueryBuilder()\n                           ->select('u')\n                           ->from(CmsUser::class, 'u')\n                           ->andWhere('u.username = :username')\n                           ->andWhere('u.id = ?0')\n                           ->setParameter('username', 'bar')\n                           ->setParameter(0, 0)\n                           ->getQuery();\n\n        $query->execute();\n\n        self::assertCount(2, $query->getParameters());\n        self::assertSame(0, $query->getParameter(0)->getValue());\n        self::assertSame('bar', $query->getParameter('username')->getValue());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6740Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH6740Test extends OrmFunctionalTestCase\n{\n    private int $productId;\n\n    private int $firstCategoryId;\n\n    private int $secondCategoryId;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('ecommerce');\n\n        parent::setUp();\n\n        $product = new ECommerceProduct();\n        $product->setName('First Product');\n\n        $firstCategory  = new ECommerceCategory();\n        $secondCategory = new ECommerceCategory();\n\n        $firstCategory->setName('Business');\n        $secondCategory->setName('Home');\n\n        $product->addCategory($firstCategory);\n        $product->addCategory($secondCategory);\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->productId        = $product->getId();\n        $this->firstCategoryId  = $firstCategory->getId();\n        $this->secondCategoryId = $secondCategory->getId();\n    }\n\n    #[Group('GH-6740')]\n    public function testCollectionFilteringLteOperator(): void\n    {\n        $product  = $this->_em->find(ECommerceProduct::class, $this->productId);\n        $criteria = Criteria::create(true)->where(Criteria::expr()->lte('id', $this->secondCategoryId));\n\n        self::assertCount(2, $product->getCategories()->matching($criteria));\n    }\n\n    #[Group('GH-6740')]\n    public function testCollectionFilteringLtOperator(): void\n    {\n        $product  = $this->_em->find(ECommerceProduct::class, $this->productId);\n        $criteria = Criteria::create(true)->where(Criteria::expr()->lt('id', $this->secondCategoryId));\n\n        self::assertCount(1, $product->getCategories()->matching($criteria));\n    }\n\n    #[Group('GH-6740')]\n    public function testCollectionFilteringGteOperator(): void\n    {\n        $product  = $this->_em->find(ECommerceProduct::class, $this->productId);\n        $criteria = Criteria::create(true)->where(Criteria::expr()->gte('id', $this->firstCategoryId));\n\n        self::assertCount(2, $product->getCategories()->matching($criteria));\n    }\n\n    #[Group('GH-6740')]\n    public function testCollectionFilteringGtOperator(): void\n    {\n        $product  = $this->_em->find(ECommerceProduct::class, $this->productId);\n        $criteria = Criteria::create(true)->where(Criteria::expr()->gt('id', $this->firstCategoryId));\n\n        self::assertCount(1, $product->getCategories()->matching($criteria));\n    }\n\n    #[Group('GH-6740')]\n    public function testCollectionFilteringEqualsOperator(): void\n    {\n        $product  = $this->_em->find(ECommerceProduct::class, $this->productId);\n        $criteria = Criteria::create(true)->where(Criteria::expr()->eq('id', $this->firstCategoryId));\n\n        self::assertCount(1, $product->getCategories()->matching($criteria));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6823Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH6823Test extends OrmFunctionalTestCase\n{\n    public function testCharsetCollationWhenCreatingForeignRelations(): void\n    {\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof MySQLPlatform) {\n            self::markTestSkipped('This test is useful for all databases, but designed only for mysql.');\n        }\n\n        $this->createSchemaForModels(\n            GH6823User::class,\n            GH6823Group::class,\n            GH6823Status::class,\n        );\n\n        $schemaManager = $this->createSchemaManager();\n        /* gh6823_user.group_id should use charset ascii and collation\n         * ascii_general_ci, because that is what gh6823_group.id falls back to */\n        $userGroupIdOptions = $schemaManager->introspectTable('gh6823_user')->getColumn('group_id')->toArray();\n        self::assertSame('ascii', $userGroupIdOptions['charset']);\n        self::assertSame('ascii_general_ci', $userGroupIdOptions['collation']);\n\n        /* gh6823_user.status_id should use charset latin1 and collation\n         * latin1_bin, because that is what gh6823_status.id uses */\n        $userStatusIdOptions = $schemaManager->introspectTable('gh6823_user')->getColumn('status_id')->toArray();\n        self::assertSame('latin1', $userStatusIdOptions['charset']);\n        self::assertSame('latin1_bin', $userStatusIdOptions['collation']);\n\n        /* gh6823_user_tags.user_id should use charset utf8mb4 and collation\n         * utf8mb4_bin, because that is what gh6823_user.id falls back to */\n        $userTagsUserIdOptions = $schemaManager->introspectTable('gh6823_user_tags')->getColumn('user_id')->toArray();\n        self::assertSame('utf8mb4', $userTagsUserIdOptions['charset']);\n        self::assertSame('utf8mb4_bin', $userTagsUserIdOptions['collation']);\n\n        /* gh6823_user_tags.tag_id should use charset latin1 and collation\n         * latin1_bin, because that is what gh6823_tag.id falls back to */\n        $userTagsTagIdOption = $schemaManager->introspectTable('gh6823_user_tags')->getColumn('tag_id')->toArray();\n        self::assertSame('latin1', $userTagsTagIdOption['charset']);\n        self::assertSame('latin1_bin', $userTagsTagIdOption['collation']);\n    }\n}\n\n#[Table(name: 'gh6823_user', options: ['charset' => 'utf8mb4', 'collation' => 'utf8mb4_bin'])]\n#[Entity]\nclass GH6823User\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public $id;\n\n    /** @var GH6823Group */\n    #[ManyToOne(targetEntity: 'GH6823Group')]\n    #[JoinColumn(name: 'group_id', referencedColumnName: 'id', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n    public $group;\n\n    /** @var GH6823Status */\n    #[ManyToOne(targetEntity: 'GH6823Status')]\n    #[JoinColumn(name: 'status_id', referencedColumnName: 'id', options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])]\n    public $status;\n\n    /** @var Collection<int, GH6823Tag> */\n    #[JoinTable(name: 'gh6823_user_tags', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id', options: ['charset' => 'utf8mb4', 'collation' => 'utf8mb4_bin'])]\n    #[InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id', options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])]\n    #[ManyToMany(targetEntity: 'GH6823Tag')]\n    public $tags;\n}\n\n#[Table(name: 'gh6823_group', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n#[Entity]\nclass GH6823Group\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public $id;\n}\n\n#[Table(name: 'gh6823_status', options: ['charset' => 'koi8r', 'collation' => 'koi8r_bin'])]\n#[Entity]\nclass GH6823Status\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255, options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])]\n    public $id;\n}\n\n#[Table(name: 'gh6823_tag', options: ['charset' => 'koi8r', 'collation' => 'koi8r_bin'])]\n#[Entity]\nclass GH6823Tag\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255, options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH6937Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-6937')]\nfinal class GH6937Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH6937Person::class, GH6937Employee::class, GH6937Manager::class]);\n    }\n\n    public function testPhoneNumberIsPopulatedWithFind(): void\n    {\n        $manager              = new GH6937Manager();\n        $manager->name        = 'Kevin';\n        $manager->phoneNumber = '555-5555';\n        $manager->department  = 'Accounting';\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedManager = $this->_em->find(GH6937Person::class, $manager->id);\n\n        self::assertSame('Kevin', $persistedManager->name);\n        self::assertSame('555-5555', $persistedManager->phoneNumber);\n        self::assertSame('Accounting', $persistedManager->department);\n    }\n\n    public function testPhoneNumberIsPopulatedWithQueryBuilderUsingSimpleObjectHydrator(): void\n    {\n        $manager              = new GH6937Manager();\n        $manager->name        = 'Kevin';\n        $manager->phoneNumber = '555-5555';\n        $manager->department  = 'Accounting';\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedManager = $this->_em->getRepository(GH6937Person::class)\n                                      ->createQueryBuilder('e')\n                                      ->where('e.id = :id')\n                                      ->setParameter('id', $manager->id)\n                                      ->getQuery()\n                                      ->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT);\n\n        self::assertSame('Kevin', $persistedManager->name);\n        self::assertSame('555-5555', $persistedManager->phoneNumber);\n        self::assertSame('Accounting', $persistedManager->department);\n    }\n\n    public function testPhoneNumberIsPopulatedWithQueryBuilder(): void\n    {\n        $manager              = new GH6937Manager();\n        $manager->name        = 'Kevin';\n        $manager->phoneNumber = '555-5555';\n        $manager->department  = 'Accounting';\n\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $persistedManager = $this->_em->getRepository(GH6937Person::class)\n                                      ->createQueryBuilder('e')\n                                      ->where('e.id = :id')\n                                      ->setParameter('id', $manager->id)\n                                      ->getQuery()\n                                      ->getOneOrNullResult();\n\n        self::assertSame('Kevin', $persistedManager->name);\n        self::assertSame('555-5555', $persistedManager->phoneNumber);\n        self::assertSame('Accounting', $persistedManager->department);\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['employee' => GH6937Employee::class, 'manager' => GH6937Manager::class])]\nabstract class GH6937Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n}\n\n#[Entity]\nabstract class GH6937Employee extends GH6937Person\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $phoneNumber;\n}\n\n#[Entity]\nclass GH6937Manager extends GH6937Employee\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $department;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7006Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH7006Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH7006Book::class, GH7006PCT::class, GH7006PCTFee::class);\n    }\n\n    public function testIssue(): void\n    {\n        $book               = new GH7006Book();\n        $book->exchangeCode = 'first';\n        $this->_em->persist($book);\n\n        $book->exchangeCode = 'second'; // change sth.\n\n        $paymentCardTransaction       = new GH7006PCT();\n        $paymentCardTransaction->book = $book;\n        $paymentCardTransactionFee    = new GH7006PCTFee($paymentCardTransaction);\n\n        $this->_em->persist($paymentCardTransaction);\n\n        $this->_em->flush();\n\n        self::assertIsInt($book->id);\n        self::assertIsInt($paymentCardTransaction->id);\n        self::assertIsInt($paymentCardTransactionFee->id);\n    }\n}\n\n#[ORM\\Entity]\nclass GH7006Book\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[ORM\\Column(type: 'string', length: 255, nullable: true)]\n    public $exchangeCode;\n\n    /** @var GH7006PCT */\n    #[ORM\\OneToOne(targetEntity: GH7006PCT::class, cascade: ['persist', 'remove'])]\n    #[ORM\\JoinColumn(name: 'paymentCardTransactionId', referencedColumnName: 'id')]\n    public $paymentCardTransaction;\n}\n\n#[ORM\\Entity]\nclass GH7006PCT\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var GH7006Book */\n    #[ORM\\ManyToOne(targetEntity: GH7006Book::class)]\n    #[ORM\\JoinColumn(name: 'bookingId', referencedColumnName: 'id', nullable: false)]\n    public $book;\n\n    /** @var Collection<int, GH7006PCTFee> */\n    #[ORM\\OneToMany(targetEntity: GH7006PCTFee::class, mappedBy: 'pct', cascade: ['persist', 'remove'])]\n    #[ORM\\OrderBy(['id' => 'ASC'])]\n    public $fees;\n\n    public function __construct()\n    {\n        $this->fees = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH7006PCTFee\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var GH7006PCT */\n    #[ORM\\ManyToOne(targetEntity: GH7006PCT::class, inversedBy: 'fees')]\n    #[ORM\\JoinColumn(name: 'paymentCardTransactionId', referencedColumnName: 'id', nullable: false)]\n    public $pct;\n\n    public function __construct(GH7006PCT $pct)\n    {\n        $this->pct = $pct;\n        $pct->fees->add($this);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7012Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\Models\\Quote\\User as QuotedUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH7012Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('quote');\n\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7012UserData::class]);\n    }\n\n    #[Group('GH-7012')]\n    public function testUpdateEntityWithIdentifierAssociationWithQuotedJoinColumn(): void\n    {\n        $user       = new QuotedUser();\n        $user->name = 'John Doe';\n\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $userData = new GH7012UserData($user, '123456789');\n\n        $this->_em->persist($userData);\n        $this->_em->flush();\n\n        $userData->name = '4321';\n        $this->_em->flush();\n\n        self::assertSame(\n            '4321',\n            $this->_em->getRepository(GH7012UserData::class)->findOneBy(['user' => $user])->name,\n        );\n    }\n}\n\n\n#[Table(name: '`quote-user-data`')]\n#[Entity]\nclass GH7012UserData\n{\n    public function __construct(\n        #[Id]\n        #[OneToOne(targetEntity: QuotedUser::class)]\n        #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`', onDelete: 'CASCADE')]\n        public QuotedUser $user,\n        #[Column(type: 'string', name: '`name`')]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7062Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nclass GH7062Test extends OrmFunctionalTestCase\n{\n    private const SEASON_ID = 'season_18';\n    private const TEAM_ID   = 'team_A';\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH7062Team::class,\n                GH7062Season::class,\n                GH7062Ranking::class,\n                GH7062RankingPosition::class,\n            ],\n        );\n    }\n\n    #[Group('GH-7062')]\n    public function testEntityWithAssociationKeyIdentityCanBeUpdated(): void\n    {\n        $this->createInitialRankingWithRelatedEntities();\n        $this->modifyRanking();\n        $this->verifyRanking();\n    }\n\n    private function createInitialRankingWithRelatedEntities(): void\n    {\n        $team   = new GH7062Team(self::TEAM_ID);\n        $season = new GH7062Season(self::SEASON_ID);\n\n        $season->ranking = new GH7062Ranking($season, [$team]);\n\n        $this->_em->persist($team);\n        $this->_em->persist($season);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        foreach ($season->ranking->positions as $position) {\n            self::assertSame(0, $position->points);\n        }\n    }\n\n    private function modifyRanking(): void\n    {\n        $ranking = $this->_em->find(GH7062Ranking::class, self::SEASON_ID);\n        assert($ranking instanceof GH7062Ranking);\n\n        foreach ($ranking->positions as $position) {\n            $position->points += 3;\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    private function verifyRanking(): void\n    {\n        $season = $this->_em->find(GH7062Season::class, self::SEASON_ID);\n        assert($season instanceof GH7062Season);\n        self::assertInstanceOf(GH7062Season::class, $season);\n\n        $ranking = $season->ranking;\n        self::assertInstanceOf(GH7062Ranking::class, $ranking);\n\n        foreach ($ranking->positions as $position) {\n            self::assertSame(3, $position->points);\n        }\n    }\n}\n\n/**\n * Simple Entity whose identity is defined through another Entity (Season)\n */\n#[Table(name: 'soccer_rankings')]\n#[Entity]\nclass GH7062Ranking\n{\n    /**\n     * @var Collection|GH7062RankingPosition[]\n     * @phpstan-var Collection<GH7062RankingPosition>\n     */\n    #[OneToMany(targetEntity: GH7062RankingPosition::class, mappedBy: 'ranking', cascade: ['all'])]\n    public $positions;\n\n    /** @param GH7062Team[] $teams */\n    public function __construct(\n        #[Id]\n        #[OneToOne(targetEntity: GH7062Season::class, inversedBy: 'ranking')]\n        #[JoinColumn(name: 'season', referencedColumnName: 'id')]\n        public GH7062Season $season,\n        array $teams,\n    ) {\n        $this->positions = new ArrayCollection();\n\n        foreach ($teams as $team) {\n            $this->positions[] = new GH7062RankingPosition($this, $team);\n        }\n    }\n}\n\n/**\n * Entity which serves as a identity provider for other entities\n */\n#[Table(name: 'soccer_seasons')]\n#[Entity]\nclass GH7062Season\n{\n    /** @var GH7062Ranking|null */\n    #[OneToOne(targetEntity: GH7062Ranking::class, mappedBy: 'season', cascade: ['all'])]\n    public $ranking;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        public string $id,\n    ) {\n    }\n}\n\n/**\n * Entity which serves as a identity provider for other entities\n */\n#[Table(name: 'soccer_teams')]\n#[Entity]\nclass GH7062Team\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        public string $id,\n    ) {\n    }\n}\n\n/**\n * Entity whose identity is defined through two other entities\n */\n#[Table(name: 'soccer_ranking_positions')]\n#[Entity]\nclass GH7062RankingPosition\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $points;\n\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: GH7062Ranking::class, inversedBy: 'positions')]\n        #[JoinColumn(name: 'season', referencedColumnName: 'season')]\n        public GH7062Ranking $ranking,\n        #[Id]\n        #[ManyToOne(targetEntity: GH7062Team::class)]\n        #[JoinColumn(name: 'team_id', referencedColumnName: 'id')]\n        public GH7062Team $team,\n    ) {\n        $this->points = 0;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7067Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nfinal class GH7067Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7067Entity::class]);\n    }\n\n    #[Group('GH-7067')]\n    public function testSLCWithVersion(): void\n    {\n        $entity             = new GH7067Entity();\n        $entity->lastUpdate = new DateTime();\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $notCached = $this->_em->find(GH7067Entity::class, $entity->id);\n        assert($notCached instanceof GH7067Entity);\n\n        self::assertNotNull($notCached->version, 'Version already cached by persister above, it must be not null');\n\n        $notCached->lastUpdate = new DateTime('+1 seconds');\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'NONSTRICT_READ_WRITE')]\nclass GH7067Entity\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    public $lastUpdate;\n\n    /** @var DateTime */\n    #[Column(type: 'datetime')]\n    #[Version]\n    public $version;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7068Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\TransactionRequiredException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH7068Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                SomeEntity::class,\n            ],\n        );\n    }\n\n    public function testLockModeIsRespected(): void\n    {\n        $entity = new SomeEntity();\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->_em->find(SomeEntity::class, 1);\n\n        $this->expectException(TransactionRequiredException::class);\n        $this->_em->find(SomeEntity::class, 1, LockMode::PESSIMISTIC_WRITE);\n    }\n}\n\n#[Entity]\nfinal class SomeEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7079Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH7079')]\nfinal class GH7079Test extends OrmFunctionalTestCase\n{\n    private DefaultQuoteStrategy $strategy;\n\n    private AbstractPlatform $platform;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->platform = $this->_em->getConnection()->getDatabasePlatform();\n        $this->strategy = new DefaultQuoteStrategy();\n    }\n\n    public function testGetTableName(): void\n    {\n        $table = [\n            'name'   => 'cms_user',\n            'schema' => 'cms',\n        ];\n\n        $cm = $this->createClassMetadata(GH7079CmsUser::class);\n        $cm->setPrimaryTable($table);\n\n        self::assertEquals($this->getTableFullName($table), $this->strategy->getTableName($cm, $this->platform));\n    }\n\n    public function testJoinTableName(): void\n    {\n        $table = [\n            'name'   => 'cmsaddress_cmsuser',\n            'schema' => 'cms',\n        ];\n\n        $cm = $this->createClassMetadata(GH7079CmsAddress::class);\n        $cm->mapManyToMany(\n            [\n                'fieldName'    => 'user',\n                'targetEntity' => 'DDC7079CmsUser',\n                'inversedBy'   => 'users',\n                'joinTable'    => $table,\n            ],\n        );\n\n        self::assertEquals(\n            $this->getTableFullName($table),\n            $this->strategy->getJoinTableName($cm->associationMappings['user'], $cm, $this->platform),\n        );\n    }\n\n    private function getTableFullName(array $table): string\n    {\n        return $table['schema'] . '.' . $table['name'];\n    }\n\n    private function createClassMetadata(string $className): ClassMetadata\n    {\n        $cm = new ClassMetadata($className);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        return $cm;\n    }\n}\n\n#[Table(name: 'cms_users', schema: 'cms')]\n#[Entity]\nclass GH7079CmsUser\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH7079CmsAddress */\n    #[OneToOne(targetEntity: GH7079CmsAddress::class, mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    public $address;\n}\n\n#[Table(name: 'cms_addresses', schema: 'cms')]\n#[Entity]\nclass GH7079CmsAddress\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH7079CmsUser */\n    #[OneToOne(targetEntity: GH7079CmsUser::class, inversedBy: 'address')]\n    #[JoinColumn(referencedColumnName: 'id')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7180Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests suggested in https://github.com/doctrine/orm/pull/7180#issuecomment-380841413 and\n * https://github.com/doctrine/orm/pull/7180#issuecomment-381067448.\n */\n#[Group('GH7180')]\nfinal class GH7180Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7180A::class, GH7180B::class, GH7180C::class, GH7180D::class, GH7180E::class, GH7180F::class, GH7180G::class]);\n    }\n\n    public function testIssue(): void\n    {\n        $a = new GH7180A();\n        $b = new GH7180B();\n        $c = new GH7180C();\n\n        $a->b = $b;\n        $b->a = $a;\n        $c->a = $a;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n\n        $this->_em->flush();\n\n        self::assertIsInt($a->id);\n        self::assertIsInt($b->id);\n        self::assertIsInt($c->id);\n    }\n\n    public function testIssue3NodeCycle(): void\n    {\n        $d = new GH7180D();\n        $e = new GH7180E();\n        $f = new GH7180F();\n        $g = new GH7180G();\n\n        $d->e = $e;\n        $e->f = $f;\n        $f->d = $d;\n        $g->d = $d;\n\n        $this->_em->persist($d);\n        $this->_em->persist($e);\n        $this->_em->persist($f);\n        $this->_em->persist($g);\n\n        $this->_em->flush();\n\n        self::assertIsInt($d->id);\n        self::assertIsInt($e->id);\n        self::assertIsInt($f->id);\n        self::assertIsInt($g->id);\n    }\n}\n\n#[Entity]\nclass GH7180A\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180B */\n    #[OneToOne(targetEntity: GH7180B::class, inversedBy: 'a')]\n    #[JoinColumn(nullable: false)]\n    public $b;\n}\n\n#[Entity]\nclass GH7180B\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180A */\n    #[OneToOne(targetEntity: GH7180A::class, mappedBy: 'b')]\n    public $a;\n}\n\n#[Entity]\nclass GH7180C\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180A */\n    #[ManyToOne(targetEntity: GH7180A::class)]\n    #[JoinColumn(nullable: false)]\n    public $a;\n}\n\n#[Entity]\nclass GH7180D\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180E */\n    #[OneToOne(targetEntity: GH7180E::class)]\n    #[JoinColumn(nullable: false)]\n    public $e;\n}\n\n#[Entity]\nclass GH7180E\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180F */\n    #[OneToOne(targetEntity: GH7180F::class)]\n    #[JoinColumn(nullable: false)]\n    public $f;\n}\n\n#[Entity]\nclass GH7180F\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180D */\n    #[ManyToOne(targetEntity: GH7180D::class)]\n    #[JoinColumn(nullable: true)]\n    public $d;\n}\n\n#[Entity]\nclass GH7180G\n{\n    /** @var int */\n    #[GeneratedValue]\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7180D */\n    #[ManyToOne(targetEntity: GH7180D::class)]\n    #[JoinColumn(nullable: false)]\n    public $d;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7259Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH7259Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7259Space::class, GH7259File::class, GH7259FileVersion::class, GH7259Feed::class]);\n    }\n\n    #[Group('GH-7259')]\n    public function testPersistFileBeforeVersion(): void\n    {\n        $space = new GH7259Space();\n\n        $this->_em->persist($space);\n        $this->_em->flush();\n\n        $feed        = new GH7259Feed();\n        $feed->space = $space;\n\n        $file              = new GH7259File();\n        $file->space       = $space;\n        $fileVersion       = new GH7259FileVersion();\n        $fileVersion->file = $file;\n\n        $this->_em->persist($file);\n        $this->_em->persist($fileVersion);\n        $this->_em->persist($feed);\n\n        $this->_em->flush();\n\n        self::assertNotNull($fileVersion->id);\n    }\n\n    #[Group('GH-7259')]\n    public function testPersistFileAfterVersion(): void\n    {\n        $space = new GH7259Space();\n\n        $this->_em->persist($space);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $space = $this->_em->find(GH7259Space::class, $space->id);\n\n        $feed        = new GH7259Feed();\n        $feed->space = $space;\n\n        $file              = new GH7259File();\n        $file->space       = $space;\n        $fileVersion       = new GH7259FileVersion();\n        $fileVersion->file = $file;\n\n        $this->_em->persist($fileVersion);\n        $this->_em->persist($file);\n        $this->_em->persist($feed);\n\n        $this->_em->flush();\n\n        self::assertNotNull($fileVersion->id);\n    }\n}\n\n#[Entity]\nclass GH7259File\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7259Space|null */\n    #[ManyToOne(targetEntity: GH7259Space::class)]\n    #[JoinColumn(nullable: false)]\n    public $space;\n}\n\n#[Entity]\nclass GH7259FileVersion\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7259File|null */\n    #[ManyToOne(targetEntity: GH7259File::class)]\n    #[JoinColumn(nullable: false)]\n    public $file;\n}\n\n#[Entity]\nclass GH7259Space\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7259File|null */\n    #[ManyToOne(targetEntity: GH7259File::class)]\n    #[JoinColumn(nullable: true)]\n    public $ruleFile;\n}\n\n#[Entity]\nclass GH7259Feed\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH7259Space|null */\n    #[ManyToOne(targetEntity: GH7259Space::class)]\n    #[JoinColumn(nullable: false)]\n    public $space;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7286Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\AST\\Node;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH7286Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH7286Entity::class,\n            ],\n        );\n\n        $this->_em->persist(new GH7286Entity('foo', 1));\n        $this->_em->persist(new GH7286Entity('foo', 2));\n        $this->_em->persist(new GH7286Entity('bar', 3));\n        $this->_em->persist(new GH7286Entity(null, 4));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testAggregateExpressionInFunction(): void\n    {\n        $query = $this->_em->createQuery(\n            'SELECT CONCAT(e.type, MIN(e.version)) pair'\n            . ' FROM ' . GH7286Entity::class . ' e'\n            . ' WHERE e.type IS NOT NULL'\n            . ' GROUP BY e.type'\n            . ' ORDER BY e.type',\n        );\n\n        self::assertSame(\n            [\n                ['pair' => 'bar3'],\n                ['pair' => 'foo1'],\n            ],\n            $query->getArrayResult(),\n        );\n    }\n\n    #[Group('DDC-1091')]\n    public function testAggregateFunctionInCustomFunction(): void\n    {\n        $this->_em->getConfiguration()->addCustomStringFunction('CC', GH7286CustomConcat::class);\n\n        $query = $this->_em->createQuery(\n            'SELECT CC(e.type, MIN(e.version)) pair'\n            . ' FROM ' . GH7286Entity::class . ' e'\n            . ' WHERE e.type IS NOT NULL AND e.type != :type'\n            . ' GROUP BY e.type',\n        );\n        $query->setParameter('type', 'bar');\n\n        self::assertSame(\n            ['pair' => 'foo1'],\n            $query->getSingleResult(),\n        );\n    }\n}\n\n#[Entity]\nclass GH7286Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[Column(nullable: true)]\n        public string|null $type,\n        #[Column(type: 'integer')]\n        public int $version,\n    ) {\n    }\n}\n\nclass GH7286CustomConcat extends FunctionNode\n{\n    private Node|null $first = null;\n\n    private Node|null $second = null;\n\n    public function parse(Parser $parser): void\n    {\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->first = $parser->StringPrimary();\n        $parser->match(TokenType::T_COMMA);\n        $this->second = $parser->StringPrimary();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n\n    public function getSql(SqlWalker $walker): string\n    {\n        return $walker->getConnection()->getDatabasePlatform()->getConcatExpression(\n            $this->first->dispatch($walker),\n            $this->second->dispatch($walker),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7366Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\ORM\\TransactionRequiredException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH7366Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH7366Entity::class,\n            ],\n        );\n\n        $this->_em->persist(new GH7366Entity('baz'));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testOptimisticLockNoExceptionOnFind(): void\n    {\n        try {\n            $entity = $this->_em->find(GH7366Entity::class, 1, LockMode::OPTIMISTIC);\n        } catch (TransactionRequiredException) {\n            self::fail('EntityManager::find() threw TransactionRequiredException with LockMode::OPTIMISTIC');\n        }\n\n        self::assertEquals('baz', $entity->getName());\n    }\n}\n\n#[Entity]\nclass GH7366Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Version]\n    protected $lockVersion = 1;\n\n    public function __construct(\n        #[Column(length: 32)]\n        protected string $name,\n    ) {\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7496WithToIterableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\Tests\\IterableTester;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH7496WithToIterableTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH7496EntityA::class,\n                GH7496EntityB::class,\n                GH7496EntityAinB::class,\n            ],\n        );\n\n        $this->_em->persist($a1 = new GH7496EntityA(1, 'A#1'));\n        $this->_em->persist($a2 = new GH7496EntityA(2, 'A#2'));\n        $this->_em->persist($b1 = new GH7496EntityB(1, 'B#1'));\n        $this->_em->persist(new GH7496EntityAinB(1, $a1, $b1));\n        $this->_em->persist(new GH7496EntityAinB(2, $a2, $b1));\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testNonUniqueObjectHydrationDuringIteration(): void\n    {\n        $q = $this->_em->createQuery(\n            'SELECT b FROM ' . GH7496EntityAinB::class . ' aib JOIN ' . GH7496EntityB::class . ' b ON aib.eB = b',\n        );\n\n        $bs = IterableTester::iterableToArray(\n            $q->toIterable([], AbstractQuery::HYDRATE_OBJECT),\n        );\n\n        self::assertCount(2, $bs);\n        self::assertInstanceOf(GH7496EntityB::class, $bs[0]);\n        self::assertInstanceOf(GH7496EntityB::class, $bs[1]);\n        self::assertEquals(1, $bs[0]->id);\n        self::assertEquals(1, $bs[1]->id);\n\n        $bs = IterableTester::iterableToArray(\n            $q->toIterable([], AbstractQuery::HYDRATE_ARRAY),\n        );\n\n        self::assertCount(2, $bs);\n        self::assertEquals(1, $bs[0]['id']);\n        self::assertEquals(1, $bs[1]['id']);\n    }\n}\n\n#[Entity]\nclass GH7496EntityA\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer', name: 'a_id')]\n        public int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH7496EntityB\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer', name: 'b_id')]\n        public int $id,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH7496EntityAinB\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n        #[ManyToOne(targetEntity: GH7496EntityA::class)]\n        #[JoinColumn(name: 'a_id', referencedColumnName: 'a_id', nullable: false)]\n        public GH7496EntityA $eA,\n        #[ManyToOne(targetEntity: GH7496EntityB::class)]\n        #[JoinColumn(name: 'b_id', referencedColumnName: 'b_id', nullable: false)]\n        public GH7496EntityB $eB,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7505Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\n#[Group('GH7505')]\nfinal class GH7505Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH7505AbstractResponse::class,\n            GH7505ArrayResponse::class,\n            GH7505TextResponse::class,\n        ]);\n    }\n\n    public function testSimpleArrayTypeHydratedCorrectly(): void\n    {\n        $arrayResponse = new GH7505ArrayResponse();\n        $this->_em->persist($arrayResponse);\n\n        $textResponse = new GH7505TextResponse();\n        $this->_em->persist($textResponse);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(GH7505AbstractResponse::class);\n\n        $arrayResponse = $repository->find($arrayResponse->id);\n        assert($arrayResponse instanceof GH7505ArrayResponse);\n        self::assertSame([], $arrayResponse->value);\n\n        $textResponse = $repository->find($textResponse->id);\n        assert($textResponse instanceof GH7505TextResponse);\n        self::assertNull($textResponse->value);\n    }\n}\n\n#[Table(name: 'gh7505_responses')]\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['array' => GH7505ArrayResponse::class, 'text' => GH7505TextResponse::class])]\nabstract class GH7505AbstractResponse\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n}\n\n#[Entity]\nclass GH7505ArrayResponse extends GH7505AbstractResponse\n{\n    /** @var mixed[] */\n    #[Column(name: 'value_array', type: 'simple_array')]\n    public $value = [];\n}\n\n#[Entity]\nclass GH7505TextResponse extends GH7505AbstractResponse\n{\n    /** @var string|null */\n    #[Column(name: 'value_string', type: 'string', length: 255)]\n    public $value;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7512Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH7512Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH7512EntityA::class,\n            GH7512EntityB::class,\n            GH7512EntityC::class,\n        ]);\n\n        $this->_em->persist(new GH7512EntityA());\n        $this->_em->persist(new GH7512EntityC());\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testFindEntityByAssociationPropertyJoinedChildWithClearMetadata(): void\n    {\n        // pretend we are starting afresh\n        $this->_em = $this->getEntityManager();\n        $result    = $this->_em->getRepository(GH7512EntityC::class)->findBy([\n            'entityA' => new GH7512EntityB(),\n        ]);\n        $this->assertEmpty($result);\n    }\n}\n\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorMap(['entitya' => GH7512EntityA::class, 'entityB' => GH7512EntityB::class])]\nclass GH7512EntityA\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var Collection<int, GH7512EntityC> */\n    #[OneToMany(targetEntity: 'GH7512EntityC', mappedBy: 'entityA')]\n    public $entityCs;\n}\n\n#[Entity]\nclass GH7512EntityB extends GH7512EntityA\n{\n}\n\n#[Entity]\nclass GH7512EntityC\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var GH7512EntityA */\n    #[ManyToOne(targetEntity: 'GH7512EntityA', inversedBy: 'entityCs')]\n    public $entityA;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7629Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass GH7629Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH7629Entity::class,\n        ]);\n\n        $this->_em->persist(new GH7629Entity());\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testClearScheduledForSynchronizationWhenCommitEmpty(): void\n    {\n        $entity = $this->_em->find(GH7629Entity::class, 1);\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDirtyCheck($entity));\n    }\n\n    #[Group('GH-8231')]\n    public function testPersistAfterRemoveSchedulesForSynchronization(): void\n    {\n        $entity = $this->_em->find(GH7629Entity::class, 1);\n\n        $this->_em->remove($entity);\n\n        $this->_em->persist($entity);\n\n        self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDirtyCheck($entity));\n    }\n}\n\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass GH7629Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7661Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_keys;\n\n#[Group('GH-7661')]\nclass GH7661Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH7661User::class,\n            GH7661Event::class,\n            GH7661Participant::class,\n        ]);\n\n        $u1 = new GH7661User();\n        $u2 = new GH7661User();\n        $e  = new GH7661Event();\n        $this->_em->persist($u1);\n        $this->_em->persist($u2);\n        $this->_em->persist($e);\n        $this->_em->persist(new GH7661Participant($u1, $e));\n        $this->_em->persist(new GH7661Participant($u2, $e));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testIndexByAssociation(): void\n    {\n        $e    = $this->_em->find(GH7661Event::class, 1);\n        $keys = $e->participants->getKeys();\n        self::assertEquals([1, 2], $keys);\n\n        $participants = $this->_em->createQuery('SELECT p FROM ' . GH7661Participant::class . ' p INDEX BY p.user')->getResult();\n        $keys         = array_keys($participants);\n        self::assertEquals([1, 2], $keys);\n    }\n}\n\n#[Entity]\nclass GH7661User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH7661Event\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n    /** @var GH7661Participant[] */\n    #[OneToMany(targetEntity: GH7661Participant::class, mappedBy: 'event', indexBy: 'user_id')]\n    public $participants;\n}\n\n#[Entity]\nclass GH7661Participant\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: GH7661User::class)]\n        public GH7661User $user,\n        #[ManyToOne(targetEntity: GH7661Event::class)]\n        public GH7661Event $event,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7684Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Schema\\Name\\Identifier;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\Tests\\ORM\\Functional\\DatabaseDriverTestCase;\n\nuse function class_exists;\n\n/**\n * Verifies that associations/columns with an inline '_id' get named properly\n *\n * Github issue: 7684\n */\nclass GH7684Test extends DatabaseDriverTestCase\n{\n    public function testIssue(): void\n    {\n        $table1 = new Table('GH7684_identity_test_table');\n        $table1->addColumn('id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $table1->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $table1->setPrimaryKey(['id']);\n        }\n\n        $table2 = new Table('GH7684_identity_test_assoc_table');\n        $table2->addColumn('id', 'integer');\n        $table2->addColumn('gh7684_identity_test_id', 'integer');\n\n        if (class_exists(PrimaryKeyConstraint::class)) {\n            $table2->addPrimaryKeyConstraint(new PrimaryKeyConstraint(null, [new UnqualifiedName(Identifier::unquoted('id'))], true));\n        } else {\n            $table2->setPrimaryKey(['id']);\n        }\n\n        $table2->addForeignKeyConstraint('GH7684_identity_test', ['gh7684_identity_test_id'], ['id']);\n\n        $metadatas = $this->convertToClassMetadata([$table1, $table2]);\n        $metadata  = $metadatas['Gh7684IdentityTestAssocTable'];\n\n        self::assertArrayHasKey('gh7684IdentityTest', $metadata->associationMappings);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7717Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Tests\\Models\\GH7717\\GH7717Child;\nuse Doctrine\\Tests\\Models\\GH7717\\GH7717Parent;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH7717Test extends OrmFunctionalTestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH7717Parent::class,\n            GH7717Child::class,\n        );\n    }\n\n    public function testManyToManyPersisterIsNullComparison(): void\n    {\n        $childWithNullProperty                      = new GH7717Child();\n        $childWithoutNullProperty                   = new GH7717Child();\n        $childWithoutNullProperty->nullableProperty = 'nope';\n\n        $parent           = new GH7717Parent();\n        $parent->children = new ArrayCollection([$childWithNullProperty, $childWithoutNullProperty]);\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $parent = $this->_em->find(GH7717Parent::class, 1);\n\n        $this->assertCount(1, $parent->children->matching(Criteria::create(true)->where(\n            Criteria::expr()->isNull('nullableProperty'),\n        )));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7735Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Cache;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\Test;\n\nuse function assert;\n\nfinal class GH7735Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH7735Car::class,\n            GH7735Power::class,\n            GH7735Engine::class,\n        );\n\n        $this->_em->persist(new GH7735Car(1, new GH7735Engine(1, 'turbo', new GH7735Power(1))));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    #[Test]\n    #[Group('GH7735')]\n    public function findByReturnsCachedEntity(): void\n    {\n        $this->_em->getCache()->evictEntityRegion(GH7735Power::class);\n\n        $car = $this->_em->find(GH7735Car::class, 1);\n        assert($car instanceof GH7735Car);\n\n        self::assertSame('turbo', $car->getEngine()->getModel());\n        self::assertSame(1, $car->getEngine()->getPower()->getId());\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'READ_ONLY')]\nclass GH7735Car\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        private int $id,\n        #[ManyToOne(targetEntity: GH7735Engine::class, cascade: ['all'])]\n        #[JoinColumn(nullable: false)]\n        #[Cache('READ_ONLY')]\n        private GH7735Engine $engine,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getEngine(): GH7735Engine\n    {\n        return $this->engine;\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'READ_ONLY')]\nclass GH7735Engine\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        private int $id,\n        #[Column]\n        private string $model,\n        #[OneToOne(targetEntity: GH7735Power::class, mappedBy: 'engine', cascade: ['all'])]\n        #[Cache('READ_ONLY')]\n        private GH7735Power $power,\n    ) {\n        $power->setEngine($this);\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getPower(): GH7735Power\n    {\n        return $this->power;\n    }\n\n    public function getModel(): string\n    {\n        return $this->model;\n    }\n}\n\n#[Entity]\n#[Cache(usage: 'READ_ONLY')]\nclass GH7735Power\n{\n    #[OneToOne(targetEntity: GH7735Engine::class, inversedBy: 'power')]\n    #[Cache('READ_ONLY')]\n    private GH7735Engine|null $engine = null;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        private int $id,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setEngine(GH7735Engine $engine): void\n    {\n        $this->engine = $engine;\n    }\n\n    public function getEngine(): GH7735Engine\n    {\n        return $this->engine;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7737Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\Test;\n\n#[Group('GH7737')]\nclass GH7737Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7737Group::class, GH7737Person::class]);\n\n        $group1 = new GH7737Group(1, 'Test 1');\n        $person = new GH7737Person(1);\n        $person->groups->add($group1);\n\n        $this->_em->persist($person);\n        $this->_em->persist($group1);\n        $this->_em->persist(new GH7737Group(2, 'Test 2'));\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    #[Test]\n    public function memberOfCriteriaShouldBeCompatibleWithQueryBuilder(): void\n    {\n        $query = $this->_em->createQueryBuilder()\n            ->select('person')\n            ->from(GH7737Person::class, 'person')\n            ->addCriteria(Criteria::create(true)->where(Criteria::expr()->memberOf(':group', 'person.groups')))\n            ->getQuery();\n\n        $group1   = $this->_em->find(GH7737Group::class, 1);\n        $matching = $query->setParameter('group', $group1)->getOneOrNullResult();\n\n        self::assertInstanceOf(GH7737Person::class, $matching);\n        self::assertSame(1, $matching->id);\n\n        $group2      = $this->_em->find(GH7737Group::class, 2);\n        $notMatching = $query->setParameter('group', $group2)->getOneOrNullResult();\n\n        self::assertNull($notMatching);\n    }\n}\n\n#[Entity]\nclass GH7737Group\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n        #[Column]\n        public string $name,\n    ) {\n    }\n}\n\n#[Entity]\nclass GH7737Person\n{\n    /** @var Collection<int, GH7737Group> */\n    #[JoinTable]\n    #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', unique: true)]\n    #[ManyToMany(targetEntity: GH7737Group::class)]\n    public $groups;\n\n    public function __construct(\n        #[Id]\n        #[Column(type: 'integer')]\n        public int $id,\n    ) {\n        $this->groups = new ArrayCollection();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7761Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\ChangeTrackingPolicy;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nfinal class GH7761Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH7761Entity::class,\n            GH7761ChildEntity::class,\n        ]);\n\n        $parent = new GH7761Entity();\n        $child  = new GH7761ChildEntity();\n        $parent->children->add($child);\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testCollectionClearDoesNotClearIfNotPersisted(): void\n    {\n        $entity = $this->_em->find(GH7761Entity::class, 1);\n        assert($entity instanceof GH7761Entity);\n        $entity->children->clear();\n        $this->_em->persist(new GH7761Entity());\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(GH7761Entity::class, 1);\n        self::assertCount(1, $entity->children);\n    }\n\n    #[Group('GH-7862')]\n    public function testCollectionClearDoesClearIfPersisted(): void\n    {\n        $entity = $this->_em->find(GH7761Entity::class, 1);\n        assert($entity instanceof GH7761Entity);\n        $entity->children->clear();\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(GH7761Entity::class, 1);\n        self::assertCount(0, $entity->children);\n    }\n}\n\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass GH7761Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, GH7761ChildEntity> */\n    #[JoinTable(name: 'gh7761_to_child')]\n    #[JoinColumn(name: 'entity_id')]\n    #[InverseJoinColumn(name: 'child_id')]\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH7761ChildEntity', cascade: ['all'])]\n    public $children;\n\n    public function __construct()\n    {\n        $this->children = new ArrayCollection();\n    }\n}\n\n#[Entity]\n#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]\nclass GH7761ChildEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7767Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function class_exists;\n\n#[Group('GH7767')]\nclass GH7767Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7767ParentEntity::class, GH7767ChildEntity::class]);\n\n        $parent = new GH7767ParentEntity();\n        $parent->addChild(200);\n        $parent->addChild(100);\n        $parent->addChild(300);\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testMatchingRespectsCollectionOrdering(): void\n    {\n        $parent = $this->_em->find(GH7767ParentEntity::class, 1);\n        assert($parent instanceof GH7767ParentEntity);\n\n        $children = $parent->getChildren()->matching(Criteria::create(true));\n\n        self::assertEquals(100, $children[0]->position);\n        self::assertEquals(200, $children[1]->position);\n        self::assertEquals(300, $children[2]->position);\n    }\n\n    public function testMatchingOverrulesCollectionOrdering(): void\n    {\n        $parent = $this->_em->find(GH7767ParentEntity::class, 1);\n        assert($parent instanceof GH7767ParentEntity);\n\n        $children = $parent->getChildren()->matching(\n            Criteria::create(true)->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC']),\n        );\n\n        self::assertEquals(300, $children[0]->position);\n        self::assertEquals(200, $children[1]->position);\n        self::assertEquals(100, $children[2]->position);\n    }\n}\n\n#[Entity]\nclass GH7767ParentEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */\n    #[OneToMany(targetEntity: GH7767ChildEntity::class, mappedBy: 'parent', fetch: 'EXTRA_LAZY', cascade: ['persist'])]\n    #[OrderBy(['position' => 'ASC'])]\n    private $children;\n\n    public function addChild(int $position): void\n    {\n        $this->children[] = new GH7767ChildEntity($this, $position);\n    }\n\n    /** @phpstan-return Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n}\n\n#[Entity]\nclass GH7767ChildEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: GH7767ParentEntity::class, inversedBy: 'children')]\n        private GH7767ParentEntity $parent,\n        #[Column(type: 'integer')]\n        public int $position,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7820Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\StringType;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Assert;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Psr\\Cache\\CacheItemInterface;\nuse Stringable;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\nuse Symfony\\Component\\Cache\\CacheItem;\n\nuse function array_map;\nuse function is_string;\nuse function iterator_to_array;\n\n/**\n * When using a {@see \\Doctrine\\ORM\\Tools\\Pagination\\Paginator} to iterate over a query\n * that has entities with a custom DBAL type used in the identifier, then `$id->__toString()`\n * is used implicitly by {@see \\PDOStatement::bindValue()}, instead of being converted by the\n * expected {@see \\Doctrine\\DBAL\\Types\\Type::convertToDatabaseValue()}.\n *\n * In order to reproduce this, you must have identifiers implementing\n * `#__toString()` (to allow {@see \\Doctrine\\ORM\\UnitOfWork} to hash them) and other accessors\n * that are used by the custom DBAL type during DB/PHP conversions.\n *\n * If `#__toString()` and the DBAL type conversions are asymmetric, then the paginator will fail\n * to find records.\n * Tricky situation, but this very much affects `ramsey/uuid-doctrine` and anyone relying on (for\n * example) the {@see \\Ramsey\\Uuid\\Doctrine\\UuidBinaryType} type.\n */\n#[Group('GH7820')]\nclass GH7820Test extends OrmFunctionalTestCase\n{\n    private const SONG = [\n        'What is this song all about?',\n        'Can\\'t figure any lyrics out',\n        'How do the words to it go?',\n        'I wish you\\'d tell me, I don\\'t know',\n        'Don\\'t know, don\\'t know, don\\'t know, I don\\'t know!',\n        'Don\\'t know, don\\'t know, don\\'t know...',\n    ];\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! Type::hasType(GH7820LineTextType::class)) {\n            Type::addType(GH7820LineTextType::class, GH7820LineTextType::class);\n        }\n\n        $this->setUpEntitySchema([GH7820Line::class]);\n\n        $this->_em->createQuery('DELETE FROM ' . GH7820Line::class . ' l')\n            ->execute();\n\n        foreach (self::SONG as $index => $line) {\n            $this->_em->persist(new GH7820Line(GH7820LineText::fromText($line), $index));\n        }\n\n        $this->_em->flush();\n    }\n\n    public function testWillFindSongsInPaginator(): void\n    {\n        $lines = $this->fetchSongLinesWithPaginator();\n\n        self::assertSame(self::SONG, $lines);\n    }\n\n    #[Group('GH7837')]\n    public function testWillFindSongsInPaginatorEvenWithCachedQueryParsing(): void\n    {\n        // Enable the query cache\n        $this->_em->getConfiguration()\n            ->getQueryCache()\n            ->clear();\n\n        // Fetch song lines with the paginator, also priming the query cache\n        $lines = $this->fetchSongLinesWithPaginator();\n        self::assertSame(self::SONG, $lines, 'Expected to return expected data before query cache is populated with DQL -> SQL translation. Were SQL parameters translated?');\n\n        // Fetch song lines again\n        $lines = $this->fetchSongLinesWithPaginator();\n        self::assertSame(self::SONG, $lines, 'Expected to return expected data even when DQL -> SQL translation is present in cache. Were SQL parameters translated again?');\n    }\n\n    public function testPaginatorDoesNotForceCacheToUpdateEntries(): void\n    {\n        $this->_em->getConfiguration()->setQueryCache(new class extends ArrayAdapter {\n            public function save(CacheItemInterface $item): bool\n            {\n                Assert::assertFalse($this->hasItem($item->getKey()), 'The cache should not have to overwrite the entry');\n\n                return parent::save($item);\n            }\n        });\n\n        // \"Prime\" the cache (in fact, that should not even happen)\n        $this->fetchSongLinesWithPaginator();\n\n        // Make sure we can query again without overwriting the cache\n        $this->fetchSongLinesWithPaginator();\n    }\n\n    public function testPaginatorQueriesWillBeCached(): void\n    {\n        $cache = new class extends ArrayAdapter {\n            /** @var bool */\n            private $failOnCacheMiss = false;\n\n            public function failOnCacheMiss(): void\n            {\n                $this->failOnCacheMiss = true;\n            }\n\n            public function getItem($key): CacheItem\n            {\n                $item = parent::getItem($key);\n                Assert::assertTrue(! $this->failOnCacheMiss || $item->isHit(), 'cache was missed');\n\n                return $item;\n            }\n        };\n        $this->_em->getConfiguration()->setQueryCache($cache);\n\n        // Prime the cache\n        $this->fetchSongLinesWithPaginator();\n\n        $cache->failOnCacheMiss();\n\n        $this->fetchSongLinesWithPaginator();\n    }\n\n    private function fetchSongLinesWithPaginator(): array\n    {\n        $query = $this->_em->getRepository(GH7820Line::class)\n            ->createQueryBuilder('l')\n            ->orderBy('l.lineNumber', 'ASC')\n            ->setMaxResults(100);\n\n        return array_map(static fn (GH7820Line $line): string => $line->toString(), iterator_to_array(new Paginator($query)));\n    }\n}\n\n#[Entity]\nclass GH7820Line\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH7820LineTextType', length: 255)]\n        private GH7820LineText $text,\n        #[Column(type: 'integer')]\n        private int $lineNumber,\n    ) {\n    }\n\n    public function toString(): string\n    {\n        return $this->text->getText();\n    }\n}\n\nfinal class GH7820LineText implements Stringable\n{\n    private function __construct(private string $text)\n    {\n    }\n\n    public static function fromText(string $text): self\n    {\n        return new self($text);\n    }\n\n    public function getText(): string\n    {\n        return $this->text;\n    }\n\n    public function __toString(): string\n    {\n        return 'Line: ' . $this->text;\n    }\n}\n\nfinal class GH7820LineTextType extends StringType\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToPHPValue($value, AbstractPlatform $platform): mixed\n    {\n        $text = parent::convertToPHPValue($value, $platform);\n\n        if (! is_string($text)) {\n            return $text;\n        }\n\n        return GH7820LineText::fromText($text);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed\n    {\n        if (! $value instanceof GH7820LineText) {\n            return parent::convertToDatabaseValue($value, $platform);\n        }\n\n        return parent::convertToDatabaseValue($value->getText(), $platform);\n    }\n\n    /** {@inheritDoc} */\n    public function getName(): string\n    {\n        return self::class;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7829Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH7829')]\nfinal class GH7829Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n\n        $article = new CmsArticle();\n\n        $article->topic = 'Skip Limit Subquery';\n        $article->text  = 'Skip Limit Subquery if not required.';\n\n        $this->_em->persist($article);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testPaginatorWithLimitSubquery(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n\n        $query = $this->_em->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a');\n        $query->setMaxResults(1);\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(false);\n\n        $paginator->count();\n        $paginator->getIterator();\n\n        $this->assertQueryCount(3);\n    }\n\n    public function testPaginatorWithLimitSubquerySkipped(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n\n        $query = $this->_em->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a');\n\n        $paginator = new Paginator($query, true);\n        $paginator->setUseOutputWalkers(false);\n\n        $paginator->count();\n        $paginator->getIterator();\n\n        $this->assertQueryCount(2);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7836Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\Common\\Collections\\Selectable;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function class_exists;\n\n#[Group('GH7836')]\nclass GH7836Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7836ParentEntity::class, GH7836ChildEntity::class]);\n\n        $parent = new GH7836ParentEntity();\n        $parent->addChild(100, 'foo');\n        $parent->addChild(100, 'bar');\n        $parent->addChild(200, 'baz');\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testMatchingRespectsCollectionOrdering(): void\n    {\n        $parent = $this->_em->find(GH7836ParentEntity::class, 1);\n        assert($parent instanceof GH7836ParentEntity);\n\n        $children = $parent->getChildren()->matching(Criteria::create(true));\n\n        self::assertSame(100, $children[0]->position);\n        self::assertSame('bar', $children[0]->name);\n        self::assertSame(100, $children[1]->position);\n        self::assertSame('foo', $children[1]->name);\n        self::assertSame(200, $children[2]->position);\n        self::assertSame('baz', $children[2]->name);\n    }\n\n    public function testMatchingOverrulesCollectionOrdering(): void\n    {\n        $parent = $this->_em->find(GH7836ParentEntity::class, 1);\n        assert($parent instanceof GH7836ParentEntity);\n\n        $children = $parent->getChildren()->matching(\n            Criteria::create(true)->orderBy(\n                class_exists(Order::class)\n                    ? ['position' => Order::Descending, 'name' => Order::Ascending]\n                    : ['position' => 'DESC', 'name' => 'ASC'],\n            ),\n        );\n\n        self::assertSame(200, $children[0]->position);\n        self::assertSame('baz', $children[0]->name);\n        self::assertSame(100, $children[1]->position);\n        self::assertSame('bar', $children[1]->name);\n        self::assertSame(100, $children[2]->position);\n        self::assertSame('foo', $children[2]->name);\n    }\n\n    public function testMatchingKeepsOrderOfCriteriaOrderingKeys(): void\n    {\n        $parent = $this->_em->find(GH7836ParentEntity::class, 1);\n        assert($parent instanceof GH7836ParentEntity);\n\n        $children = $parent->getChildren()->matching(\n            Criteria::create(true)->orderBy(\n                class_exists(Order::class)\n                    ? ['name' => Order::Ascending, 'position' => Order::Ascending]\n                    : ['name' => 'ASC', 'position' => 'ASC'],\n            ),\n        );\n\n        self::assertSame(100, $children[0]->position);\n        self::assertSame('bar', $children[0]->name);\n        self::assertSame(200, $children[1]->position);\n        self::assertSame('baz', $children[1]->name);\n        self::assertSame(100, $children[2]->position);\n        self::assertSame('foo', $children[2]->name);\n    }\n}\n\n#[Entity]\nclass GH7836ParentEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @var Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity> */\n    #[OneToMany(targetEntity: GH7836ChildEntity::class, mappedBy: 'parent', fetch: 'EXTRA_LAZY', cascade: ['persist'])]\n    #[OrderBy(['position' => 'ASC', 'name' => 'ASC'])]\n    private $children;\n\n    public function addChild(int $position, string $name): void\n    {\n        $this->children[] = new GH7836ChildEntity($this, $position, $name);\n    }\n\n    /** @phpstan-return Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity> */\n    public function getChildren(): Collection\n    {\n        return $this->children;\n    }\n}\n\n#[Entity]\nclass GH7836ChildEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function __construct(\n        #[ManyToOne(targetEntity: GH7836ParentEntity::class, inversedBy: 'children')]\n        private GH7836ParentEntity $parent,\n        #[Column(type: 'integer')]\n        public int $position,\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7864Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_values;\n\n#[Group('gh7864')]\nclass GH7864Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH7864User::class,\n                GH7864Tweet::class,\n            ],\n        );\n    }\n\n    public function testExtraLazyRemoveElement(): void\n    {\n        $user       = new GH7864User();\n        $user->name = 'test';\n\n        $tweet1          = new GH7864Tweet();\n        $tweet1->content = 'Hello World!';\n        $user->addTweet($tweet1);\n\n        $tweet2          = new GH7864Tweet();\n        $tweet2->content = 'Goodbye, and thanks for all the fish';\n        $user->addTweet($tweet2);\n\n        $this->_em->persist($user);\n        $this->_em->persist($tweet1);\n        $this->_em->persist($tweet2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $user  = $this->_em->find(GH7864User::class, $user->id);\n        $tweet = $this->_em->find(GH7864Tweet::class, $tweet1->id);\n\n        $user->tweets->removeElement($tweet);\n\n        $tweets = $user->tweets->map(static fn (GH7864Tweet $tweet) => $tweet->content);\n\n        self::assertEquals(['Goodbye, and thanks for all the fish'], array_values($tweets->toArray()));\n    }\n}\n\n#[Entity]\nclass GH7864User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var Collection<int, GH7864Tweet> */\n    #[OneToMany(targetEntity: 'GH7864Tweet', mappedBy: 'user', fetch: 'EXTRA_LAZY')]\n    public $tweets;\n\n    public function __construct()\n    {\n        $this->tweets = new ArrayCollection();\n    }\n\n    public function addTweet(GH7864Tweet $tweet): void\n    {\n        $tweet->user = $this;\n        $this->tweets->add($tweet);\n    }\n}\n\n#[Entity]\nclass GH7864Tweet\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $content;\n\n    /** @var GH7864User */\n    #[ManyToOne(targetEntity: 'GH7864User', inversedBy: 'tweets')]\n    public $user;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7869Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Decorator\\EntityManagerDecorator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function method_exists;\n\n#[Group('GH7869')]\nclass GH7869Test extends OrmTestCase\n{\n    public function testDQLDeferredEagerLoad(): void\n    {\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $connection = $this->createMock(Connection::class);\n        $connection->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        if (method_exists($connection, 'getEventManager')) {\n            $connection->method('getEventManager')\n                ->willReturn(new EventManager());\n        }\n\n        $em = new class (new EntityManagerMock($connection)) extends EntityManagerDecorator {\n            /** @var int */\n            public $getClassMetadataCalls = 0;\n\n            public function getClassMetadata($className): ClassMetadata\n            {\n                ++$this->getClassMetadataCalls;\n\n                return parent::getClassMetadata($className);\n            }\n        };\n\n        $hints = [\n            UnitOfWork::HINT_DEFEREAGERLOAD => true,\n            'fetchMode' => [GH7869Appointment::class => ['patient' => ClassMetadata::FETCH_EAGER]],\n        ];\n\n        $uow = new UnitOfWork($em);\n        $uow->createEntity(GH7869Appointment::class, ['id' => 1, 'patient_id' => 1], $hints);\n        $uow->clear();\n        $uow->triggerEagerLoads();\n\n        self::assertSame(4, $em->getClassMetadataCalls);\n    }\n}\n\n#[Entity]\nclass GH7869Appointment\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH7869Patient */\n    #[OneToOne(targetEntity: 'GH7869Patient', inversedBy: 'appointment', fetch: 'EAGER')]\n    public $patient;\n}\n\n#[Entity]\nclass GH7869Patient\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH7869Appointment */\n    #[OneToOne(targetEntity: 'GH7869Appointment', mappedBy: 'patient')]\n    public $appointment;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7875Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\After;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_filter;\nuse function current;\nuse function sprintf;\nuse function str_contains;\nuse function str_starts_with;\n\n#[Group('GH7875')]\nfinal class GH7875Test extends OrmFunctionalTestCase\n{\n    #[After]\n    public function cleanUpSchema(): void\n    {\n        $connection = $this->_em->getConnection();\n\n        $connection->executeStatement('DROP TABLE IF EXISTS gh7875_my_entity');\n        $connection->executeStatement('DROP TABLE IF EXISTS gh7875_my_other_entity');\n\n        $platform = $connection->getDatabasePlatform();\n        if ($platform instanceof PostgreSQLPlatform) {\n            $connection->executeStatement('DROP SEQUENCE IF EXISTS gh7875_my_entity_id_seq');\n            $connection->executeStatement('DROP SEQUENCE IF EXISTS gh7875_my_other_entity_id_seq');\n        }\n    }\n\n    /**\n     * @param string[] $sqls\n     *\n     * @return string[]\n     */\n    private function filterCreateTable(array $sqls, string $tableName): array\n    {\n        return array_filter($sqls, static fn (string $sql): bool => str_starts_with($sql, sprintf('CREATE TABLE %s (', $tableName)));\n    }\n\n    public function testUpdateSchemaSql(): void\n    {\n        $classes = [GH7875MyEntity::class];\n\n        $sqls = $this->filterCreateTable($this->getUpdateSchemaSqlForModels(...$classes), 'gh7875_my_entity');\n\n        self::assertCount(1, $sqls);\n\n        $this->_em->getConnection()->executeStatement(current($sqls));\n\n        $sqls = array_filter($this->getUpdateSchemaSqlForModels(...$classes), static fn (string $sql): bool => str_contains($sql, ' gh7875_my_entity '));\n\n        self::assertSame([], $sqls);\n\n        $classes[] = GH7875MyOtherEntity::class;\n\n        $sqls = $this->getUpdateSchemaSqlForModels(...$classes);\n\n        self::assertCount(0, $this->filterCreateTable($sqls, 'gh7875_my_entity'));\n        self::assertCount(1, $this->filterCreateTable($sqls, 'gh7875_my_other_entity'));\n    }\n\n    public function testUpdateSchemaSqlWithSchemaAssetFilter(): void\n    {\n        $filterCallback = static fn ($assetName): bool => $assetName !== 'gh7875_my_entity';\n\n        $class = GH7875MyEntity::class;\n\n        $this->createSchemaForModels($class);\n\n        $config = $this->_em->getConnection()->getConfiguration();\n        $config->setSchemaAssetsFilter($filterCallback);\n\n        $previousFilter = $config->getSchemaAssetsFilter();\n\n        $sqls = $this->getUpdateSchemaSqlForModels($class);\n        $sqls = array_filter($sqls, static fn (string $sql): bool => str_contains($sql, ' gh7875_my_entity '));\n\n        self::assertCount(0, $sqls);\n        self::assertSame($previousFilter, $config->getSchemaAssetsFilter());\n    }\n}\n\n#[Table(name: 'gh7875_my_entity')]\n#[Entity]\nclass GH7875MyEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Table(name: 'gh7875_my_other_entity')]\n#[Entity]\nclass GH7875MyOtherEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH7941Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTimeImmutable;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\Test;\n\nuse function ltrim;\nuse function strlen;\n\n#[Group('GH7941')]\nfinal class GH7941Test extends OrmFunctionalTestCase\n{\n    private const PRODUCTS = [\n        ['name' => 'Test 1', 'price' => '100', 'square_root' => 10],\n        ['name' => 'Test 2', 'price' => '100', 'square_root' => 10],\n        ['name' => 'Test 3', 'price' => '100', 'square_root' => 10],\n        ['name' => 'Test 4', 'price' => '25', 'square_root' => 5],\n        ['name' => 'Test 5', 'price' => '25', 'square_root' => 5],\n        ['name' => 'Test 6', 'price' => '-25', 'square_root' => 5],\n    ];\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([GH7941Product::class]);\n\n        foreach (self::PRODUCTS as $product) {\n            $this->_em->persist(new GH7941Product($product['name'], $product['price']));\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    #[Test]\n    public function typesShouldBeConvertedForDQLFunctions(): void\n    {\n        $query = $this->_em->createQuery(\n            'SELECT\n                 COUNT(product.id) as count,\n                 SUM(product.price) as sales,\n                 AVG(product.price) as average\n             FROM ' . GH7941Product::class . ' product',\n        );\n\n        $result = $query->getSingleResult();\n\n        self::assertSame(6, $result['count']);\n\n        // While other drivers will return a string, pdo_sqlite returns an integer as of PHP 8.1\n        self::assertEquals(325, $result['sales']);\n\n        // Driver return type and precision is determined by the underlying php extension, most seem to return a string.\n        // pdo_mysql and mysqli both currently return '54.1667' so this is the maximum precision we can assert.\n        // See https://github.com/doctrine/orm/pull/8532#pullrequestreview-610037209\n        self::assertEqualsWithDelta(54.1667, $result['average'], 0.0001);\n\n        $query = $this->_em->createQuery(\n            'SELECT\n                 ABS(product.price) as absolute,\n                 SQRT(ABS(product.price)) as square_root,\n                 LENGTH(product.name) as length\n             FROM ' . GH7941Product::class . ' product',\n        );\n\n        foreach ($query->getResult() as $i => $item) {\n            $product = self::PRODUCTS[$i];\n\n            self::assertEquals(ltrim($product['price'], '-'), $item['absolute']);\n            self::assertSame(strlen($product['name']), $item['length']);\n\n            // Driver return types for the `square_root` column are inconsistent depending on the underlying\n            // database driver. Most return string (though some '10' and some '10.000000000000000') but at least mysqli\n            // returns a float.\n            self::assertEqualsWithDelta($product['square_root'], $item['square_root'], 0.00000001);\n        }\n    }\n}\n\n#[Table]\n#[Entity]\nclass GH7941Product\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DateTimeImmutable */\n    #[Column(type: 'datetime_immutable')]\n    public $createdAt;\n\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public string $name,\n        #[Column(type: 'decimal', precision: 10)]\n        public string $price,\n    ) {\n        $this->createdAt = new DateTimeImmutable();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8055Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH8055')]\nfinal class GH8055Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH8055BaseClass::class,\n            GH8055SubClass::class,\n        ]);\n    }\n\n    public function testNumericDescriminatorColumn(): void\n    {\n        $entity        = new GH8055SubClass();\n        $entity->value = 'test';\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(GH8055SubClass::class);\n        $hydrated   = $repository->find($entity->id);\n\n        self::assertSame('test', $hydrated->value);\n    }\n}\n\n#[Table(name: 'gh8055')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'integer')]\n#[DiscriminatorMap([1 => GH8055BaseClass::class, 2 => GH8055SubClass::class])]\nclass GH8055BaseClass\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n}\n\n#[Entity]\nclass GH8055SubClass extends GH8055BaseClass\n{\n    /** @var string */\n    #[Column(name: 'test', type: 'string', length: 255)]\n    public $value;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8061Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function sprintf;\n\n#[Group('GH8061')]\nfinal class GH8061Test extends OrmTestCase\n{\n    public static function setUpBeforeClass(): void\n    {\n        Type::addType('GH8061Type', GH8061Type::class);\n    }\n\n    public function testConvertToPHPValueSQLForNewObjectExpression(): void\n    {\n        $dql           = 'SELECT NEW ' . GH8061Class::class . '(e.field) FROM ' . GH8061Entity::class . ' e';\n        $entityManager = $this->getTestEntityManager();\n        $query         = $entityManager->createQuery($dql);\n\n        self::assertMatchesRegularExpression('/SELECT DatabaseFunction\\(\\w+\\.field\\) AS /', $query->getSQL());\n    }\n}\n\n#[Entity]\nfinal class GH8061Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var mixed */\n    #[Column(type: 'GH8061Type', length: 255)]\n    public $field;\n}\n\nfinal class GH8061Type extends Type\n{\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getStringTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return 'GH8061';\n    }\n\n    public function convertToPHPValueSQL($sqlExpr, $platform): string\n    {\n        return sprintf('DatabaseFunction(%s)', $sqlExpr);\n    }\n}\n\nfinal class GH8061Class\n{\n    public function __construct(public string $field)\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8127Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass GH8127Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH8127Root::class,\n            GH8127Middle::class,\n            GH8127Leaf::class,\n        );\n    }\n\n    #[DataProvider('queryClasses')]\n    public function testLoadFieldsFromAllClassesInHierarchy(string $queryClass): void\n    {\n        $entity         = new GH8127Leaf();\n        $entity->root   = 'root';\n        $entity->middle = 'middle';\n        $entity->leaf   = 'leaf';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $loadedEntity = $this->_em->find($queryClass, $entity->id);\n\n        self::assertSame('root', $loadedEntity->root);\n        self::assertSame('middle', $loadedEntity->middle);\n        self::assertSame('leaf', $loadedEntity->leaf);\n    }\n\n    public static function queryClasses(): array\n    {\n        return [\n            'query via root entity' => [GH8127Root::class],\n            'query via leaf entity' => [GH8127Leaf::class],\n        ];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'root')]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorMap(['leaf' => GH8127Leaf::class])]\nabstract class GH8127Root\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[ORM\\Column]\n    public $root;\n}\n\n#[ORM\\Entity]\nabstract class GH8127Middle extends GH8127Root\n{\n    /** @var string */\n    #[ORM\\Column]\n    public $middle;\n}\n\n#[ORM\\Entity]\nclass GH8127Leaf extends GH8127Middle\n{\n    /** @var string */\n    #[ORM\\Column]\n    public $leaf;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8217Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH8217Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema(\n            [\n                GH8217Collection::class,\n                GH8217CollectionItem::class,\n            ],\n        );\n    }\n\n    #[Group('GH-8217')]\n    public function testNoQueriesAfterSecondFlush(): void\n    {\n        $collection = new GH8217Collection();\n        $collection->addItem(new GH8217CollectionItem($collection, 0));\n        $collection->addItem(new GH8217CollectionItem($collection, 1));\n        $this->_em->persist($collection);\n        $this->_em->flush();\n\n        $this->getQueryLog()->reset()->enable();\n        $this->_em->flush();\n        $this->assertQueryCount(0);\n    }\n}\n\n#[Entity]\nclass GH8217Collection\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, GH8217CollectionItem> */\n    #[OneToMany(targetEntity: 'GH8217CollectionItem', mappedBy: 'collection', cascade: ['persist', 'remove'], orphanRemoval: true)]\n    public $items;\n\n    public function __construct()\n    {\n        $this->items = new ArrayCollection();\n    }\n\n    public function addItem(GH8217CollectionItem $item): void\n    {\n        $this->items->add($item);\n    }\n}\n\n#[Entity]\nclass GH8217CollectionItem\n{\n    public function __construct(\n        #[Id]\n        #[ManyToOne(targetEntity: 'GH8217Collection', inversedBy: 'items')]\n        #[JoinColumn(name: 'id', referencedColumnName: 'id')]\n        public GH8217Collection $collection,\n        #[Id]\n        #[Column(type: 'integer', options: ['unsigned' => true])]\n        public int $collectionIndex,\n    ) {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8415Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH8415Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            GH8415BaseClass::class,\n            GH8415MiddleMappedSuperclass::class,\n            GH8415LeafClass::class,\n            GH8415AssociationTarget::class,\n        ]);\n    }\n\n    public function testAssociationIsBasedOnBaseClass(): void\n    {\n        $target            = new GH8415AssociationTarget();\n        $leaf              = new GH8415LeafClass();\n        $leaf->baseField   = 'base';\n        $leaf->middleField = 'middle';\n        $leaf->leafField   = 'leaf';\n        $leaf->target      = $target;\n\n        $this->_em->persist($target);\n        $this->_em->persist($leaf);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $query  = $this->_em->createQuery('SELECT leaf FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH8415LeafClass leaf JOIN leaf.target t');\n        $result = $query->getOneOrNullResult();\n\n        $this->assertInstanceOf(GH8415LeafClass::class, $result);\n        $this->assertSame('base', $result->baseField);\n        $this->assertSame('middle', $result->middleField);\n        $this->assertSame('leaf', $result->leafField);\n    }\n}\n\n#[ORM\\Entity]\nclass GH8415AssociationTarget\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('JOINED')]\n#[ORM\\DiscriminatorColumn(name: 'discriminator', type: 'string')]\n#[ORM\\DiscriminatorMap(['1' => GH8415BaseClass::class, '2' => GH8415LeafClass::class])]\nclass GH8415BaseClass\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public $id;\n\n    /** @var GH8415AssociationTarget */\n    #[ORM\\ManyToOne(targetEntity: GH8415AssociationTarget::class)]\n    public $target;\n\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $baseField;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH8415MiddleMappedSuperclass extends GH8415BaseClass\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $middleField;\n}\n\n#[ORM\\Entity]\nclass GH8415LeafClass extends GH8415MiddleMappedSuperclass\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $leafField;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8415ToManyAssociationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass GH8415ToManyAssociationTest extends OrmTestCase\n{\n    public function testToManyAssociationOnBaseClassAllowedWhenThereAreMappedSuperclassesAsChildren(): void\n    {\n        $this->expectNotToPerformAssertions();\n\n        $em = $this->getTestEntityManager();\n        $em->getClassMetadata(GH8415ToManyLeafClass::class);\n    }\n}\n\n#[ORM\\Entity]\nclass GH8415ToManyAssociationTarget\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var GH8415ToManyBaseClass */\n    #[ORM\\ManyToOne(targetEntity: GH8415ToManyBaseClass::class, inversedBy: 'targets')]\n    public $base;\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'discriminator', type: 'string')]\n#[ORM\\DiscriminatorMap(['1' => GH8415ToManyBaseClass::class, '2' => GH8415ToManyLeafClass::class])]\nclass GH8415ToManyBaseClass\n{\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var Collection */\n    #[ORM\\OneToMany(targetEntity: GH8415ToManyAssociationTarget::class, mappedBy: 'base')]\n    public $targets;\n}\n\n#[ORM\\MappedSuperclass]\nclass GH8415ToManyMappedSuperclass extends GH8415ToManyBaseClass\n{\n}\n\n#[ORM\\Entity]\nclass GH8415ToManyLeafClass extends GH8415ToManyMappedSuperclass\n{\n    /** @var string */\n    #[ORM\\Column(type: 'string')]\n    public $leafField;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8443Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\n\nfinal class GH8443Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('company');\n\n        parent::setUp();\n\n        $this->createSchemaForModels(GH8443Foo::class);\n    }\n\n    #[Group('GH-8443')]\n    public function testJoinRootEntityWithForcePartialLoad(): void\n    {\n        $person = new CompanyPerson();\n        $person->setName('John');\n\n        $manager = new CompanyManager();\n        $manager->setName('Adam');\n        $manager->setSalary(1000);\n        $manager->setDepartment('IT');\n        $manager->setTitle('manager');\n\n        $manager->setSpouse($person);\n\n        $this->_em->persist($person);\n        $this->_em->persist($manager);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $manager = $this->_em->createQuery(\n            \"SELECT m from Doctrine\\Tests\\Models\\Company\\CompanyManager m\n               JOIN m.spouse s\n               WITH s.name = 'John'\",\n        )->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)->getSingleResult();\n        $this->_em->refresh($manager);\n\n        $this->assertEquals('John', $manager->getSpouse()->getName());\n    }\n\n    #[Group('GH-8443')]\n    public function testJoinRootEntityWithOnlyOneEntityInHierarchy(): void\n    {\n        $bar = new GH8443Foo('bar');\n\n        $foo = new GH8443Foo('foo');\n        $foo->setBar($bar);\n\n        $this->_em->persist($bar);\n        $this->_em->persist($foo);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $foo = $this->_em->createQuery(\n            'SELECT f from ' . GH8443Foo::class . \" f JOIN f.bar b WITH b.name = 'bar'\",\n        )->getSingleResult();\n        assert($foo instanceof GH8443Foo);\n\n        $bar = $foo->getBar();\n        assert($bar !== null);\n        $this->assertEquals('bar', $bar->getName());\n    }\n}\n#[Table(name: 'GH2947_foo')]\n#[Entity]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['foo' => 'GH8443Foo'])]\nclass GH8443Foo\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int|null $id = null;\n\n    /** @var GH8443Foo|null */\n    #[OneToOne(targetEntity: 'GH8443Foo')]\n    #[JoinColumn(name: 'bar_id', referencedColumnName: 'id')]\n    private $bar;\n\n    public function __construct(\n        #[Column]\n        private string $name,\n    ) {\n    }\n\n    public function getName(): string|null\n    {\n        return $this->name;\n    }\n\n    public function setBar(GH8443Foo $bar): void\n    {\n        if ($bar !== $this->bar) {\n            $this->bar      = $bar;\n            $this->bar->bar = $this;\n        }\n    }\n\n    public function getBar(): GH8443Foo|null\n    {\n        return $this->bar;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8499Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse DateTime;\nuse DateTimeInterface;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function date;\nuse function strtotime;\n\nclass GH8499Test extends OrmFunctionalTestCase\n{\n    /** @var Connection */\n    protected $conn;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->conn = $this->_em->getConnection();\n    }\n\n    protected function createSchema(): void\n    {\n        $this->createSchemaForModels(GH8499VersionableEntity::class);\n    }\n\n    #[Group('GH-8499')]\n    public function testOptimisticTimestampSetsDefaultValue(): GH8499VersionableEntity\n    {\n        $this->createSchema();\n        $entity = new GH8499VersionableEntity();\n        $entity->setName('Test Entity');\n        $entity->setDescription('Entity to test optimistic lock fix with DateTimeInterface objects');\n        self::assertNull($entity->getRevision(), 'Pre-Condition');\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        self::assertInstanceOf(DateTimeInterface::class, $entity->getRevision());\n\n        return $entity;\n    }\n\n    #[Depends('testOptimisticTimestampSetsDefaultValue')]\n    #[Group('GH-8499')]\n    public function testOptimisticLockWithDateTimeForVersion(GH8499VersionableEntity $entity): void\n    {\n        $q = $this->_em->createQuery('SELECT t FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH8499VersionableEntity t WHERE t.id = :id');\n        $q->setParameter('id', $entity->id);\n        $test = $q->getSingleResult();\n\n        $format       = $this->_em->getConnection()->getDatabasePlatform()->getDateTimeFormatString();\n        $modifiedDate = new DateTime(date(\n            $format,\n            strtotime($test->getRevision()->format($format)) - 3600,\n        ));\n\n        $this->conn->executeQuery(\n            'UPDATE GH8499VersionableEntity SET revision = ? WHERE id = ?',\n            [$modifiedDate->format($format), $test->id],\n        );\n\n        $this->_em->refresh($test);\n        $this->_em->lock($test, LockMode::OPTIMISTIC, $modifiedDate);\n\n        $test->setName('Test Entity Locked');\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertEquals(\n            'Test Entity Locked',\n            $test->getName(),\n            'Entity not modified after persist/flush,',\n        );\n        self::assertGreaterThan(\n            $modifiedDate->getTimestamp(),\n            $test->getRevision()->getTimestamp(),\n            'Current version timestamp is not greater than previous one.',\n        );\n    }\n\n    #[Group('GH-8499')]\n    public function testOptimisticLockWithDateTimeForVersionThrowsException(): void\n    {\n        $this->createSchema();\n        $entity = new GH8499VersionableEntity();\n        $entity->setName('Test Entity');\n        $entity->setDescription('Entity to test optimistic lock fix with DateTimeInterface objects');\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $this->expectException(OptimisticLockException::class);\n        $this->_em->lock($entity, LockMode::OPTIMISTIC, new DateTime('2020-07-15 18:04:00'));\n    }\n}\n\n#[Table]\n#[Entity]\nclass GH8499VersionableEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $description;\n\n    /** @var DateTimeInterface */\n    #[Version]\n    #[Column(type: 'datetime')]\n    public $revision;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n\n    public function setDescription(string $description): void\n    {\n        $this->description = $description;\n    }\n\n    public function getRevision(): DateTimeInterface|null\n    {\n        return $this->revision;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8663Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH8663Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH8663VersionedEntity::class);\n    }\n\n    public function testDeletedEntity(): void\n    {\n        $result = $this->_em->find(GH8663VersionedEntity::class, 1, LockMode::OPTIMISTIC);\n\n        self::assertNull($result);\n    }\n}\n\n#[Entity]\nclass GH8663VersionedEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    /** @var int */\n    #[Version]\n    #[Column(type: 'integer')]\n    protected $version;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH8914Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nfinal class GH8914Test extends OrmTestCase\n{\n    #[DoesNotPerformAssertions]\n    #[Group('GH-8914')]\n    public function testDiscriminatorMapWithSeveralLevelsIsSupported(): void\n    {\n        $entityManager = $this->getTestEntityManager();\n        $entityManager->getClassMetadata(GH8914Person::class);\n    }\n}\n\n#[MappedSuperclass]\nabstract class GH8914BaseEntity\n{\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap(['person' => 'GH8914Person', 'employee' => 'GH8914Employee'])]\nclass GH8914Person extends GH8914BaseEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass GH8914Employee extends GH8914Person\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9027Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass GH9027Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH9027Cart::class, GH9027Customer::class);\n    }\n\n    #[Group('GH-9027')]\n    public function testUnitOfWorkHandlesNullRelations(): void\n    {\n        $uow   = new UnitOfWork($this->_em);\n        $hints = ['fetchMode' => [GH9027Cart::class => ['customer' => ClassMetadata::FETCH_EAGER]]];\n\n        $cart = $uow->createEntity(\n            GH9027Cart::class,\n            ['id' => 1, 'customer' => 24252],\n            $hints,\n        );\n\n        $this->assertEquals(null, $cart->customer);\n    }\n}\n\n#[Entity]\nclass GH9027Customer\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH9027Cart */\n    #[OneToOne(targetEntity: 'GH9027Cart', mappedBy: 'customer')]\n    public $cart;\n}\n\n#[Entity]\nclass GH9027Cart\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH9027Customer */\n    #[OneToOne(targetEntity: 'GH9027Customer', inversedBy: 'cart')]\n    #[JoinColumn(name: 'customer', referencedColumnName: 'id')]\n    public $customer;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9109Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-9109')]\nclass GH9109Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH9109User::class, GH9109Product::class);\n    }\n\n    public function testIssue(): void\n    {\n        $userFirstName = 'GH9109Test';\n        $userLastName  = 'UserGH9109';\n        $productTitle  = 'Test product';\n\n        $userRepository = $this->_em->getRepository(GH9109User::class);\n\n        $user = new GH9109User();\n        $user->setFirstName($userFirstName);\n        $user->setLastName($userLastName);\n\n        $product = new GH9109Product();\n        $product->setTitle($productTitle);\n\n        $this->_em->persist($user);\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $product->addBuyer($user);\n\n        $this->_em->persist($product);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $persistedProduct = $this->_em->find(GH9109Product::class, $product->getId());\n\n        // assert Product was persisted\n        self::assertInstanceOf(GH9109Product::class, $persistedProduct);\n        self::assertEquals($productTitle, $persistedProduct->getTitle());\n\n        // assert Product has a Buyer\n        $count = $persistedProduct->getBuyers()->count();\n        self::assertEquals(1, $count);\n\n        // assert NOT QUOTED will WORK with findOneBy\n        $user = $userRepository->findOneBy(['lastName' => $userLastName]);\n        self::assertInstanceOf(GH9109User::class, $user);\n        self::assertEquals($userLastName, $user->getLastName());\n\n        // assert NOT QUOTED will WORK with Criteria\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('lastName', $userLastName));\n        $user = $persistedProduct->getBuyers()->matching($criteria)->first();\n        self::assertInstanceOf(GH9109User::class, $user);\n        self::assertEquals($userLastName, $user->getLastName());\n\n        // assert QUOTED will WORK with findOneBy\n        $user = $userRepository->findOneBy(['firstName' => $userFirstName]);\n        self::assertInstanceOf(GH9109User::class, $user);\n        self::assertEquals($userFirstName, $user->getFirstName());\n\n        // assert QUOTED will WORK with Criteria\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('firstName', $userFirstName));\n        $user = $persistedProduct->getBuyers()->matching($criteria)->first();\n        self::assertInstanceOf(GH9109User::class, $user);\n        self::assertEquals($userFirstName, $user->getFirstName());\n    }\n}\n\n#[Entity]\nclass GH9109Product\n{\n    #[Column(name: '`id`', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(name: '`title`', type: 'string', length: 255)]\n    private string|null $title = null;\n\n    /**\n     * @var Collection|GH9109User[]\n     * @phpstan-var Collection<int, GH9109User>\n     */\n    #[ManyToMany(targetEntity: 'GH9109User')]\n    private $buyers;\n\n    public function __construct()\n    {\n        $this->buyers = new ArrayCollection();\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function setTitle(string $title): void\n    {\n        $this->title = $title;\n    }\n\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    /** @phpstan-return Collection<int, GH9109User> */\n    public function getBuyers(): Collection\n    {\n        return $this->buyers;\n    }\n\n    public function addBuyer(GH9109User $buyer): void\n    {\n        $this->buyers[] = $buyer;\n    }\n}\n\n#[Entity]\nclass GH9109User\n{\n    #[Column(name: '`id`', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(name: '`first_name`', type: 'string', length: 255)]\n    private string|null $firstName = null;\n\n    #[Column(name: 'last_name', type: 'string', length: 255)]\n    private string|null $lastName = null;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getFirstName(): string\n    {\n        return $this->firstName;\n    }\n\n    public function setFirstName(string $firstName): void\n    {\n        $this->firstName = $firstName;\n    }\n\n    public function getLastName(): string\n    {\n        return $this->lastName;\n    }\n\n    public function setLastName(string $lastName): void\n    {\n        $this->lastName = $lastName;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9192Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH9192Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH9192A::class, GH9192B::class, GH9192C::class);\n    }\n\n    public function testIssue(): void\n    {\n        $a = new GH9192A();\n\n        $b    = new GH9192B();\n        $b->a = $a;\n        $a->bs->add($b);\n\n        $c    = new GH9192C();\n        $c->b = $b;\n        $b->cs->add($c);\n\n        $a->c = $c;\n\n        $this->_em->persist($a);\n        $this->_em->persist($b);\n        $this->_em->persist($c);\n        $this->_em->flush();\n\n        $this->expectNotToPerformAssertions();\n\n        $this->_em->remove($a);\n        $this->_em->flush();\n    }\n}\n\n#[ORM\\Entity]\nclass GH9192A\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var Collection<GH9192B> */\n    #[ORM\\OneToMany(mappedBy: 'a', targetEntity: GH9192B::class, cascade: ['remove'])]\n    public $bs;\n\n    /** @var GH9192C */\n    #[ORM\\OneToOne(targetEntity: GH9192C::class)]\n    #[ORM\\JoinColumn(nullable: true, onDelete: 'SET NULL')]\n    public $c;\n\n    public function __construct()\n    {\n        $this->bs = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH9192B\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var Collection<GH9192C> */\n    #[ORM\\OneToMany(mappedBy: 'b', targetEntity: GH9192C::class, cascade: ['remove'])]\n    public $cs;\n\n    /** @var GH9192A */\n    #[ORM\\ManyToOne(inversedBy: 'bs', targetEntity: GH9192A::class)]\n    public $a;\n\n    public function __construct()\n    {\n        $this->cs = new ArrayCollection();\n    }\n}\n\n#[ORM\\Entity]\nclass GH9192C\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var GH9192B */\n    #[ORM\\ManyToOne(inversedBy: 'cs', targetEntity: GH9192B::class)]\n    public $b;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9230Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-9230')]\nclass GH9230Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        // ensure entity table exists\n        $this->setUpEntitySchema([GH9230Entity::class]);\n    }\n\n    protected function tearDown(): void\n    {\n        parent::tearDown();\n\n        $connection = static::$sharedConn;\n        if ($connection === null) {\n            return;\n        }\n\n        // remove persisted entities\n        $connection->executeStatement('DELETE FROM GH9230Entity');\n    }\n\n    /**\n     * This does not work before the fix in PR#9663, but is does work after the fix is applied\n     */\n    public static function failingValuesBeforeFix(): array\n    {\n        return [\n            'string=\"\"' => ['name', '', 'test name'],\n            'string=\"0\"' => ['name', '0', 'test name'],\n            'string=null' => ['name', null, 'test name'],\n\n            'int=0' => ['counter', 0, 1],\n            'int=null' => ['counter', null, 1],\n\n            'bool=false' => ['enabled', false, true],\n            'bool=null' => ['enabled', null, true],\n\n            'float=0.0' => ['price', 0.0, 1.1],\n            'float=-0.0' => ['price', -0.0, 1.1],\n            'float=null' => ['price', null, 1.1],\n\n            'json=[]' => ['extra', [], 1],\n            'json=0' => ['extra', 0, 1],\n            'json=0.0' => ['extra', 0.0, 1],\n            'json=false' => ['extra', false, 1],\n            'json=\"\"' => ['extra', '', 1, ['\"\"', '1']],\n            'json=null' => ['enabled', null, 1],\n        ];\n    }\n\n    /**\n     * This already works before the fix in PR#9663 is applied because none of these are falsy values in php\n     */\n    public static function succeedingValuesBeforeFix(): array\n    {\n        return [\n            'string=\"test\"' => ['name', 'test', 'test2'],\n            'int=1' => ['counter', 1, 1],\n            'bool=true' => ['enabled', true, 1],\n            'json=[null]' => ['extra', [null], 1],\n            'json=1' => ['extra', 1, 1],\n            'json=1.1' => ['extra', 1.1, 1],\n            'json=true' => ['extra', true, 1],\n            'json=\"test\"' => ['extra', 'test', 'test'],\n            'json=\"1\"' => ['extra', '1', 1],\n        ];\n    }\n\n    #[DataProvider('failingValuesBeforeFix')]\n    #[DataProvider('succeedingValuesBeforeFix')]\n    public function testIssue(string $property, $falsyValue, $truthyValue): void\n    {\n        $counter1            = new GH9230Entity();\n        $counter1->$property = $falsyValue;\n\n        $counter2            = new GH9230Entity();\n        $counter2->$property = $truthyValue;\n\n        $this->_em->persist($counter1);\n        $this->_em->persist($counter2);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $persistedCounter1 = $this->_em->find(GH9230Entity::class, $counter1->id);\n        $persistedCounter2 = $this->_em->find(GH9230Entity::class, $counter2->id);\n\n        // Assert entities were persisted\n        self::assertInstanceOf(GH9230Entity::class, $persistedCounter1);\n        self::assertInstanceOf(GH9230Entity::class, $persistedCounter2);\n        self::assertEquals($falsyValue, $persistedCounter1->$property);\n        self::assertEquals($truthyValue, $persistedCounter2->$property);\n\n        $this->_em->clear();\n\n        $counterRepository = $this->_em->getRepository(GH9230Entity::class);\n\n        $query = $counterRepository->createQueryBuilder('counter')\n            ->select('counter.' . $property)\n            ->getQuery();\n\n        $values = $query->getSingleColumnResult();\n\n        // Assert that there are 2 values returned.\n        // This fails when there is a falsy value in the array,\n        // because the first falsy value halts the hydration process (before the fix is applied).\n        self::assertCount(2, $values);\n    }\n}\n\n\n#[Entity]\nclass GH9230Entity\n{\n    /** @var int */\n    #[Column(name: 'id', type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var ?string */\n    #[Column(name: 'name', type: 'string', nullable: true)]\n    public $name;\n\n    /** @var ?int */\n    #[Column(name: 'counter', type: 'integer', nullable: true)]\n    public $counter;\n\n    /** @var ?bool */\n    #[Column(name: 'enabled', type: 'boolean', nullable: true)]\n    public $enabled;\n\n    /** @var ?float */\n    #[Column(name: 'price', type: 'decimal', scale: 1, precision: 2, nullable: true)]\n    public $price;\n\n    /** @var mixed[] */\n    #[Column(name: 'extra', type: 'json', nullable: true)]\n    public $extra;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9335Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\Tests\\Mocks\\CompatibilityType;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH9335')]\nfinal class GH9335Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! DBALType::hasType(GH9335IntObjectType::class)) {\n            DBALType::addType(GH9335IntObjectType::class, GH9335IntObjectType::class);\n        }\n\n        $this->setUpEntitySchema([GH9335Book::class, GH9335Author::class]);\n    }\n\n    /**\n     *  Verifies that entities with foreign keys with custom id object types don't throw an exception\n     *\n     * The test passes when refresh() does not throw an exception\n     */\n    public function testFlattenIdentifierWithObjectId(): void\n    {\n        $author = new GH9335Author('Douglas Adams');\n        $book   = new GH9335Book(new GH9335IntObject(42), 'The Hitchhiker\\'s Guide to the Galaxy', $author);\n\n        $this->_em->persist($author);\n        $this->_em->persist($book);\n        $this->_em->flush();\n\n        $this->_em->refresh($book);\n\n        self::assertInstanceOf(GH9335IntObject::class, $book->getId());\n    }\n}\n\n\nclass GH9335IntObjectType extends Type\n{\n    use CompatibilityType;\n\n    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string\n    {\n        return $platform->getIntegerTypeDeclarationSQL($column);\n    }\n\n    public function getName(): string\n    {\n        return self::class;\n    }\n\n    public function convertToDatabaseValue($value, AbstractPlatform $platform): int\n    {\n        return $value->wrappedInt;\n    }\n\n    public function convertToPHPValue($value, AbstractPlatform $platform): GH9335IntObject\n    {\n        return new GH9335IntObject((int) $value);\n    }\n\n    private function doGetBindingType(): ParameterType|int\n    {\n        return ParameterType::INTEGER;\n    }\n\n    public function requiresSQLCommentHint(AbstractPlatform $platform): bool\n    {\n        return true;\n    }\n}\n\nclass GH9335IntObject\n{\n    /** @var int */\n    public $wrappedInt;\n\n    public function __construct(int $wrappedInt)\n    {\n        $this->wrappedInt = $wrappedInt;\n    }\n\n    public function __toString(): string\n    {\n        return (string) $this->wrappedInt;\n    }\n}\n\n#[Entity]\nclass GH9335Book\n{\n    /** @var GH9335IntObject */\n    #[Id]\n    #[Column(type: GH9335IntObjectType::class, unique: true)]\n    private $id;\n\n    /** @var string */\n    #[Column(type: 'string')]\n    private $title;\n\n    /** @var GH9335Author */\n    #[OneToOne(targetEntity: 'GH9335Author', mappedBy: 'book', cascade: ['persist', 'remove'])]\n    private $author;\n\n    public function __construct(GH9335IntObject $id, string $title, GH9335Author|null $author = null)\n    {\n        $this->setId($id);\n        $this->setTitle($title);\n        $this->setAuthor($author);\n    }\n\n    public function getId(): GH9335IntObject|null\n    {\n        return $this->id;\n    }\n\n    public function setId($id): void\n    {\n        $this->id = $id;\n    }\n\n    public function getTitle(): string|null\n    {\n        return $this->title;\n    }\n\n    public function setTitle($title): void\n    {\n        $this->title = $title;\n    }\n\n    public function getAuthor(): GH9335Author|null\n    {\n        return $this->author;\n    }\n\n    public function setAuthor(GH9335Author|null $author): self\n    {\n        $this->author = $author;\n\n        // set the owning side of the relation\n        if ($author) {\n            $author->setBook($this);\n        }\n\n        return $this;\n    }\n}\n\n#[Entity]\nclass GH9335Author\n{\n    /** @var GH9335Book */\n    #[Id]\n    #[OneToOne(targetEntity: 'GH9335Book', inversedBy: 'author')]\n    #[JoinColumn(name: 'book')]\n    private $book;\n\n    /** @var string */\n    #[Column(type: 'string', nullable: true)]\n    private $name;\n\n    public function __construct(string|null $name)\n    {\n        $this->setName($name);\n    }\n\n    public function getBook(): GH9335Book|null\n    {\n        return $this->book;\n    }\n\n    public function setBook(GH9335Book $book): self\n    {\n        $this->book = $book;\n\n        return $this;\n    }\n\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    public function setName(string $name): void\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/GH9467Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\n\nclass GH9467Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            JoinedInheritanceRoot::class,\n            JoinedInheritanceChild::class,\n            JoinedInheritanceWritableColumn::class,\n            JoinedInheritanceNonWritableColumn::class,\n            JoinedInheritanceNonInsertableColumn::class,\n            JoinedInheritanceNonUpdatableColumn::class,\n        );\n    }\n\n    public function testRootColumnsInsert(): int\n    {\n        $entity                           = new JoinedInheritanceChild();\n        $entity->rootWritableContent      = 'foo';\n        $entity->rootNonWritableContent   = 'foo';\n        $entity->rootNonInsertableContent = 'foo';\n        $entity->rootNonUpdatableContent  = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check INSERT query cause set database values into non-insertable entity properties\n        self::assertEquals('foo', $entity->rootWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonInsertableContent);\n        self::assertEquals('foo', $entity->rootNonUpdatableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceChild::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceChild::class, $entity);\n        self::assertEquals('foo', $entity->rootWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonInsertableContent);\n        self::assertEquals('foo', $entity->rootNonUpdatableContent);\n\n        return $entity->id;\n    }\n\n    #[Depends('testRootColumnsInsert')]\n    public function testRootColumnsUpdate(int $entityId): void\n    {\n        $entity = $this->_em->find(JoinedInheritanceChild::class, $entityId);\n        self::assertInstanceOf(JoinedInheritanceChild::class, $entity);\n\n        // update exist entity\n        $entity->rootWritableContent      = 'bar';\n        $entity->rootNonInsertableContent = 'bar';\n        $entity->rootNonWritableContent   = 'bar';\n        $entity->rootNonUpdatableContent  = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check UPDATE query cause set database values into non-insertable entity properties\n        self::assertEquals('bar', $entity->rootWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonWritableContent);\n        self::assertEquals('bar', $entity->rootNonInsertableContent);\n        self::assertEquals('foo', $entity->rootNonUpdatableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceChild::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceChild::class, $entity);\n        self::assertEquals('bar', $entity->rootWritableContent);\n        self::assertEquals('dbDefault', $entity->rootNonWritableContent);\n        self::assertEquals('bar', $entity->rootNonInsertableContent);\n        self::assertEquals('foo', $entity->rootNonUpdatableContent);\n    }\n\n    public function testChildWritableColumnInsert(): int\n    {\n        $entity                  = new JoinedInheritanceWritableColumn();\n        $entity->writableContent = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check INSERT query doesn't change insertable entity property\n        self::assertEquals('foo', $entity->writableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceWritableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceWritableColumn::class, $entity);\n        self::assertEquals('foo', $entity->writableContent);\n\n        return $entity->id;\n    }\n\n    #[Depends('testChildWritableColumnInsert')]\n    public function testChildWritableColumnUpdate(int $entityId): void\n    {\n        $entity = $this->_em->find(JoinedInheritanceWritableColumn::class, $entityId);\n        self::assertInstanceOf(JoinedInheritanceWritableColumn::class, $entity);\n\n        // update exist entity\n        $entity->writableContent = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check UPDATE query doesn't change updatable entity property\n        self::assertEquals('bar', $entity->writableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceWritableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceWritableColumn::class, $entity);\n        self::assertEquals('bar', $entity->writableContent);\n    }\n\n    public function testChildNonWritableColumnInsert(): int\n    {\n        $entity                     = new JoinedInheritanceNonWritableColumn();\n        $entity->nonWritableContent = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check INSERT query cause set database value into non-insertable entity property\n        self::assertEquals('dbDefault', $entity->nonWritableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonWritableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonWritableColumn::class, $entity);\n        self::assertEquals('dbDefault', $entity->nonWritableContent);\n\n        return $entity->id;\n    }\n\n    #[Depends('testChildNonWritableColumnInsert')]\n    public function testChildNonWritableColumnUpdate(int $entityId): void\n    {\n        $entity = $this->_em->find(JoinedInheritanceNonWritableColumn::class, $entityId);\n        self::assertInstanceOf(JoinedInheritanceNonWritableColumn::class, $entity);\n\n        // update exist entity\n        $entity->nonWritableContent = 'bar';\n        // change some property to ensure UPDATE query will be done\n        self::assertNotEquals('bar', $entity->rootField);\n        $entity->rootField = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check UPDATE query cause set database value into non-updatable entity property\n        self::assertEquals('dbDefault', $entity->nonWritableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonWritableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonWritableColumn::class, $entity);\n        self::assertEquals('bar', $entity->rootField); // check that UPDATE query done\n        self::assertEquals('dbDefault', $entity->nonWritableContent);\n    }\n\n    public function testChildNonInsertableColumnInsert(): int\n    {\n        $entity                       = new JoinedInheritanceNonInsertableColumn();\n        $entity->nonInsertableContent = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check INSERT query cause set database value into non-insertable entity property\n        self::assertEquals('dbDefault', $entity->nonInsertableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonInsertableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonInsertableColumn::class, $entity);\n        self::assertEquals('dbDefault', $entity->nonInsertableContent);\n\n        return $entity->id;\n    }\n\n    #[Depends('testChildNonInsertableColumnInsert')]\n    public function testChildNonInsertableColumnUpdate(int $entityId): void\n    {\n        $entity = $this->_em->find(JoinedInheritanceNonInsertableColumn::class, $entityId);\n        self::assertInstanceOf(JoinedInheritanceNonInsertableColumn::class, $entity);\n\n        // update exist entity\n        $entity->nonInsertableContent = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check UPDATE query doesn't change updatable entity property\n        self::assertEquals('bar', $entity->nonInsertableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonInsertableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonInsertableColumn::class, $entity);\n        self::assertEquals('bar', $entity->nonInsertableContent);\n    }\n\n    public function testChildNonUpdatableColumnInsert(): int\n    {\n        $entity                      = new JoinedInheritanceNonUpdatableColumn();\n        $entity->nonUpdatableContent = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check INSERT query doesn't change insertable entity property\n        self::assertEquals('foo', $entity->nonUpdatableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonUpdatableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonUpdatableColumn::class, $entity);\n        self::assertEquals('foo', $entity->nonUpdatableContent);\n\n        return $entity->id;\n    }\n\n    #[Depends('testChildNonUpdatableColumnInsert')]\n    public function testChildNonUpdatableColumnUpdate(int $entityId): void\n    {\n        $entity = $this->_em->find(JoinedInheritanceNonUpdatableColumn::class, $entityId);\n        self::assertInstanceOf(JoinedInheritanceNonUpdatableColumn::class, $entity);\n        self::assertEquals('foo', $entity->nonUpdatableContent);\n\n        // update exist entity\n        $entity->nonUpdatableContent = 'bar';\n        // change some property to ensure UPDATE query will be done\n        self::assertNotEquals('bar', $entity->rootField);\n        $entity->rootField = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        // check UPDATE query cause set database value into non-updatable entity property\n        self::assertEquals('foo', $entity->nonUpdatableContent);\n\n        // check other process get same state\n        $this->_em->clear();\n        $entity = $this->_em->find(JoinedInheritanceNonUpdatableColumn::class, $entity->id);\n        self::assertInstanceOf(JoinedInheritanceNonUpdatableColumn::class, $entity);\n        self::assertEquals('bar', $entity->rootField); // check that UPDATE query done\n        self::assertEquals('foo', $entity->nonUpdatableContent);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceChild.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_child')]\nclass JoinedInheritanceChild extends JoinedInheritanceRoot\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonInsertableColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_non_insertable_column')]\nclass JoinedInheritanceNonInsertableColumn extends JoinedInheritanceRoot\n{\n    /** @var string */\n    #[Column(type: 'string', insertable: false, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $nonInsertableContent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonUpdatableColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_non_updatable_column')]\nclass JoinedInheritanceNonUpdatableColumn extends JoinedInheritanceRoot\n{\n    /** @var string */\n    #[Column(type: 'string', insertable: true, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $nonUpdatableContent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonWritableColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_non_writable_column')]\nclass JoinedInheritanceNonWritableColumn extends JoinedInheritanceRoot\n{\n    /** @var string */\n    #[Column(type: 'string', insertable: false, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $nonWritableContent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceRoot.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_root')]\n#[InheritanceType('JOINED')]\n#[DiscriminatorColumn(name: 'discr', type: 'string')]\n#[DiscriminatorMap([\n    'child' => JoinedInheritanceChild::class,\n    'writable' => JoinedInheritanceWritableColumn::class,\n    'nonWritable' => JoinedInheritanceNonWritableColumn::class,\n    'nonInsertable' => JoinedInheritanceNonInsertableColumn::class,\n    'nonUpdatable' => JoinedInheritanceNonUpdatableColumn::class,\n])]\nclass JoinedInheritanceRoot\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string')]\n    public $rootField = '';\n\n    /** @var string */\n    #[Column(type: 'string', insertable: true, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $rootWritableContent = '';\n\n    /** @var string */\n    #[Column(type: 'string', insertable: false, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $rootNonWritableContent;\n\n    /** @var string */\n    #[Column(type: 'string', insertable: false, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $rootNonInsertableContent;\n\n    /** @var string */\n    #[Column(type: 'string', insertable: true, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $rootNonUpdatableContent = '';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceWritableColumn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9467;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Entity]\n#[Table(name: 'joined_inheritance_writable_column')]\nclass JoinedInheritanceWritableColumn extends JoinedInheritanceRoot\n{\n    /** @var string */\n    #[Column(type: 'string', insertable: true, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')]\n    public $writableContent;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9516Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass GH9516Test extends OrmFunctionalTestCase\n{\n    public function testEntityCanHaveInverseOneToManyAssociationWithChildMappedSuperclass(): void\n    {\n        $sportsCarMetadata = $this->_em->getClassMetadata(GH9516SportsCar::class);\n        $this->assertTrue($sportsCarMetadata->hasAssociation('passengers'));\n    }\n}\n\n#[Entity]\nclass GH9516Passenger\n{\n    /** @var int $id */\n    #[Id]\n    #[Column(type: 'integer')]\n    private $id;\n\n    /** @var GH9516Vehicle $vehicle */\n    #[ManyToOne(targetEntity: GH9516Vehicle::class, inversedBy: 'passengers')]\n    private $vehicle;\n}\n\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\n#[ORM\\DiscriminatorMap(['sports' => GH9516SportsCar::class])]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[Entity]\nabstract class GH9516Vehicle\n{\n    /** @var int $id */\n    #[Id]\n    #[Column(type: 'integer')]\n    private $id;\n\n    /** @var GH9516Passenger[] $passengers */\n    #[OneToMany(targetEntity: GH9516Passenger::class, mappedBy: 'vehicle')]\n    private $passengers;\n}\n\n#[MappedSuperclass]\nabstract class GH9516Car extends GH9516Vehicle\n{\n}\n\n#[Entity]\nclass GH9516SportsCar extends GH9516Car\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9579Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-9579')]\nclass GH9579Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            GH9579Container::class,\n            GH9579Item::class,\n            GH9579Part::class,\n        );\n\n        $container = new GH9579Container();\n\n        $item            = new GH9579Item();\n        $item->container = $container;\n\n        $container->currentItem = $item;\n\n        $part       = new GH9579Part();\n        $part->item = $item;\n\n        $this->_em->persist($item);\n        $this->_em->persist($container);\n        $this->_em->persist($part);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    #[Group('GH-9579')]\n    public function testIssue(): void\n    {\n        $dql        = <<<'DQL'\nSELECT container, currentItem, currentItemPart, item, itemPart\nFROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\GH9579Container container\nLEFT JOIN container.currentItem currentItem\nLEFT JOIN currentItem.parts currentItemPart\nLEFT JOIN container.items item \nLEFT JOIN item.parts itemPart\nDQL;\n        $containers = $this->_em->createQuery($dql)->execute();\n        self::assertCount(1, $containers);\n        self::assertCount(1, $containers[0]->items);\n        self::assertCount(1, $containers[0]->items[0]->parts);\n    }\n}\n\n#[Table(name: 'GH9579_containers')]\n#[Entity]\nclass GH9579Container\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, GH9579Item> */\n    #[OneToMany(targetEntity: 'GH9579Item', mappedBy: 'container')]\n    public $items;\n\n    public function __construct()\n    {\n        $this->items = new ArrayCollection();\n    }\n\n    /** @var GH9579Item */\n    #[OneToOne(targetEntity: 'GH9579Item')]\n    #[JoinColumn(name: 'item_id', referencedColumnName: 'id')]\n    public $currentItem;\n}\n\n#[Table(name: 'GH9579_items')]\n#[Entity]\nclass GH9579Item\n{\n    public function __construct()\n    {\n        $this->parts = new ArrayCollection();\n    }\n\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Collection<int, GH9579Part> */\n    #[OneToMany(targetEntity: 'GH9579Part', mappedBy: 'item')]\n    public $parts;\n\n    /** @var GH9579Container */\n    #[ManyToOne(targetEntity: 'GH9579Container', inversedBy: 'items')]\n    #[JoinColumn(name: 'container_id', referencedColumnName: 'id')]\n    public $container;\n}\n\n#[Table(name: 'GH9579_parts')]\n#[Entity]\nclass GH9579Part\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var GH9579Item */\n    #[ManyToOne(targetEntity: 'GH9579Item', inversedBy: 'parts')]\n    #[JoinColumn(name: 'item_id', referencedColumnName: 'id')]\n    public $item;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/GH9807Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class GH9807Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(GH9807Main::class, GH9807Join::class);\n    }\n\n    public function testHydrateJoinedCollectionWithFirstNullishRow(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(GH9807Main::class, 'm');\n        $rsm->addJoinedEntityResult(GH9807Join::class, 'j', 'm', 'joins');\n\n        $rsm->addFieldResult('m', 'id_0', 'id');\n        $rsm->addFieldResult('j', 'id_1', 'id');\n        $rsm->addFieldResult('j', 'value_2', 'value');\n\n        $hydrator = new ObjectHydrator($this->_em);\n\n        $uow = $this->_em->getUnitOfWork();\n\n        $uow->createEntity(\n            GH9807Main::class,\n            ['id' => 1],\n        );\n\n        $resultSet = [\n            [\n                'id_0' => 1,\n                'id_1' => null,\n                'value_2' => null,\n            ],\n            [\n                'id_0' => 1,\n                'id_1' => 1,\n                'value_2' => '2',\n            ],\n            [\n                'id_0' => 1,\n                'id_1' => 2,\n                'value_2' => '2',\n            ],\n        ];\n\n        $stmt = ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class));\n\n        /** @var GH9807Main[] $result */\n        $result = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertInstanceOf(GH9807Main::class, $result[0]);\n        self::assertCount(2, $result[0]->getJoins());\n    }\n}\n\n#[Entity]\nclass GH9807Main\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private $id;\n\n    /** @var Collection<int, GH9807Join> */\n    #[ORM\\ManyToMany(targetEntity: 'GH9807Join', inversedBy: 'starts')]\n    private $joins;\n\n    /** @return Collection<int, GH9807Join> */\n    public function getJoins(): Collection\n    {\n        return $this->joins;\n    }\n}\n\n#[Entity]\nclass GH9807Join\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue]\n    private $id;\n\n    /** @var Collection<int, GH9807Main> */\n    #[ORM\\ManyToMany(targetEntity: 'GH9807Main', mappedBy: 'bases')]\n    private $mains;\n\n    /** @var string */\n    #[ORM\\Column(type: 'string', nullable: false)]\n    private $value;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Issue5989Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Employee;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Manager;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Person;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('issue-5989')]\nclass Issue5989Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('issue5989');\n\n        parent::setUp();\n    }\n\n    public function testSimpleArrayTypeHydratedCorrectlyInJoinedInheritance(): void\n    {\n        $manager = new Issue5989Manager();\n\n        $managerTags   = ['tag1', 'tag2'];\n        $manager->tags = $managerTags;\n        $this->_em->persist($manager);\n\n        $employee = new Issue5989Employee();\n\n        $employeeTags   = ['tag2', 'tag3'];\n        $employee->tags = $employeeTags;\n        $this->_em->persist($employee);\n\n        $this->_em->flush();\n\n        $managerId  = $manager->id;\n        $employeeId = $employee->id;\n\n        // clear entity manager so that $repository->find actually fetches them and uses the hydrator\n        // instead of just returning the existing managed entities\n        $this->_em->clear();\n\n        $repository = $this->_em->getRepository(Issue5989Person::class);\n\n        $manager  = $repository->find($managerId);\n        $employee = $repository->find($employeeId);\n\n        self::assertEquals($managerTags, $manager->tags);\n        self::assertEquals($employeeTags, $employee->tags);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Issue9300Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Tests\\Models\\Issue9300\\Issue9300Child;\nuse Doctrine\\Tests\\Models\\Issue9300\\Issue9300Parent;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('GH-9300')]\nclass Issue9300Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('issue9300');\n\n        parent::setUp();\n    }\n\n    public function testPersistedCollectionIsPresentInOriginalDataAfterFlush(): void\n    {\n        $parent = new Issue9300Parent();\n        $child  = new Issue9300Child();\n        $child->parents->add($parent);\n\n        $parent->name = 'abc';\n        $child->name  = 'abc';\n\n        $this->_em->persist($parent);\n        $this->_em->persist($child);\n        $this->_em->flush();\n\n        $parent->name = 'abcd';\n        $child->name  = 'abcd';\n\n        $this->_em->flush();\n\n        self::assertArrayHasKey('parents', $this->_em->getUnitOfWork()->getOriginalEntityData($child));\n    }\n\n    public function testPersistingCollectionAfterFlushWorksAsExpected(): void\n    {\n        $parentOne = new Issue9300Parent();\n        $parentTwo = new Issue9300Parent();\n        $childOne  = new Issue9300Child();\n\n        $parentOne->name   = 'abc';\n        $parentTwo->name   = 'abc';\n        $childOne->name    = 'abc';\n        $childOne->parents = new ArrayCollection([$parentOne]);\n\n        $this->_em->persist($parentOne);\n        $this->_em->persist($parentTwo);\n        $this->_em->persist($childOne);\n        $this->_em->flush();\n\n        // Recalculate change-set -> new original data\n        $childOne->name = 'abcd';\n        $this->_em->flush();\n\n        $childOne->parents = new ArrayCollection([$parentTwo]);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $childOneFresh = $this->_em->find(Issue9300Child::class, $childOne->id);\n        self::assertCount(1, $childOneFresh->parents);\n        self::assertEquals($parentTwo->id, $childOneFresh->parents[0]->id);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/AbstractTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nuse function sprintf;\nuse function str_replace;\n\nabstract class AbstractTestCase extends OrmFunctionalTestCase\n{\n    protected function generateMessage(string $message): string\n    {\n        $log = $this->getLastLoggedQuery();\n\n        return sprintf(\"%s\\nSQL: %s\", $message, str_replace(['?'], (array) $log['params'], $log['sql']));\n    }\n\n    protected function clearCachedData(object ...$entities): void\n    {\n        foreach ($entities as $entity) {\n            $this->_em->refresh($entity);\n        }\n    }\n\n    protected function persistFlushClear(object ...$entities): void\n    {\n        foreach ($entities as $entity) {\n            $this->_em->persist($entity);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/ChangeFiltersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter;\n\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Order;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\User;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\SQLFilter\\CompanySQLFilter;\n\nfinal class ChangeFiltersTest extends AbstractTestCase\n{\n    private const COMPANY_A = 'A';\n    private const COMPANY_B = 'B';\n\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            Order::class,\n            User::class,\n        ]);\n    }\n\n    /** @return non-empty-array<\"companyA\"|\"companyB\", array{orderId: int, userId: int}> */\n    private function prepareData(): array\n    {\n        $user1  = new User(self::COMPANY_A);\n        $order1 = new Order($user1);\n        $user2  = new User(self::COMPANY_B);\n        $order2 = new Order($user2);\n\n        $this->_em->persist($user1);\n        $this->_em->persist($order1);\n        $this->_em->persist($user2);\n        $this->_em->persist($order2);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        return [\n            'companyA' => ['orderId' => $order1->id, 'userId' => $user1->id],\n            'companyB' => ['orderId' => $order2->id, 'userId' => $user2->id],\n        ];\n    }\n\n    public function testUseEnableDisableFilter(): void\n    {\n        $this->_em->getConfiguration()->addFilter(CompanySQLFilter::class, CompanySQLFilter::class);\n        $this->_em->getFilters()->enable(CompanySQLFilter::class)->setParameter('company', self::COMPANY_A);\n\n        ['companyA' => $companyA, 'companyB' => $companyB] = $this->prepareData();\n\n        $order1 = $this->_em->find(Order::class, $companyA['orderId']);\n\n        self::assertNotNull($order1->user, $this->generateMessage('Order1->User1 not found'));\n        self::assertEquals($companyA['userId'], $order1->user->id, $this->generateMessage('Order1->User1 != User1'));\n\n        $this->_em->getFilters()->disable(CompanySQLFilter::class);\n        $this->_em->getFilters()->enable(CompanySQLFilter::class)->setParameter('company', self::COMPANY_B);\n\n        $order2 = $this->_em->find(Order::class, $companyB['orderId']);\n\n        self::assertNotNull($order2->user, $this->generateMessage('Order2->User2 not found'));\n        self::assertEquals($companyB['userId'], $order2->user->id, $this->generateMessage('Order2->User2 != User2'));\n    }\n\n    public function testUseChangeFilterParameters(): void\n    {\n        $this->_em->getConfiguration()->addFilter(CompanySQLFilter::class, CompanySQLFilter::class);\n        $filter = $this->_em->getFilters()->enable(CompanySQLFilter::class);\n\n        ['companyA' => $companyA, 'companyB' => $companyB] = $this->prepareData();\n\n        $filter->setParameter('company', self::COMPANY_A);\n\n        $order1 = $this->_em->find(Order::class, $companyA['orderId']);\n\n        self::assertNotNull($order1->user, $this->generateMessage('Order1->User1 not found'));\n        self::assertEquals($companyA['userId'], $order1->user->id, $this->generateMessage('Order1->User1 != User1'));\n\n        $filter->setParameter('company', self::COMPANY_B);\n\n        $order2 = $this->_em->find(Order::class, $companyB['orderId']);\n\n        self::assertNotNull($order2->user, $this->generateMessage('Order2->User2 not found'));\n        self::assertEquals($companyB['userId'], $order2->user->id, $this->generateMessage('Order2->User2 != User2'));\n    }\n\n    public function testUseQueryBuilder(): void\n    {\n        $this->_em->getConfiguration()->addFilter(CompanySQLFilter::class, CompanySQLFilter::class);\n        $filter = $this->_em->getFilters()->enable(CompanySQLFilter::class);\n\n        ['companyA' => $companyA, 'companyB' => $companyB] = $this->prepareData();\n\n        $getOrderByIdCache = function (int $orderId): Order|null {\n            return $this->_em->createQueryBuilder()\n                ->select('orderMaster, user')\n                ->from(Order::class, 'orderMaster')\n                ->innerJoin('orderMaster.user', 'user')\n                ->where('orderMaster.id = :orderId')\n                ->setParameter('orderId', $orderId)\n                ->setCacheable(true)\n                ->getQuery()\n                ->setQueryCacheLifetime(10)\n                ->getOneOrNullResult();\n        };\n\n        $filter->setParameter('company', self::COMPANY_A);\n\n        $order = $getOrderByIdCache($companyB['orderId']);\n        self::assertNull($order);\n\n        $order = $getOrderByIdCache($companyA['orderId']);\n\n        self::assertInstanceOf(Order::class, $order);\n        self::assertInstanceOf(User::class, $order->user);\n        self::assertEquals($companyA['userId'], $order->user->id);\n\n        $filter->setParameter('company', self::COMPANY_B);\n\n        $order = $getOrderByIdCache($companyA['orderId']);\n        self::assertNull($order);\n\n        $order = $getOrderByIdCache($companyB['orderId']);\n\n        self::assertInstanceOf(Order::class, $order);\n        self::assertInstanceOf(User::class, $order->user);\n        self::assertEquals($companyB['userId'], $order->user->id);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/Insurance.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass Insurance\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    #[ORM\\ManyToOne(targetEntity: Practice::class)]\n    public Practice $practice;\n\n    public function __construct(Practice $practice, string $name)\n    {\n        $this->practice = $practice;\n        $this->name     = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/Order.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'Order_Master')]\nclass Order\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $company;\n\n    #[ORM\\ManyToOne(targetEntity: User::class, fetch: 'EAGER')]\n    public User $user;\n\n    public function __construct(User $user)\n    {\n        $this->user    = $user;\n        $this->company = $user->company;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/Patient.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass Patient\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    /** @var Collection<int, PatientInsurance> */\n    #[ORM\\OneToMany(targetEntity: PatientInsurance::class, mappedBy: 'patient', fetch: 'LAZY', cascade: ['persist'])]\n    public Collection $insurances;\n\n    public function __construct(string $name)\n    {\n        $this->name       = $name;\n        $this->insurances = new ArrayCollection();\n    }\n\n    /** @return Collection<PrimaryPatInsurance> */\n    public function getPrimaryInsurances(): Collection\n    {\n        return $this->insurances->filter(static function (PatientInsurance $insurances) {\n            return $insurances instanceof PrimaryPatInsurance;\n        });\n    }\n\n    /** @return Collection<SecondaryPatInsurance> */\n    public function getSecondaryInsurances(): Collection\n    {\n        return $this->insurances->filter(static function (PatientInsurance $insurances) {\n            return $insurances instanceof SecondaryPatInsurance;\n        });\n    }\n\n    public function addPrimaryInsurance(Insurance $insurance): void\n    {\n        $this->insurances[] = new PrimaryPatInsurance($this, $insurance);\n    }\n\n    public function addSecondaryInsurance(Insurance $insurance): void\n    {\n        $this->insurances[] = new SecondaryPatInsurance($this, $insurance);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/PatientInsurance.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap(['primary' => PrimaryPatInsurance::class, 'secondary' => SecondaryPatInsurance::class])]\n#[ORM\\DiscriminatorColumn(name: 'type', type: 'string')]\nabstract class PatientInsurance\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\ManyToOne(targetEntity: Insurance::class, fetch: 'EAGER', cascade: ['persist'])]\n    #[ORM\\JoinColumn(referencedColumnName: 'id', nullable: false)]\n    public Insurance $insurance;\n\n    #[ORM\\ManyToOne(targetEntity: Patient::class, inversedBy: 'insurances')]\n    public Patient $patient;\n\n    public function __construct(Patient $patient, Insurance $insurance)\n    {\n        $this->patient   = $patient;\n        $this->insurance = $insurance;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/Practice.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass Practice\n{\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue]\n    #[ORM\\Column(type: 'integer')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    public function __construct(string $name)\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/PrimaryPatInsurance.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass PrimaryPatInsurance extends PatientInsurance\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/SecondaryPatInsurance.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass SecondaryPatInsurance extends PatientInsurance\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Entity/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'User_Master')]\nclass User\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $company;\n\n    public function __construct(string $company)\n    {\n        $this->company = $company;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/SQLFilter/CompanySQLFilter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\SQLFilter;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Order;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\User;\n\nuse function sprintf;\n\nclass CompanySQLFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string\n    {\n        if ($targetEntity->getName() === User::class) {\n            return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']->fieldName, $this->getParameter('company'));\n        }\n\n        if ($targetEntity->getName() === Order::class) {\n            return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']->fieldName, $this->getParameter('company'));\n        }\n\n        return '';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/SQLFilter/PracticeContextSQLFilter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\SQLFilter;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Insurance;\n\nuse function sprintf;\n\nclass PracticeContextSQLFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string\n    {\n        if (! $this->hasParameter('practiceId') || $this->getParameter('practiceId') === null) {\n            return '';\n        }\n\n        if ($targetEntity->getName() === Insurance::class) {\n            return sprintf(\n                '%s.%s = %s',\n                $targetTableAlias,\n                $targetEntity->associationMappings['practice']->joinColumns[0]->name,\n                $this->getParameter('practiceId'),\n            );\n        }\n\n        return '';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/SwitchContextTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter;\n\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Insurance;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Patient;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\PatientInsurance;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\Practice;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\PrimaryPatInsurance;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\Entity\\SecondaryPatInsurance;\nuse Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilter\\SQLFilter\\PracticeContextSQLFilter;\n\nfinal class SwitchContextTest extends AbstractTestCase\n{\n    /** @var SQLFilter|PracticeContextSQLFilter */\n    private $sqlFilter;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            Practice::class,\n            Patient::class,\n            PatientInsurance::class,\n            PrimaryPatInsurance::class,\n            SecondaryPatInsurance::class,\n            Insurance::class,\n        );\n\n        $this->_em->getConfiguration()->addFilter(PracticeContextSQLFilter::class, PracticeContextSQLFilter::class);\n        $this->sqlFilter = $this->_em->getFilters()->enable(PracticeContextSQLFilter::class);\n    }\n\n    /** @return array{Patient, Patient} */\n    private function fixtureGenerate(): array\n    {\n        $practiceA        = new Practice('Practice A');\n        $practiceB        = new Practice('Practice B');\n        $insuranceAetna   = new Insurance($practiceA, 'Aetna in Practice A');\n        $insuranceBHumana = new Insurance($practiceB, 'Humana in Practice B');\n        $insuranceBCustom = new Insurance($practiceB, 'Custom in Practice B');\n\n        $patientEgor = new Patient('Egor');\n        $patientEgor->addPrimaryInsurance($insuranceAetna);\n        $patientEgor->addPrimaryInsurance($insuranceBHumana);\n\n        $patientGena = new Patient('Gena');\n        $patientGena->addPrimaryInsurance($insuranceBHumana);\n        $patientGena->addSecondaryInsurance($insuranceBCustom);\n\n        $this->persistFlushClear(\n            $practiceA,\n            $practiceB,\n            $insuranceAetna,\n            $insuranceBHumana,\n            $insuranceBCustom,\n            $patientEgor,\n            $patientGena,\n        );\n\n        return [\n            $this->_em->getReference(Patient::class, $patientEgor->id),\n            $this->_em->getReference(Patient::class, $patientGena->id),\n        ];\n    }\n\n    /**\n     * @param callable(): T $callback\n     *\n     * @return T\n     *\n     * @template T\n     */\n    private function switchPracticeContext(Practice $practice, callable $callback)\n    {\n        $this->sqlFilter->setParameter('practiceId', $practice->id);\n\n        try {\n            return $callback();\n        } finally {\n            $this->sqlFilter->setParameter('practiceId', null);\n        }\n    }\n\n    public function testSwitchContext(): void\n    {\n        [$patientEgor, $patentGena] = $this->fixtureGenerate();\n\n        $practiceA = $this->_em->getRepository(Practice::class)->findOneBy(['name' => 'Practice A']);\n        $practiceB = $this->_em->getRepository(Practice::class)->findOneBy(['name' => 'Practice B']);\n\n        $this->switchPracticeContext($practiceA, function () use ($patientEgor, $patentGena): void {\n            $this->clearCachedData($patentGena, $patientEgor);\n\n            self::assertCount(1, $patientEgor->insurances);\n            self::assertInstanceOf(PrimaryPatInsurance::class, $patientEgor->getPrimaryInsurances()->first());\n            self::assertEquals('Aetna in Practice A', $patientEgor->getPrimaryInsurances()->first()->insurance->name);\n\n            self::assertCount(0, $patentGena->insurances);\n        });\n\n        $this->switchPracticeContext($practiceB, function () use ($patientEgor, $patentGena): void {\n            $this->clearCachedData($patentGena, $patientEgor);\n\n            self::assertCount(1, $patientEgor->insurances);\n            self::assertInstanceOf(PrimaryPatInsurance::class, $patientEgor->getPrimaryInsurances()->first());\n            self::assertEquals('Humana in Practice B', $patientEgor->getPrimaryInsurances()->first()->insurance->name);\n\n            self::assertCount(2, $patentGena->insurances);\n            self::assertInstanceOf(PrimaryPatInsurance::class, $patentGena->getPrimaryInsurances()->first());\n            self::assertInstanceOf(SecondaryPatInsurance::class, $patentGena->getSecondaryInsurances()->first());\n            self::assertEquals('Humana in Practice B', $patentGena->getPrimaryInsurances()->first()->insurance->name);\n            self::assertEquals('Custom in Practice B', $patentGena->getSecondaryInsurances()->first()->insurance->name);\n        });\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Category.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilterAndIndexedRelation;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'Category_Master')]\nclass Category\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    #[ORM\\Column(type: 'string')]\n    public string $type;\n\n    public function __construct(string $name, string $type)\n    {\n        $this->name = $name;\n        $this->type = $type;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/CategoryTypeSQLFilter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilterAndIndexedRelation;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\n\nuse function sprintf;\n\nclass CategoryTypeSQLFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string\n    {\n        if ($targetEntity->getName() === Category::class) {\n            return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['type']->fieldName, $this->getParameter('type'));\n        }\n\n        return '';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/ChangeFiltersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilterAndIndexedRelation;\n\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nfinal class ChangeFiltersTest extends OrmFunctionalTestCase\n{\n    private const COMPANY_A = 'A';\n    private const CAT_BAR   = 'bar';\n    private const CAT_FOO   = 'foo';\n\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->setUpEntitySchema([\n            Company::class,\n            Category::class,\n        ]);\n    }\n\n    private function prepareData(): void\n    {\n        $cat1     = new Category('cat1', self::CAT_FOO);\n        $cat2     = new Category('cat2', self::CAT_BAR);\n        $companyA = new Company(self::COMPANY_A, [$cat1, $cat2]);\n\n        $this->_em->persist($cat1);\n        $this->_em->persist($cat2);\n        $this->_em->persist($companyA);\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public function testIndexAliasUpdatedWithUpdatedFilter(): void\n    {\n        $this->prepareData();\n\n        $company = $this->_em->getRepository(Company::class)->findOneBy([]);\n\n        self::assertCount(2, $company->categories);\n        self::assertEquals([self::CAT_FOO, self::CAT_BAR], $company->categories->map(static function (Category $c): string {\n            return $c->type;\n        })->getValues());\n\n        $this->_em->clear();\n        $this->_em->getConfiguration()->addFilter(CategoryTypeSQLFilter::class, CategoryTypeSQLFilter::class);\n        $this->_em->getFilters()->enable(CategoryTypeSQLFilter::class)->setParameter('type', self::CAT_FOO);\n\n        $company = $this->_em->getRepository(Company::class)->findOneBy([]);\n\n        self::assertCount(1, $company->categories);\n        self::assertEquals([self::CAT_FOO], $company->categories->map(static function (Category $c): string {\n            return $c->type;\n        })->getValues());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Company.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket\\SwitchContextWithFilterAndIndexedRelation;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'Company_Master')]\nclass Company\n{\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[ORM\\Column(type: 'string')]\n    public string $name;\n\n    /** @var Collection<int, Category> */\n    #[ORM\\ManyToMany(targetEntity: Category::class, fetch: 'EAGER', indexBy: 'type')]\n    public Collection $categories;\n\n    /** @param Category[] $categories */\n    public function __construct(string $name, array $categories)\n    {\n        $this->name       = $name;\n        $this->categories = new ArrayCollection($categories);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket2481Test.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket2481Test extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(Ticket2481Product::class);\n    }\n\n    public function testEmptyInsert(): void\n    {\n        $test = new Ticket2481Product();\n        $this->_em->persist($test);\n        $this->_em->flush();\n\n        self::assertGreaterThan(0, $test->id);\n    }\n}\n\n#[Table(name: 'ticket_2481_products')]\n#[Entity]\nclass Ticket2481Product\n{\n  /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket4646InstanceOfAbstractTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersonTicket4646Abstract::class,\n            EmployeeTicket4646Abstract::class,\n        );\n    }\n\n    public function testInstanceOf(): void\n    {\n        $this->_em->persist(new EmployeeTicket4646Abstract());\n        $this->_em->flush();\n\n        $dql    = 'SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Abstract p\n                WHERE p INSTANCE OF Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Abstract';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertContainsOnlyInstancesOf(PersonTicket4646Abstract::class, $result);\n    }\n}\n\n#[Table(name: 'instance_of_abstract_test_person')]\n#[Entity]\n#[InheritanceType(value: 'JOINED')]\n#[DiscriminatorColumn(name: 'kind', type: 'string')]\n#[DiscriminatorMap(value: ['employee' => EmployeeTicket4646Abstract::class])]\nabstract class PersonTicket4646Abstract\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'instance_of_abstract_test_employee')]\n#[Entity]\nclass EmployeeTicket4646Abstract extends PersonTicket4646Abstract\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket4646InstanceOfMultiLevelTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersonTicket4646MultiLevel::class,\n            EmployeeTicket4646MultiLevel::class,\n            EngineerTicket4646MultiLevel::class,\n        );\n    }\n\n    public function testInstanceOf(): void\n    {\n        $this->_em->persist(new PersonTicket4646MultiLevel());\n        $this->_em->persist(new EmployeeTicket4646MultiLevel());\n        $this->_em->persist(new EngineerTicket4646MultiLevel());\n        $this->_em->flush();\n\n        $dql    = 'SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646MultiLevel p\n                WHERE p INSTANCE OF Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646MultiLevel';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(3, $result);\n        self::assertContainsOnlyInstancesOf(PersonTicket4646MultiLevel::class, $result);\n    }\n}\n\n#[Table(name: 'instance_of_multi_level_test_person')]\n#[Entity]\n#[InheritanceType(value: 'JOINED')]\n#[DiscriminatorColumn(name: 'kind', type: 'string')]\n#[DiscriminatorMap(value: ['person' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646MultiLevel', 'employee' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\EmployeeTicket4646MultiLevel', 'engineer' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\EngineerTicket4646MultiLevel'])]\nclass PersonTicket4646MultiLevel\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'instance_of_multi_level_employee')]\n#[Entity]\nclass EmployeeTicket4646MultiLevel extends PersonTicket4646MultiLevel\n{\n}\n\n#[Table(name: 'instance_of_multi_level_engineer')]\n#[Entity]\nclass EngineerTicket4646MultiLevel extends EmployeeTicket4646MultiLevel\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket4646InstanceOfParametricTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersonTicket4646Parametric::class,\n            EmployeeTicket4646Parametric::class,\n        );\n    }\n\n    public function testInstanceOf(): void\n    {\n        $this->_em->persist(new PersonTicket4646Parametric());\n        $this->_em->persist(new EmployeeTicket4646Parametric());\n        $this->_em->flush();\n        $dql   = 'SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Parametric p\n                WHERE p INSTANCE OF :parameter';\n        $query = $this->_em->createQuery($dql);\n        $query->setParameter(\n            'parameter',\n            $this->_em->getClassMetadata(PersonTicket4646Parametric::class),\n        );\n        $result = $query->getResult();\n        self::assertCount(2, $result);\n        self::assertContainsOnlyInstancesOf(PersonTicket4646Parametric::class, $result);\n    }\n}\n\n#[Table(name: 'instance_of_parametric_person')]\n#[Entity]\n#[InheritanceType(value: 'JOINED')]\n#[DiscriminatorColumn(name: 'kind', type: 'string')]\n#[DiscriminatorMap(value: ['person' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Parametric', 'employee' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\EmployeeTicket4646Parametric'])]\nclass PersonTicket4646Parametric\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'instance_of_parametric_employee')]\n#[Entity]\nclass EmployeeTicket4646Parametric extends PersonTicket4646Parametric\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket4646InstanceOfTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersonTicket4646::class,\n            EmployeeTicket4646::class,\n        );\n    }\n\n    public function testInstanceOf(): void\n    {\n        $this->_em->persist(new PersonTicket4646());\n        $this->_em->persist(new EmployeeTicket4646());\n        $this->_em->flush();\n\n        $dql    = 'SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646 p\n                WHERE p INSTANCE OF Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n        self::assertContainsOnlyInstancesOf(PersonTicket4646::class, $result);\n    }\n}\n\n#[Table(name: 'instance_of_test_person')]\n#[Entity]\n#[InheritanceType(value: 'JOINED')]\n#[DiscriminatorColumn(name: 'kind', type: 'string')]\n#[DiscriminatorMap(value: ['person' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646', 'employee' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\EmployeeTicket4646'])]\nclass PersonTicket4646\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int|null\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'instance_of_test_employee')]\n#[Entity]\nclass EmployeeTicket4646 extends PersonTicket4646\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfWithMultipleParametersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\Ticket;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass Ticket4646InstanceOfWithMultipleParametersTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            PersonTicket4646Multiple::class,\n            EmployeeTicket4646Multiple::class,\n            ManagerTicket4646Multiple::class,\n            InternTicket4646Multiple::class,\n        );\n    }\n\n    public function testInstanceOf(): void\n    {\n        $this->_em->persist(new PersonTicket4646Multiple());\n        $this->_em->persist(new EmployeeTicket4646Multiple());\n        $this->_em->persist(new ManagerTicket4646Multiple());\n        $this->_em->persist(new InternTicket4646Multiple());\n        $this->_em->flush();\n\n        $dql    = 'SELECT p FROM Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Multiple p\n                WHERE p INSTANCE OF (Doctrine\\Tests\\ORM\\Functional\\Ticket\\EmployeeTicket4646Multiple, Doctrine\\Tests\\ORM\\Functional\\Ticket\\InternTicket4646Multiple)';\n        $query  = $this->_em->createQuery($dql);\n        $result = $query->getResult();\n\n        self::assertCount(2, $result);\n        self::assertContainsOnlyInstancesOf(PersonTicket4646Multiple::class, $result);\n    }\n}\n\n#[Table(name: 'instance_of_test_multiple_person')]\n#[Entity]\n#[InheritanceType(value: 'JOINED')]\n#[DiscriminatorColumn(name: 'kind', type: 'string')]\n#[DiscriminatorMap(value: ['person' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\PersonTicket4646Multiple', 'employee' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\EmployeeTicket4646Multiple', 'manager' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\ManagerTicket4646Multiple', 'intern' => 'Doctrine\\Tests\\ORM\\Functional\\Ticket\\InternTicket4646Multiple'])]\nclass PersonTicket4646Multiple\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Table(name: 'instance_of_test_multiple_employee')]\n#[Entity]\nclass EmployeeTicket4646Multiple extends PersonTicket4646Multiple\n{\n}\n\n#[Table(name: 'instance_of_test_multiple_manager')]\n#[Entity]\nclass ManagerTicket4646Multiple extends PersonTicket4646Multiple\n{\n}\n\n#[Table(name: 'instance_of_test_multiple_intern')]\n#[Entity]\nclass InternTicket4646Multiple extends PersonTicket4646Multiple\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/TypeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse DateTimeZone;\nuse Doctrine\\DBAL\\Types\\ArrayType;\nuse Doctrine\\DBAL\\Types\\ObjectType;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\Models\\Generic\\DecimalModel;\nuse Doctrine\\Tests\\Models\\Generic\\SerializationModel;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse stdClass;\n\nuse function class_exists;\n\nclass TypeTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('generic');\n\n        parent::setUp();\n    }\n\n    public function testDecimal(): void\n    {\n        $decimal            = new DecimalModel();\n        $decimal->decimal   = 0.15;\n        $decimal->highScale = 0.1515;\n\n        $this->_em->persist($decimal);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql     = 'SELECT d FROM ' . DecimalModel::class . ' d';\n        $decimal = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertSame('0.15', $decimal->decimal);\n        self::assertSame('0.1515', $decimal->highScale);\n    }\n\n    #[Group('DDC-1394')]\n    public function testBoolean(): void\n    {\n        $bool               = new BooleanModel();\n        $bool->booleanField = true;\n\n        $this->_em->persist($bool);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql  = 'SELECT b FROM ' . BooleanModel::class . ' b WHERE b.booleanField = true';\n        $bool = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertTrue($bool->booleanField);\n\n        $bool->booleanField = false;\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql  = 'SELECT b FROM ' . BooleanModel::class . ' b WHERE b.booleanField = false';\n        $bool = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertFalse($bool->booleanField);\n    }\n\n    public function testArray(): void\n    {\n        if (! class_exists(ArrayType::class)) {\n            self::markTestSkipped('Test valid for doctrine/dbal:3.x only.');\n        }\n\n        $serialize               = new SerializationModel();\n        $serialize->array['foo'] = 'bar';\n        $serialize->array['bar'] = 'baz';\n\n        $this->createSchemaForModels(SerializationModel::class);\n        static::$sharedConn->executeStatement('DELETE FROM serialize_model');\n        $this->_em->persist($serialize);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql       = 'SELECT s FROM ' . SerializationModel::class . ' s';\n        $serialize = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertSame(['foo' => 'bar', 'bar' => 'baz'], $serialize->array);\n    }\n\n    public function testObject(): void\n    {\n        if (! class_exists(ObjectType::class)) {\n            self::markTestSkipped('Test valid for doctrine/dbal:3.x only.');\n        }\n\n        $serialize         = new SerializationModel();\n        $serialize->object = new stdClass();\n\n        $this->createSchemaForModels(SerializationModel::class);\n        static::$sharedConn->executeStatement('DELETE FROM serialize_model');\n        $this->_em->persist($serialize);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql       = 'SELECT s FROM ' . SerializationModel::class . ' s';\n        $serialize = $this->_em->createQuery($dql)->getSingleResult();\n\n        self::assertInstanceOf(stdClass::class, $serialize->object);\n    }\n\n    public function testDate(): void\n    {\n        $dateTime       = new DateTimeModel();\n        $dateTime->date = new DateTime('2009-10-01', new DateTimeZone('Europe/Berlin'));\n\n        $this->_em->persist($dateTime);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dateTimeDb = $this->_em->find(DateTimeModel::class, $dateTime->id);\n\n        self::assertInstanceOf(DateTime::class, $dateTimeDb->date);\n        self::assertSame('2009-10-01', $dateTimeDb->date->format('Y-m-d'));\n    }\n\n    public function testDateTime(): void\n    {\n        $dateTime           = new DateTimeModel();\n        $dateTime->datetime = new DateTime('2009-10-02 20:10:52', new DateTimeZone('Europe/Berlin'));\n\n        $this->_em->persist($dateTime);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dateTimeDb = $this->_em->find(DateTimeModel::class, $dateTime->id);\n\n        self::assertInstanceOf(DateTime::class, $dateTimeDb->datetime);\n        self::assertSame('2009-10-02 20:10:52', $dateTimeDb->datetime->format('Y-m-d H:i:s'));\n\n        $articles = $this->_em->getRepository(DateTimeModel::class)\n                              ->findBy(['datetime' => new DateTime()]);\n\n        self::assertEmpty($articles);\n    }\n\n    public function testDqlQueryBindDateTimeInstance(): void\n    {\n        $date = new DateTime('2009-10-02 20:10:52', new DateTimeZone('Europe/Berlin'));\n\n        $dateTime           = new DateTimeModel();\n        $dateTime->datetime = $date;\n\n        $this->_em->persist($dateTime);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dateTimeDb = $this->_em->createQuery('SELECT d FROM Doctrine\\Tests\\Models\\Generic\\DateTimeModel d WHERE d.datetime = ?1')\n                                ->setParameter(1, $date, Types::DATETIME_MUTABLE)\n                                ->getSingleResult();\n\n        self::assertInstanceOf(DateTime::class, $dateTimeDb->datetime);\n        self::assertSame('2009-10-02 20:10:52', $dateTimeDb->datetime->format('Y-m-d H:i:s'));\n    }\n\n    public function testDqlQueryBuilderBindDateTimeInstance(): void\n    {\n        $date = new DateTime('2009-10-02 20:10:52', new DateTimeZone('Europe/Berlin'));\n\n        $dateTime           = new DateTimeModel();\n        $dateTime->datetime = $date;\n\n        $this->_em->persist($dateTime);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dateTimeDb = $this->_em->createQueryBuilder()\n                                 ->select('d')\n                                 ->from(DateTimeModel::class, 'd')\n                                 ->where('d.datetime = ?1')\n                                 ->setParameter(1, $date, Types::DATETIME_MUTABLE)\n                                 ->getQuery()->getSingleResult();\n\n        self::assertInstanceOf(DateTime::class, $dateTimeDb->datetime);\n        self::assertSame('2009-10-02 20:10:52', $dateTimeDb->datetime->format('Y-m-d H:i:s'));\n    }\n\n    public function testTime(): void\n    {\n        $dateTime       = new DateTimeModel();\n        $dateTime->time = new DateTime('2010-01-01 19:27:20');\n\n        $this->_em->persist($dateTime);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dateTimeDb = $this->_em->find(DateTimeModel::class, $dateTime->id);\n\n        self::assertInstanceOf(DateTime::class, $dateTimeDb->time);\n        self::assertSame('19:27:20', $dateTimeDb->time->format('H:i:s'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/TypeValueSqlTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\Tests\\DbalTypes\\NegativeToPositiveType;\nuse Doctrine\\Tests\\DbalTypes\\UpperCaseStringType;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeChild;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeUpperCase;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass TypeValueSqlTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        if (DBALType::hasType(UpperCaseStringType::NAME)) {\n            DBALType::overrideType(UpperCaseStringType::NAME, UpperCaseStringType::class);\n        } else {\n            DBALType::addType(UpperCaseStringType::NAME, UpperCaseStringType::class);\n        }\n\n        if (DBALType::hasType(NegativeToPositiveType::NAME)) {\n            DBALType::overrideType(NegativeToPositiveType::NAME, NegativeToPositiveType::class);\n        } else {\n            DBALType::addType(NegativeToPositiveType::NAME, NegativeToPositiveType::class);\n        }\n\n        $this->useModelSet('customtype');\n\n        parent::setUp();\n    }\n\n    public function testUpperCaseStringType(): void\n    {\n        $entity                  = new CustomTypeUpperCase();\n        $entity->lowerCaseString = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $id = $entity->id;\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(CustomTypeUpperCase::class, $id);\n\n        self::assertEquals('foo', $entity->lowerCaseString, 'Entity holds lowercase string');\n        self::assertEquals('FOO', $this->_em->getConnection()->fetchOne('select lowerCaseString from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string');\n    }\n\n    #[Group('DDC-1642')]\n    public function testUpperCaseStringTypeWhenColumnNameIsDefined(): void\n    {\n        $entity                       = new CustomTypeUpperCase();\n        $entity->lowerCaseString      = 'Some Value';\n        $entity->namedLowerCaseString = 'foo';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $id = $entity->id;\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(CustomTypeUpperCase::class, $id);\n        self::assertEquals('foo', $entity->namedLowerCaseString, 'Entity holds lowercase string');\n        self::assertEquals('FOO', $this->_em->getConnection()->fetchOne('select named_lower_case_string from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string');\n\n        $entity->namedLowerCaseString = 'bar';\n\n        $this->_em->persist($entity);\n        $this->_em->flush();\n\n        $id = $entity->id;\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(CustomTypeUpperCase::class, $id);\n        self::assertEquals('bar', $entity->namedLowerCaseString, 'Entity holds lowercase string');\n        self::assertEquals('BAR', $this->_em->getConnection()->fetchOne('select named_lower_case_string from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string');\n    }\n\n    public function testTypeValueSqlWithAssociations(): void\n    {\n        $parent                = new CustomTypeParent();\n        $parent->customInteger = -1;\n        $parent->child         = new CustomTypeChild();\n\n        $friend1 = new CustomTypeParent();\n        $friend2 = new CustomTypeParent();\n\n        $parent->addMyFriend($friend1);\n        $parent->addMyFriend($friend2);\n\n        $this->_em->persist($parent);\n        $this->_em->persist($friend1);\n        $this->_em->persist($friend2);\n        $this->_em->flush();\n\n        $parentId = $parent->id;\n\n        $this->_em->clear();\n\n        $entity = $this->_em->find(CustomTypeParent::class, $parentId);\n\n        self::assertTrue($entity->customInteger < 0, 'Fetched customInteger negative');\n        self::assertEquals(1, $this->_em->getConnection()->fetchOne('select customInteger from customtype_parents where id=' . $entity->id . ''), 'Database has stored customInteger positive');\n\n        self::assertNotNull($parent->child, 'Child attached');\n        self::assertCount(2, $entity->getMyFriends(), '2 friends attached');\n    }\n\n    public function testSelectDQL(): void\n    {\n        $parent                = new CustomTypeParent();\n        $parent->customInteger = -1;\n        $parent->child         = new CustomTypeChild();\n\n        $this->_em->persist($parent);\n        $this->_em->flush();\n\n        $parentId = $parent->id;\n\n        $this->_em->clear();\n\n        $query = $this->_em->createQuery('SELECT p, p.customInteger, c from Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p JOIN p.child c where p.id = ' . $parentId);\n\n        $result = $query->getResult();\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(CustomTypeParent::class, $result[0][0]);\n        self::assertEquals(-1, $result[0][0]->customInteger);\n\n        self::assertEquals(-1, $result[0]['customInteger']);\n\n        self::assertEquals('foo', $result[0][0]->child->lowerCaseString);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/UnitOfWorkLifecycleTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\n\nclass UnitOfWorkLifecycleTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('cms');\n\n        parent::setUp();\n    }\n\n    public function testScheduleInsertManaged(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin';\n        $user->status   = 'active';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage('A managed+dirty entity Doctrine\\Tests\\Models\\CMS\\CmsUser');\n\n        $this->_em->getUnitOfWork()->scheduleForInsert($user);\n    }\n\n    public function testScheduleInsertDeleted(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin';\n        $user->status   = 'active';\n        $this->_em->persist($user);\n        $this->_em->flush();\n\n        $this->_em->remove($user);\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage('Removed entity Doctrine\\Tests\\Models\\CMS\\CmsUser');\n\n        $this->_em->getUnitOfWork()->scheduleForInsert($user);\n    }\n\n    public function testScheduleInsertTwice(): void\n    {\n        $user           = new CmsUser();\n        $user->username = 'beberlei';\n        $user->name     = 'Benjamin';\n        $user->status   = 'active';\n\n        $this->_em->getUnitOfWork()->scheduleForInsert($user);\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage('Entity Doctrine\\Tests\\Models\\CMS\\CmsUser');\n\n        $this->_em->getUnitOfWork()->scheduleForInsert($user);\n    }\n\n    public function testAddToIdentityMapWithoutIdentity(): void\n    {\n        $user = new CmsUser();\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage(\"The given entity of type 'Doctrine\\Tests\\Models\\CMS\\CmsUser' (Doctrine\\Tests\\Models\\CMS\\CmsUser@\");\n\n        $this->_em->getUnitOfWork()->registerManaged($user, [], []);\n    }\n\n    public function testMarkReadOnlyNonManaged(): void\n    {\n        $user = new CmsUser();\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage('Only managed entities can be marked or checked as read only. But Doctrine\\Tests\\Models\\CMS\\CmsUser@');\n\n        $this->_em->getUnitOfWork()->markReadOnly($user);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\AuxiliaryEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that ManyToMany associations with composite id of which one is a\n * association itself work correctly.\n */\n#[Group('DDC-3380')]\nclass ManyToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_manytomany_compositeid_foreignkey');\n\n        parent::setUp();\n\n        $auxiliary      = new Entity\\AuxiliaryEntity();\n        $auxiliary->id4 = 'abc';\n\n        $inversed                = new Entity\\InversedManyToManyCompositeIdForeignKeyEntity();\n        $inversed->id1           = 'def';\n        $inversed->foreignEntity = $auxiliary;\n\n        $owning      = new Entity\\OwningManyToManyCompositeIdForeignKeyEntity();\n        $owning->id2 = 'ghi';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntities->add($inversed);\n\n        $this->_em->persist($auxiliary);\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_xref_manytomany_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_owning_manytomany_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_inversed_manytomany_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_auxiliary');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id4 FROM vct_auxiliary LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id1 FROM vct_inversed_manytomany_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT foreign_id FROM vct_inversed_manytomany_compositeid_foreignkey LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id2 FROM vct_owning_manytomany_compositeid_foreignkey LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_foreign_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('tuv', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary);\n        self::assertInstanceOf(InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToManyCompositeIdForeignKeyEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $auxiliary->id4);\n        self::assertEquals('def', $inversed->id1);\n        self::assertEquals('abc', $inversed->foreignEntity->id4);\n        self::assertEquals('ghi', $owning->id2);\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')]\n    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => $auxiliary],\n        );\n\n        self::assertInstanceOf(InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertCount(1, $owning->associatedEntities);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n\n    #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')]\n    #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')]\n    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        // remove association\n\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        foreach ($inversed->associatedEntities as $owning) {\n            $inversed->associatedEntities->removeElement($owning);\n            $owning->associatedEntities->removeElement($inversed);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // test association is removed\n\n        self::assertEquals(0, $conn->fetchOne('SELECT COUNT(*) FROM vct_xref_manytomany_compositeid_foreignkey'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyCompositeIdEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that ManyToMany associations with composite id work correctly.\n */\n#[Group('DDC-3380')]\nclass ManyToManyCompositeIdTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_manytomany_compositeid');\n\n        parent::setUp();\n\n        $inversed      = new Entity\\InversedManyToManyCompositeIdEntity();\n        $inversed->id1 = 'abc';\n        $inversed->id2 = 'def';\n\n        $owning      = new Entity\\OwningManyToManyCompositeIdEntity();\n        $owning->id3 = 'ghi';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntities->add($inversed);\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_xref_manytomany_compositeid');\n        $conn->executeStatement('DROP TABLE vct_owning_manytomany_compositeid');\n        $conn->executeStatement('DROP TABLE vct_inversed_manytomany_compositeid');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_manytomany_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_inversed_manytomany_compositeid LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id3 FROM vct_owning_manytomany_compositeid LIMIT 1'));\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT inversed_id1 FROM vct_xref_manytomany_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT inversed_id2 FROM vct_xref_manytomany_compositeid LIMIT 1'));\n        self::assertEquals('tuv', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany_compositeid LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(InversedManyToManyCompositeIdEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToManyCompositeIdEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $inversed->id2);\n        self::assertEquals('ghi', $owning->id3);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertCount(1, $owning->associatedEntities);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n\n    #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')]\n    #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')]\n    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        // remove association\n\n        $inversed = $this->_em->find(\n            InversedManyToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        foreach ($inversed->associatedEntities as $owning) {\n            $inversed->associatedEntities->removeElement($owning);\n            $owning->associatedEntities->removeElement($inversed);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // test association is removed\n\n        self::assertEquals(0, $conn->fetchOne('SELECT COUNT(*) FROM vct_xref_manytomany_compositeid'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCriteriaMatchingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass ManyToManyCriteriaMatchingTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_manytomany');\n\n        parent::setUp();\n    }\n\n    #[DataProvider('provideMatchingExpressions')]\n    public function testCriteriaMatchingOnFieldInManyToManyTarget(Comparison $comparison): void\n    {\n        $associated      = new InversedManyToManyEntity();\n        $associated->id1 = 'associated';\n\n        $matching        = new OwningManyToManyEntity();\n        $matching->id2   = 'first';\n        $matching->field = 'match this'; // stored as 'zngpu guvf'\n        $matching->associatedEntities->add($associated);\n\n        $nonMatching        = new OwningManyToManyEntity();\n        $nonMatching->id2   = 'second';\n        $nonMatching->field = 'this is no match';\n        $nonMatching->associatedEntities->add($associated);\n\n        $this->_em->persist($associated);\n        $this->_em->persist($matching);\n        $this->_em->persist($nonMatching);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $associated = $this->_em->find(InversedManyToManyEntity::class, 'associated');\n        self::assertFalse($associated->associatedEntities->isInitialized(), 'Pre-condition: lazy collection');\n\n        $result = $associated->associatedEntities->matching(Criteria::create(true)->where($comparison));\n\n        $l = $this->getQueryLog();\n\n        self::assertCount(1, $result);\n        self::assertSame('first', $result[0]->id2);\n    }\n\n    public static function provideMatchingExpressions(): Generator\n    {\n        yield [Criteria::expr()->eq('field', 'match this')]; // should convert to 'zngpu guvf'\n        yield [Criteria::expr()->in('field', ['match this'])]; // should convert to ['zngpu guvf']\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that ManyToMany associations work correctly, focusing on EXTRA_LAZY\n * functionality.\n */\n#[Group('DDC-3380')]\nclass ManyToManyExtraLazyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_manytomany_extralazy');\n\n        parent::setUp();\n\n        $inversed1      = new Entity\\InversedManyToManyExtraLazyEntity();\n        $inversed1->id1 = 'abc';\n\n        $inversed2      = new Entity\\InversedManyToManyExtraLazyEntity();\n        $inversed2->id1 = 'def';\n\n        $owning1      = new Entity\\OwningManyToManyExtraLazyEntity();\n        $owning1->id2 = 'ghi';\n\n        $owning2      = new Entity\\OwningManyToManyExtraLazyEntity();\n        $owning2->id2 = 'jkl';\n\n        $inversed1->associatedEntities->add($owning1);\n        $owning1->associatedEntities->add($inversed1);\n        $inversed1->associatedEntities->add($owning2);\n        $owning2->associatedEntities->add($inversed1);\n\n        $inversed2->associatedEntities->add($owning1);\n        $owning1->associatedEntities->add($inversed2);\n        $inversed2->associatedEntities->add($owning2);\n        $owning2->associatedEntities->add($inversed2);\n\n        $this->_em->persist($inversed1);\n        $this->_em->persist($inversed2);\n        $this->_em->persist($owning1);\n        $this->_em->persist($owning2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_xref_manytomany_extralazy');\n        $conn->executeStatement('DROP TABLE vct_owning_manytomany_extralazy');\n        $conn->executeStatement('DROP TABLE vct_inversed_manytomany_extralazy');\n    }\n\n    public function testThatTheExtraLazyCollectionFromOwningToInversedIsCounted(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyExtraLazyEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals(2, $owning->associatedEntities->count());\n    }\n\n    public function testThatTheExtraLazyCollectionFromInversedToOwningIsCounted(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertEquals(2, $inversed->associatedEntities->count());\n    }\n\n    public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnEntity(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyExtraLazyEntity::class,\n            'ghi',\n        );\n\n        $inversed = $this->_em->find(\n            InversedManyToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertTrue($owning->associatedEntities->contains($inversed));\n    }\n\n    public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnEntity(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyExtraLazyEntity::class,\n            'ghi',\n        );\n\n        self::assertTrue($inversed->associatedEntities->contains($owning));\n    }\n\n    public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnIndexByKey(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyExtraLazyEntity::class,\n            'ghi',\n        );\n\n        self::assertTrue($owning->associatedEntities->containsKey('abc'));\n    }\n\n    public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnIndexByKey(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertTrue($inversed->associatedEntities->containsKey('ghi'));\n    }\n\n    public function testThatASliceOfTheExtraLazyCollectionFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyExtraLazyEntity::class,\n            'ghi',\n        );\n\n        self::assertCount(1, $owning->associatedEntities->slice(0, 1));\n    }\n\n    public function testThatASliceOfTheExtraLazyCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertCount(1, $inversed->associatedEntities->slice(1, 1));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that ManyToMany associations work correctly.\n */\n#[Group('DDC-3380')]\nclass ManyToManyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_manytomany');\n\n        parent::setUp();\n\n        $inversed      = new Entity\\InversedManyToManyEntity();\n        $inversed->id1 = 'abc';\n\n        $owning      = new Entity\\OwningManyToManyEntity();\n        $owning->id2 = 'def';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntities->add($inversed);\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_xref_manytomany');\n        $conn->executeStatement('DROP TABLE vct_owning_manytomany');\n        $conn->executeStatement('DROP TABLE vct_inversed_manytomany');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_manytomany LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_owning_manytomany LIMIT 1'));\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT inversed_id FROM vct_xref_manytomany LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyEntity::class,\n            'def',\n        );\n\n        self::assertInstanceOf(InversedManyToManyEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToManyEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToManyEntity::class,\n            'def',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $owning->id2);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToManyEntity::class,\n            'def',\n        );\n\n        self::assertCount(1, $owning->associatedEntities);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedManyToManyEntity::class,\n            'abc',\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n\n    #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')]\n    #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')]\n    public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        // remove association\n\n        $inversed = $this->_em->find(\n            InversedManyToManyEntity::class,\n            'abc',\n        );\n\n        foreach ($inversed->associatedEntities as $owning) {\n            $inversed->associatedEntities->removeElement($owning);\n            $owning->associatedEntities->removeElement($inversed);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // test association is removed\n\n        self::assertEquals(0, $conn->fetchOne('SELECT COUNT(*) FROM vct_xref_manytomany'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\AuxiliaryEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToMany associations with composite id of which one is a\n * association itself work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetomany_compositeid_foreignkey');\n\n        parent::setUp();\n\n        $auxiliary      = new Entity\\AuxiliaryEntity();\n        $auxiliary->id4 = 'abc';\n\n        $inversed                = new Entity\\InversedOneToManyCompositeIdForeignKeyEntity();\n        $inversed->id1           = 'def';\n        $inversed->foreignEntity = $auxiliary;\n        $inversed->someProperty  = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningManyToOneCompositeIdForeignKeyEntity();\n        $owning->id2 = 'ghi';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntity = $inversed;\n\n        $this->_em->persist($auxiliary);\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_manytoone_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetomany_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_auxiliary');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id4 FROM vct_auxiliary LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetomany_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT foreign_id FROM vct_inversed_onetomany_compositeid_foreignkey LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id2 FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_foreign_id FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary);\n        self::assertInstanceOf(InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToOneCompositeIdForeignKeyEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $auxiliary->id4);\n        self::assertEquals('def', $inversed->id1);\n        self::assertEquals('abc', $inversed->foreignEntity->id4);\n        self::assertEquals('ghi', $owning->id2);\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')]\n    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => $auxiliary],\n        );\n\n        self::assertInstanceOf(InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertSame('def', $inversedProxy->id1, 'Proxy identifier is converted');\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneCompositeIdEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToMany associations with composite id work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToManyCompositeIdTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetomany_compositeid');\n\n        parent::setUp();\n\n        $inversed               = new Entity\\InversedOneToManyCompositeIdEntity();\n        $inversed->id1          = 'abc';\n        $inversed->id2          = 'def';\n        $inversed->someProperty = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningManyToOneCompositeIdEntity();\n        $owning->id3 = 'ghi';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntity = $inversed;\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_manytoone_compositeid');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetomany_compositeid');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetomany_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_inversed_onetomany_compositeid LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id3 FROM vct_owning_manytoone_compositeid LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_id1 FROM vct_owning_manytoone_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id2 FROM vct_owning_manytoone_compositeid LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(InversedOneToManyCompositeIdEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToOneCompositeIdEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $inversed->id2);\n        self::assertEquals('ghi', $owning->id3);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToManyCriteriaMatchingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass OneToManyCriteriaMatchingTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetomany');\n\n        parent::setUp();\n    }\n\n    #[DataProvider('provideMatchingExpressions')]\n    public function testCriteriaMatchingOnFieldInOneToManyTarget(Comparison $comparison): void\n    {\n        $entityWithCollection      = new InversedOneToManyEntity();\n        $entityWithCollection->id1 = 'associated';\n\n        $matching                   = new OwningManyToOneEntity();\n        $matching->id2              = 'first';\n        $matching->field            = 'match this'; // stored as 'zngpu guvf'\n        $matching->associatedEntity = $entityWithCollection;\n\n        $notMatching                   = new OwningManyToOneEntity();\n        $notMatching->id2              = 'second';\n        $notMatching->field            = 'this is no match';\n        $notMatching->associatedEntity = $entityWithCollection;\n\n        $this->_em->persist($entityWithCollection);\n        $this->_em->persist($matching);\n        $this->_em->persist($notMatching);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $this->getQueryLog()->reset()->enable();\n\n        $entityWithCollection = $this->_em->find(InversedOneToManyEntity::class, 'associated');\n        self::assertFalse($entityWithCollection->associatedEntities->isInitialized(), 'Pre-condition: lazy collection');\n\n        $result = $entityWithCollection->associatedEntities->matching(Criteria::create(true)->where($comparison));\n\n        $l = $this->getQueryLog();\n\n        self::assertCount(1, $result);\n        self::assertSame('first', $result[0]->id2);\n    }\n\n    public static function provideMatchingExpressions(): Generator\n    {\n        yield [Criteria::expr()->eq('field', 'match this')]; // should convert to 'zngpu guvf'\n        yield [Criteria::expr()->in('field', ['match this'])]; // should convert to ['zngpu guvf']\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneExtraLazyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToMany associations work correctly, focusing on EXTRA_LAZY\n * functionality.\n */\n#[Group('DDC-3380')]\nclass OneToManyExtraLazyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetomany_extralazy');\n\n        parent::setUp();\n\n        $inversed      = new Entity\\InversedOneToManyExtraLazyEntity();\n        $inversed->id1 = 'abc';\n\n        $owning1      = new Entity\\OwningManyToOneExtraLazyEntity();\n        $owning1->id2 = 'def';\n\n        $owning2      = new Entity\\OwningManyToOneExtraLazyEntity();\n        $owning2->id2 = 'ghi';\n\n        $owning3      = new Entity\\OwningManyToOneExtraLazyEntity();\n        $owning3->id2 = 'jkl';\n\n        $inversed->associatedEntities->add($owning1);\n        $owning1->associatedEntity = $inversed;\n        $inversed->associatedEntities->add($owning2);\n        $owning2->associatedEntity = $inversed;\n        $inversed->associatedEntities->add($owning3);\n        $owning3->associatedEntity = $inversed;\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning1);\n        $this->_em->persist($owning2);\n        $this->_em->persist($owning3);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_manytoone_extralazy');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetomany_extralazy');\n    }\n\n    public function testThatExtraLazyCollectionIsCounted(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertEquals(3, $inversed->associatedEntities->count());\n    }\n\n    public function testThatExtraLazyCollectionContainsAnEntity(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneExtraLazyEntity::class,\n            'def',\n        );\n\n        self::assertTrue($inversed->associatedEntities->contains($owning));\n    }\n\n    public function testThatExtraLazyCollectionContainsAnIndexbyKey(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertTrue($inversed->associatedEntities->containsKey('def'));\n    }\n\n    public function testThatASliceOfTheExtraLazyCollectionIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyExtraLazyEntity::class,\n            'abc',\n        );\n\n        self::assertCount(2, $inversed->associatedEntities->slice(0, 2));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToMany associations work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToManyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetomany');\n\n        parent::setUp();\n\n        $inversed               = new Entity\\InversedOneToManyEntity();\n        $inversed->id1          = 'abc';\n        $inversed->someProperty = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningManyToOneEntity();\n        $owning->id2 = 'def';\n\n        $inversed->associatedEntities->add($owning);\n        $owning->associatedEntity = $inversed;\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_manytoone');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetomany');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetomany LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_owning_manytoone LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_id FROM vct_owning_manytoone LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneEntity::class,\n            'def',\n        );\n\n        self::assertInstanceOf(InversedOneToManyEntity::class, $inversed);\n        self::assertInstanceOf(OwningManyToOneEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningManyToOneEntity::class,\n            'def',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $owning->id2);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningManyToOneEntity::class,\n            'def',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheCollectionFromInversedToOwningIsLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToManyEntity::class,\n            'abc',\n        );\n\n        self::assertCount(1, $inversed->associatedEntities);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\AuxiliaryEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToOne associations with composite id of which one is a\n * association itself work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToOneCompositeIdForeignKeyTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetoone_compositeid_foreignkey');\n\n        parent::setUp();\n\n        $auxiliary      = new Entity\\AuxiliaryEntity();\n        $auxiliary->id4 = 'abc';\n\n        $inversed                = new Entity\\InversedOneToOneCompositeIdForeignKeyEntity();\n        $inversed->id1           = 'def';\n        $inversed->foreignEntity = $auxiliary;\n        $inversed->someProperty  = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningOneToOneCompositeIdForeignKeyEntity();\n        $owning->id2 = 'ghi';\n\n        $inversed->associatedEntity = $owning;\n        $owning->associatedEntity   = $inversed;\n\n        $this->_em->persist($auxiliary);\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_onetoone_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetoone_compositeid_foreignkey');\n        $conn->executeStatement('DROP TABLE vct_auxiliary');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id4 FROM vct_auxiliary LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetoone_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT foreign_id FROM vct_inversed_onetoone_compositeid_foreignkey LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id2 FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_foreign_id FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary);\n        self::assertInstanceOf(InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed);\n        self::assertInstanceOf(OwningOneToOneCompositeIdForeignKeyEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $auxiliary->id4);\n        self::assertEquals('def', $inversed->id1);\n        self::assertEquals('abc', $inversed->foreignEntity->id4);\n        self::assertEquals('ghi', $owning->id2);\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')]\n    public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void\n    {\n        $auxiliary = $this->_em->find(\n            AuxiliaryEntity::class,\n            'abc',\n        );\n\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => $auxiliary],\n        );\n\n        self::assertInstanceOf(InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdForeignKeyEntity::class,\n            'ghi',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdForeignKeyEntity::class,\n            ['id1' => 'def', 'foreignEntity' => 'abc'],\n        );\n\n        self::assertInstanceOf(OwningOneToOneCompositeIdForeignKeyEntity::class, $inversed->associatedEntity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneCompositeIdEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToOne associations with composite id work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToOneCompositeIdTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetoone_compositeid');\n\n        parent::setUp();\n\n        $inversed               = new Entity\\InversedOneToOneCompositeIdEntity();\n        $inversed->id1          = 'abc';\n        $inversed->id2          = 'def';\n        $inversed->someProperty = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningOneToOneCompositeIdEntity();\n        $owning->id3 = 'ghi';\n\n        $inversed->associatedEntity = $owning;\n        $owning->associatedEntity   = $inversed;\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_onetoone_compositeid');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetoone_compositeid');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetoone_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_inversed_onetoone_compositeid LIMIT 1'));\n\n        self::assertEquals('tuv', $conn->fetchOne('SELECT id3 FROM vct_owning_onetoone_compositeid LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_id1 FROM vct_owning_onetoone_compositeid LIMIT 1'));\n        self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id2 FROM vct_owning_onetoone_compositeid LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertInstanceOf(InversedOneToOneCompositeIdEntity::class, $inversed);\n        self::assertInstanceOf(OwningOneToOneCompositeIdEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $inversed->id2);\n        self::assertEquals('ghi', $owning->id3);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningOneToOneCompositeIdEntity::class,\n            'ghi',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneCompositeIdEntity::class,\n            ['id1' => 'abc', 'id2' => 'def'],\n        );\n\n        self::assertInstanceOf(OwningOneToOneCompositeIdEntity::class, $inversed->associatedEntity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional\\ValueConversionType;\n\nuse Doctrine\\Tests\\Models\\ValueConversionType as Entity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * The entities all use a custom type that converst the value as identifier(s).\n * {@see \\Doctrine\\Tests\\DbalTypes\\Rot13Type}\n *\n * Test that OneToOne associations work correctly.\n */\n#[Group('DDC-3380')]\nclass OneToOneTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        $this->useModelSet('vct_onetoone');\n\n        parent::setUp();\n\n        $inversed               = new Entity\\InversedOneToOneEntity();\n        $inversed->id1          = 'abc';\n        $inversed->someProperty = 'some value to be loaded';\n\n        $owning      = new Entity\\OwningOneToOneEntity();\n        $owning->id2 = 'def';\n\n        $inversed->associatedEntity = $owning;\n        $owning->associatedEntity   = $inversed;\n\n        $this->_em->persist($inversed);\n        $this->_em->persist($owning);\n\n        $this->_em->flush();\n        $this->_em->clear();\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        $conn = static::$sharedConn;\n\n        $conn->executeStatement('DROP TABLE vct_owning_onetoone');\n        $conn->executeStatement('DROP TABLE vct_inversed_onetoone');\n    }\n\n    public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void\n    {\n        $conn = $this->_em->getConnection();\n\n        self::assertEquals('nop', $conn->fetchOne('SELECT id1 FROM vct_inversed_onetoone LIMIT 1'));\n\n        self::assertEquals('qrs', $conn->fetchOne('SELECT id2 FROM vct_owning_onetoone LIMIT 1'));\n        self::assertEquals('nop', $conn->fetchOne('SELECT associated_id FROM vct_owning_onetoone LIMIT 1'));\n    }\n\n    #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')]\n    public function testThatEntitiesAreFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneEntity::class,\n            'def',\n        );\n\n        self::assertInstanceOf(InversedOneToOneEntity::class, $inversed);\n        self::assertInstanceOf(OwningOneToOneEntity::class, $owning);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneEntity::class,\n            'abc',\n        );\n\n        $owning = $this->_em->find(\n            OwningOneToOneEntity::class,\n            'def',\n        );\n\n        self::assertEquals('abc', $inversed->id1);\n        self::assertEquals('def', $owning->id2);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheProxyFromOwningToInversedIsLoaded(): void\n    {\n        $owning = $this->_em->find(\n            OwningOneToOneEntity::class,\n            'def',\n        );\n\n        $inversedProxy = $owning->associatedEntity;\n\n        self::assertEquals('some value to be loaded', $inversedProxy->someProperty);\n    }\n\n    #[Depends('testThatEntitiesAreFetchedFromTheDatabase')]\n    public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void\n    {\n        $inversed = $this->_em->find(\n            InversedOneToOneEntity::class,\n            'abc',\n        );\n\n        self::assertInstanceOf(OwningOneToOneEntity::class, $inversed->associatedEntity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/ValueObjectsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse DateTime;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Embedded;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function sprintf;\n\n#[Group('DDC-93')]\nclass ValueObjectsTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            DDC93Person::class,\n            DDC93Address::class,\n            DDC93Vehicle::class,\n            DDC93Car::class,\n            DDC3027Animal::class,\n            DDC3027Dog::class,\n        );\n    }\n\n    public function testCRUD(): void\n    {\n        $person                   = new DDC93Person();\n        $person->name             = 'Tara';\n        $person->address          = new DDC93Address();\n        $person->address->street  = 'United States of Tara Street';\n        $person->address->zip     = '12345';\n        $person->address->city    = 'funkytown';\n        $person->address->country = new DDC93Country('Germany');\n\n        // 1. check saving value objects works\n        $this->_em->persist($person);\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        // 2. check loading value objects works\n        $person = $this->_em->find(DDC93Person::class, $person->id);\n\n        self::assertInstanceOf(DDC93Address::class, $person->address);\n        self::assertEquals('United States of Tara Street', $person->address->street);\n        self::assertEquals('12345', $person->address->zip);\n        self::assertEquals('funkytown', $person->address->city);\n        self::assertInstanceOf(DDC93Country::class, $person->address->country);\n        self::assertEquals('Germany', $person->address->country->name);\n\n        // 3. check changing value objects works\n        $person->address->street        = 'Street';\n        $person->address->zip           = '54321';\n        $person->address->city          = 'another town';\n        $person->address->country->name = 'United States of America';\n        $this->_em->flush();\n\n        $this->_em->clear();\n\n        $person = $this->_em->find(DDC93Person::class, $person->id);\n\n        self::assertEquals('Street', $person->address->street);\n        self::assertEquals('54321', $person->address->zip);\n        self::assertEquals('another town', $person->address->city);\n        self::assertEquals('United States of America', $person->address->country->name);\n\n        // 4. check deleting works\n        $personId = $person->id;\n\n        $this->_em->remove($person);\n        $this->_em->flush();\n\n        self::assertNull($this->_em->find(DDC93Person::class, $personId));\n    }\n\n    public function testLoadDql(): void\n    {\n        for ($i = 0; $i < 3; $i++) {\n            $person                   = new DDC93Person();\n            $person->name             = 'Donkey Kong' . $i;\n            $person->address          = new DDC93Address();\n            $person->address->street  = 'Tree';\n            $person->address->zip     = '12345';\n            $person->address->city    = 'funkytown';\n            $person->address->country = new DDC93Country('United States of America');\n\n            $this->_em->persist($person);\n        }\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $dql     = 'SELECT p FROM ' . __NAMESPACE__ . '\\DDC93Person p';\n        $persons = $this->_em->createQuery($dql)->getResult();\n\n        self::assertCount(3, $persons);\n        foreach ($persons as $person) {\n            self::assertInstanceOf(DDC93Address::class, $person->address);\n            self::assertEquals('Tree', $person->address->street);\n            self::assertEquals('12345', $person->address->zip);\n            self::assertEquals('funkytown', $person->address->city);\n            self::assertInstanceOf(DDC93Country::class, $person->address->country);\n            self::assertEquals('United States of America', $person->address->country->name);\n        }\n\n        $dql     = 'SELECT p FROM ' . __NAMESPACE__ . '\\DDC93Person p';\n        $persons = $this->_em->createQuery($dql)->getArrayResult();\n\n        foreach ($persons as $person) {\n            self::assertEquals('Tree', $person['address.street']);\n            self::assertEquals('12345', $person['address.zip']);\n            self::assertEquals('funkytown', $person['address.city']);\n            self::assertEquals('United States of America', $person['address.country.name']);\n        }\n    }\n\n    #[Group('dql')]\n    public function testDqlOnEmbeddedObjectsField(): void\n    {\n        if ($this->isSecondLevelCacheEnabled) {\n            self::markTestSkipped('SLC does not work with UPDATE/DELETE queries through EM.');\n        }\n\n        $person = new DDC93Person('Johannes', new DDC93Address('Moo', '12345', 'Karlsruhe', new DDC93Country('Germany')));\n        $this->_em->persist($person);\n        $this->_em->flush();\n\n        // SELECT\n        $selectDql    = 'SELECT p FROM ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.address.city = :city AND p.address.country.name = :country';\n        $loadedPerson = $this->_em->createQuery($selectDql)\n            ->setParameter('city', 'Karlsruhe')\n            ->setParameter('country', 'Germany')\n            ->getSingleResult();\n        self::assertEquals($person, $loadedPerson);\n\n        self::assertNull(\n            $this->_em->createQuery($selectDql)\n                ->setParameter('city', 'asdf')\n                ->setParameter('country', 'Germany')\n                ->getOneOrNullResult(),\n        );\n\n        // UPDATE\n        $updateDql = 'UPDATE ' . __NAMESPACE__ . '\\\\DDC93Person p SET p.address.street = :street, p.address.country.name = :country WHERE p.address.city = :city';\n        $this->_em->createQuery($updateDql)\n            ->setParameter('street', 'Boo')\n            ->setParameter('country', 'DE')\n            ->setParameter('city', 'Karlsruhe')\n            ->execute();\n\n        $this->_em->refresh($person);\n        self::assertEquals('Boo', $person->address->street);\n        self::assertEquals('DE', $person->address->country->name);\n\n        // DELETE\n        $this->_em->createQuery('DELETE ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.address.city = :city AND p.address.country.name = :country')\n            ->setParameter('city', 'Karlsruhe')\n            ->setParameter('country', 'DE')\n            ->execute();\n\n        $this->_em->clear();\n        self::assertNull($this->_em->find(DDC93Person::class, $person->id));\n    }\n\n    public function testPartialDqlOnEmbeddedObjectsField(): void\n    {\n        $person = new DDC93Person('Karl', new DDC93Address('Foo', '12345', 'Gosport', new DDC93Country('England')));\n        $this->_em->persist($person);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        // Prove that the entity was persisted correctly.\n        $dql = 'SELECT p FROM ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.name = :name';\n\n        $person = $this->_em->createQuery($dql)\n            ->setParameter('name', 'Karl')\n            ->getSingleResult();\n\n        self::assertEquals('Gosport', $person->address->city);\n        self::assertEquals('Foo', $person->address->street);\n        self::assertEquals('12345', $person->address->zip);\n        self::assertEquals('England', $person->address->country->name);\n\n        // Clear the EM and prove that the embeddable can be the subject of a partial query.\n        $this->_em->clear();\n\n        $dql = 'SELECT PARTIAL p.{id,address.city} FROM ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.name = :name';\n\n        $person = $this->_em->createQuery($dql)\n            ->setParameter('name', 'Karl')\n            ->getSingleResult();\n\n        // Selected field must be equal, all other fields must be null.\n        self::assertEquals('Gosport', $person->address->city);\n        self::assertNull($person->address->street);\n        self::assertNull($person->address->zip);\n        self::assertNull($person->address->country);\n        self::assertNull($person->name);\n\n        // Clear the EM and prove that the embeddable can be the subject of a partial query regardless of attributes positions.\n        $this->_em->clear();\n\n        $dql = 'SELECT PARTIAL p.{address.city, id} FROM ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.name = :name';\n\n        $person = $this->_em->createQuery($dql)\n            ->setParameter('name', 'Karl')\n            ->getSingleResult();\n\n        // Selected field must be equal, all other fields must be null.\n        self::assertEquals('Gosport', $person->address->city);\n        self::assertNull($person->address->street);\n        self::assertNull($person->address->zip);\n        self::assertNull($person->address->country);\n        self::assertNull($person->name);\n    }\n\n    public function testDqlWithNonExistentEmbeddableField(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('no field or association named address.asdfasdf');\n\n        $this->_em->createQuery('SELECT p FROM ' . __NAMESPACE__ . '\\\\DDC93Person p WHERE p.address.asdfasdf IS NULL')\n            ->execute();\n    }\n\n    public function testPartialDqlWithNonExistentEmbeddableField(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage(\"no mapped field named 'address.asdfasdf'\");\n\n        $this->_em->createQuery('SELECT PARTIAL p.{id,address.asdfasdf} FROM ' . __NAMESPACE__ . '\\\\DDC93Person p')\n            ->execute();\n    }\n\n    public function testEmbeddableWithInheritance(): void\n    {\n        $car = new DDC93Car(new DDC93Address('Foo', '12345', 'Asdf'));\n        $this->_em->persist($car);\n        $this->_em->flush();\n\n        $reloadedCar = $this->_em->find(DDC93Car::class, $car->id);\n        self::assertEquals($car, $reloadedCar);\n    }\n\n    public function testInlineEmbeddableWithPrefix(): void\n    {\n        $metadata = $this->_em->getClassMetadata(DDC3028PersonWithPrefix::class);\n\n        self::assertEquals('foobar_id', $metadata->getColumnName('id.id'));\n        self::assertEquals('bloo_foo_id', $metadata->getColumnName('nested.nestedWithPrefix.id'));\n        self::assertEquals('bloo_nestedWithEmptyPrefix_id', $metadata->getColumnName('nested.nestedWithEmptyPrefix.id'));\n        self::assertEquals('bloo_id', $metadata->getColumnName('nested.nestedWithPrefixFalse.id'));\n    }\n\n    public function testInlineEmbeddableEmptyPrefix(): void\n    {\n        $metadata = $this->_em->getClassMetadata(DDC3028PersonEmptyPrefix::class);\n\n        self::assertEquals('id_id', $metadata->getColumnName('id.id'));\n        self::assertEquals('nested_foo_id', $metadata->getColumnName('nested.nestedWithPrefix.id'));\n        self::assertEquals('nested_nestedWithEmptyPrefix_id', $metadata->getColumnName('nested.nestedWithEmptyPrefix.id'));\n        self::assertEquals('nested_id', $metadata->getColumnName('nested.nestedWithPrefixFalse.id'));\n    }\n\n    public function testInlineEmbeddablePrefixFalse(): void\n    {\n        $expectedColumnName = 'id';\n\n        $actualColumnName = $this->_em\n            ->getClassMetadata(DDC3028PersonPrefixFalse::class)\n            ->getColumnName('id.id');\n\n        self::assertEquals($expectedColumnName, $actualColumnName);\n    }\n\n    public function testInlineEmbeddableInMappedSuperClass(): void\n    {\n        $isFieldMapped = $this->_em\n            ->getClassMetadata(DDC3027Dog::class)\n            ->hasField('address.street');\n\n        self::assertTrue($isFieldMapped);\n    }\n\n    #[DataProvider('getInfiniteEmbeddableNestingData')]\n    public function testThrowsExceptionOnInfiniteEmbeddableNesting(\n        string $embeddableClassName,\n        string $declaredEmbeddableClassName,\n    ): void {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\n            sprintf(\n                'Infinite nesting detected for embedded property %s::nested. ' .\n                'You cannot embed an embeddable from the same type inside an embeddable.',\n                __NAMESPACE__ . '\\\\' . $declaredEmbeddableClassName,\n            ),\n        );\n\n        $this->createSchemaForModels(__NAMESPACE__ . '\\\\' . $embeddableClassName);\n    }\n\n    /** @phpstan-return list<array{string, string}> */\n    public static function getInfiniteEmbeddableNestingData(): array\n    {\n        return [\n            ['DDCInfiniteNestingEmbeddable', 'DDCInfiniteNestingEmbeddable'],\n            ['DDCNestingEmbeddable1', 'DDCNestingEmbeddable4'],\n        ];\n    }\n}\n\n\n#[Entity]\nclass DDC93Person\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC93Timestamps */\n    #[Embedded(class: 'DDC93Timestamps')]\n    public $timestamps;\n\n    public function __construct(\n        /** @var string|null */\n        #[Column(type: 'string', length: 255)]\n        public $name = null,\n        #[Embedded(class: 'DDC93Address')]\n        public DDC93Address|null $address = null,\n    ) {\n        $this->timestamps = new DDC93Timestamps(new DateTime());\n    }\n}\n\n#[Embeddable]\nclass DDC93Timestamps\n{\n    public function __construct(\n        #[Column(type: 'datetime')]\n        public DateTime $createdAt,\n    ) {\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 't', type: 'string', length: 10)]\n#[DiscriminatorMap(['v' => 'Doctrine\\Tests\\ORM\\Functional\\DDC93Car'])]\nabstract class DDC93Vehicle\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    public function __construct(\n        #[Embedded(class: 'DDC93Address')]\n        public DDC93Address $address,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC93Car extends DDC93Vehicle\n{\n}\n\n#[Embeddable]\nclass DDC93Country\n{\n    public function __construct(\n        #[Column(type: 'string', nullable: true)]\n        public string|null $name = null,\n    ) {\n    }\n}\n\n#[Embeddable]\nclass DDC93Address\n{\n    #[Embedded(class: DDC93Country::class)]\n    public DDC93Country|null $country = null;\n\n    /**\n     * @param string|null $street\n     * @param string|null $zip\n     */\n    public function __construct(\n        #[Column(type: 'string', length: 255)]\n        public $street = null,\n        #[Column(type: 'string', length: 255)]\n        public $zip = null,\n        #[Column(type: 'string', length: 255)]\n        public string|null $city = null,\n        DDC93Country|null $country = null,\n    ) {\n        $this->country = $country;\n    }\n}\n\n#[Entity]\nclass DDC93Customer\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Embedded(class: 'DDC93ContactInfo', columnPrefix: 'contact_info_')]\n    private DDC93ContactInfo $contactInfo;\n}\n\n#[Embeddable]\nclass DDC93ContactInfo\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $email;\n\n    /** @var DDC93Address */\n    #[Embedded(class: 'DDC93Address')]\n    public $address;\n}\n\n#[Entity]\nclass DDC3028PersonWithPrefix\n{\n    public function __construct(\n        #[Embedded(class: 'DDC3028Id', columnPrefix: 'foobar_')]\n        public DDC3028Id|null $id = null,\n        #[Embedded(class: 'DDC3028NestedEmbeddable', columnPrefix: 'bloo_')]\n        public DDC3028NestedEmbeddable|null $nested = null,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC3028PersonEmptyPrefix\n{\n    public function __construct(\n        #[Embedded(class: 'DDC3028Id', columnPrefix: '')]\n        public DDC3028Id|null $id = null,\n        #[Embedded(class: 'DDC3028NestedEmbeddable', columnPrefix: '')]\n        public DDC3028NestedEmbeddable|null $nested = null,\n    ) {\n    }\n}\n\n#[Entity]\nclass DDC3028PersonPrefixFalse\n{\n    public function __construct(\n        #[Embedded(class: 'DDC3028Id', columnPrefix: false)]\n        public DDC3028Id|null $id = null,\n    ) {\n    }\n}\n\n#[Embeddable]\nclass DDC3028Id\n{\n    public function __construct(\n        #[Id]\n        #[Column(type: 'string', length: 255)]\n        public string|null $id = null,\n    ) {\n    }\n}\n\n#[Embeddable]\nclass DDC3028NestedEmbeddable\n{\n    public function __construct(\n        #[Embedded(class: 'DDC3028Id', columnPrefix: 'foo_')]\n        public DDC3028Id|null $nestedWithPrefix = null,\n        #[Embedded(class: 'DDC3028Id', columnPrefix: '')]\n        public DDC3028Id|null $nestedWithEmptyPrefix = null,\n        #[Embedded(class: 'DDC3028Id', columnPrefix: false)]\n        public DDC3028Id|null $nestedWithPrefixFalse = null,\n    ) {\n    }\n}\n\n#[MappedSuperclass]\nabstract class DDC3027Animal\n{\n    /** @var int */\n    #[Id]\n    #[GeneratedValue(strategy: 'AUTO')]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var DDC93Address */\n    #[Embedded(class: 'DDC93Address')]\n    public $address;\n}\n\n#[Entity]\nclass DDC3027Dog extends DDC3027Animal\n{\n}\n\n#[Embeddable]\nclass DDCInfiniteNestingEmbeddable\n{\n    /** @var DDCInfiniteNestingEmbeddable */\n    #[Embedded(class: 'DDCInfiniteNestingEmbeddable')]\n    public $nested;\n}\n\n#[Embeddable]\nclass DDCNestingEmbeddable1\n{\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id1;\n\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id2;\n\n    /** @var DDCNestingEmbeddable2 */\n    #[Embedded(class: 'DDCNestingEmbeddable2')]\n    public $nested;\n}\n\n#[Embeddable]\nclass DDCNestingEmbeddable2\n{\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id1;\n\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id2;\n\n    /** @var DDCNestingEmbeddable3 */\n    #[Embedded(class: 'DDCNestingEmbeddable3')]\n    public $nested;\n}\n\n#[Embeddable]\nclass DDCNestingEmbeddable3\n{\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id1;\n\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id2;\n\n    /** @var DDCNestingEmbeddable4 */\n    #[Embedded(class: 'DDCNestingEmbeddable4')]\n    public $nested;\n}\n\n#[Embeddable]\nclass DDCNestingEmbeddable4\n{\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id1;\n\n    /** @var DDC3028Id */\n    #[Embedded(class: 'DDC3028Id')]\n    public $id2;\n\n    /** @var DDCNestingEmbeddable1 */\n    #[Embedded(class: 'DDCNestingEmbeddable1')]\n    public $nested;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Functional/VersionedOneToOneTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Functional;\n\nuse Doctrine\\Tests\\Models\\VersionedOneToOne\\FirstRelatedEntity;\nuse Doctrine\\Tests\\Models\\VersionedOneToOne\\SecondRelatedEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Tests that an entity with a OneToOne relationship defined as the id, with a version field can be created.\n */\n#[Group('VersionedOneToOne')]\nclass VersionedOneToOneTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            FirstRelatedEntity::class,\n            SecondRelatedEntity::class,\n        );\n    }\n\n    /**\n     * This test case tests that a versionable entity, that has a oneToOne relationship as it's id can be created\n     *  without this bug fix (DDC-3318), you could not do this\n     */\n    public function testSetVersionOnCreate(): void\n    {\n        $secondRelatedEntity       = new SecondRelatedEntity();\n        $secondRelatedEntity->name = 'Bob';\n\n        $this->_em->persist($secondRelatedEntity);\n        $this->_em->flush();\n\n        $firstRelatedEntity               = new FirstRelatedEntity();\n        $firstRelatedEntity->name         = 'Fred';\n        $firstRelatedEntity->secondEntity = $secondRelatedEntity;\n\n        $this->_em->persist($firstRelatedEntity);\n        $this->_em->flush();\n\n        $firstEntity = $this->_em->getRepository(FirstRelatedEntity::class)\n            ->findOneBy(['name' => 'Fred']);\n\n        $secondEntity = $this->_em->getRepository(SecondRelatedEntity::class)\n            ->findOneBy(['name' => 'Bob']);\n\n        self::assertSame($firstRelatedEntity, $firstEntity);\n        self::assertSame($secondRelatedEntity, $secondEntity);\n        self::assertSame($firstEntity->secondEntity, $secondEntity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/AbstractHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse BackedEnum;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\Enums\\AccessLevel;\nuse Doctrine\\Tests\\Models\\Enums\\UserStatus;\nuse Doctrine\\Tests\\Models\\Hydration\\SimpleEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse LogicException;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\n\nuse function iterator_to_array;\n\n#[CoversClass(AbstractHydrator::class)]\nclass AbstractHydratorTest extends OrmFunctionalTestCase\n{\n    private EventManager $eventManager;\n    private Result&MockObject $mockResult;\n    private ResultSetMapping&MockObject $mockResultMapping;\n    private DummyHydrator $hydrator;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $mockConnection             = $this->createMock(Connection::class);\n        $mockEntityManagerInterface = $this->createMock(EntityManagerInterface::class);\n        $this->eventManager         = new EventManager();\n        $this->mockResult           = $this->createMock(Result::class);\n        $this->mockResultMapping    = $this->createMock(ResultSetMapping::class);\n\n        $mockConnection\n            ->method('getDatabasePlatform')\n            ->willReturn($this->createMock(AbstractPlatform::class));\n        $mockEntityManagerInterface\n            ->method('getEventManager')\n            ->willReturn($this->eventManager);\n        $mockEntityManagerInterface\n            ->method('getConnection')\n            ->willReturn($mockConnection);\n        $this->mockResult\n            ->method('fetchAssociative')\n            ->willReturn(false);\n\n        $this->hydrator = new DummyHydrator($mockEntityManagerInterface);\n    }\n\n    /**\n     * Verify that the number of added events to the event listener from the abstract hydrator class is equal to the\n     * number of removed events\n     */\n    #[Group('DDC-3146')]\n    #[Group('#1515')]\n    public function testOnClearEventListenerIsDetachedOnCleanup(): void\n    {\n        $iterator = $this->hydrator->toIterable($this->mockResult, $this->mockResultMapping);\n        iterator_to_array($iterator);\n        self::assertTrue($this->hydrator->hasListener);\n        self::assertFalse($this->eventManager->hasListeners(Events::onClear));\n    }\n\n    #[Group('#6623')]\n    public function testHydrateAllRegistersAndClearsAllAttachedListeners(): void\n    {\n        $this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping);\n        self::assertTrue($this->hydrator->hasListener);\n        self::assertFalse($this->eventManager->hasListeners(Events::onClear));\n    }\n\n    #[Group('#8482')]\n    public function testHydrateAllClearsAllAttachedListenersEvenOnError(): void\n    {\n        $this->hydrator->throwException = true;\n\n        $this->expectException(LogicException::class);\n        $this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping);\n        self::assertTrue($this->hydrator->hasListener);\n        self::assertFalse($this->eventManager->hasListeners(Events::onClear));\n    }\n\n    public function testEnumCastsIntegerBackedEnumValues(): void\n    {\n        $accessLevel = $this->hydrator->buildEnumForTesting('2', AccessLevel::class);\n        $userStatus  = $this->hydrator->buildEnumForTesting('active', UserStatus::class);\n\n        self::assertSame(AccessLevel::User, $accessLevel);\n        self::assertSame(UserStatus::Active, $userStatus);\n    }\n\n    public function testEnumCastsIntegerBackedEnumArrayValues(): void\n    {\n        $accessLevels = $this->hydrator->buildEnumForTesting(['1', '2'], AccessLevel::class);\n        $userStatus   = $this->hydrator->buildEnumForTesting(['active', 'inactive'], UserStatus::class);\n\n        self::assertSame([AccessLevel::Admin, AccessLevel::User], $accessLevels);\n        self::assertSame([UserStatus::Active, UserStatus::Inactive], $userStatus);\n    }\n\n    public function testToIterableIfYieldAndBreakBeforeFinishAlwaysCleansUp(): void\n    {\n        $this->setUpEntitySchema([SimpleEntity::class]);\n\n        $entity1 = new SimpleEntity();\n        $this->_em->persist($entity1);\n        $entity2 = new SimpleEntity();\n        $this->_em->persist($entity2);\n\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $evm = $this->_em->getEventManager();\n\n        $q = $this->_em->createQuery('SELECT e.id FROM ' . SimpleEntity::class . ' e');\n\n        // select two entities, but do no iterate\n        $q->toIterable();\n        self::assertCount(0, $evm->getListeners(Events::onClear));\n\n        // select two entities, but abort after first record\n        foreach ($q->toIterable() as $result) {\n            self::assertCount(1, $evm->getListeners(Events::onClear));\n            break;\n        }\n\n        self::assertCount(0, $evm->getListeners(Events::onClear));\n    }\n}\n\nclass DummyHydrator extends AbstractHydrator\n{\n    public bool $throwException = false;\n    public bool $hasListener    = false;\n\n    public function buildEnumForTesting(mixed $value, string $enumType): BackedEnum|array\n    {\n        return $this->buildEnum($value, $enumType);\n    }\n\n    /** @return array{} */\n    protected function hydrateAllData(): array\n    {\n        if ($this->throwException) {\n            throw new LogicException();\n        }\n\n        return [];\n    }\n\n    public function prepare(): void\n    {\n        $this->hasListener = $this->em->getEventManager()->hasListeners(Events::onClear);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/ArrayHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\Internal\\Hydration\\ArrayHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Forum\\ForumBoard;\nuse Doctrine\\Tests\\Models\\Forum\\ForumCategory;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass ArrayHydratorTest extends HydrationTestCase\n{\n    /** @phpstan-return list<array{int|string}> */\n    public static function provideDataForUserEntityResult(): array\n    {\n        return [\n            [0],\n            ['user'],\n            ['scalars'],\n            ['newObjects'],\n        ];\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testSimpleEntityQuery(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n\n        self::assertEquals(1, $result[0]['id']);\n        self::assertEquals('romanb', $result[0]['name']);\n\n        self::assertEquals(2, $result[1]['id']);\n        self::assertEquals('jwage', $result[1]['name']);\n    }\n\n    /**\n     * SELECT PARTIAL scalars.{id, name}, UPPER(scalars.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser scalars\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testSimpleEntityWithScalarQuery(int|string $userEntityKey): void\n    {\n        $alias = $userEntityKey ?: 'u';\n        $rsm   = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, $alias);\n        $rsm->addFieldResult($alias, 's__id', 'id');\n        $rsm->addFieldResult($alias, 's__name', 'name');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n\n        // Faked result set\n        $resultSet = [\n            [\n                's__id' => '1',\n                's__name' => 'romanb',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                's__id' => '2',\n                's__name' => 'jwage',\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n\n        self::assertArrayHasKey('nameUpper', $result[0]);\n        self::assertArrayNotHasKey('id', $result[0]);\n        self::assertArrayNotHasKey('name', $result[0]);\n\n        self::assertArrayHasKey(0, $result[0]);\n        self::assertArrayHasKey('id', $result[0][0]);\n        self::assertArrayHasKey('name', $result[0][0]);\n\n        self::assertArrayHasKey('nameUpper', $result[1]);\n        self::assertArrayNotHasKey('id', $result[1]);\n        self::assertArrayNotHasKey('name', $result[1]);\n\n        self::assertArrayHasKey(0, $result[1]);\n        self::assertArrayHasKey('id', $result[1][0]);\n        self::assertArrayHasKey('name', $result[1][0]);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name} AS user\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testSimpleEntityQueryWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertEquals(1, $result[0]['user']['id']);\n        self::assertEquals('romanb', $result[0]['user']['name']);\n\n        self::assertArrayHasKey('user', $result[1]);\n        self::assertEquals(2, $result[1]['user']['id']);\n        self::assertEquals('jwage', $result[1]['user']['name']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQuery(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addEntityResult(CmsArticle::class, 'a');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(4, $result);\n\n        self::assertEquals(1, $result[0]['id']);\n        self::assertEquals('romanb', $result[0]['name']);\n\n        self::assertEquals(1, $result[1]['id']);\n        self::assertEquals('Cool things.', $result[1]['topic']);\n\n        self::assertEquals(2, $result[2]['id']);\n        self::assertEquals('jwage', $result[2]['name']);\n\n        self::assertEquals(2, $result[3]['id']);\n        self::assertEquals('Cool things II.', $result[3]['topic']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name} AS user, PARTIAL a.{id, topic}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addEntityResult(CmsArticle::class, 'a');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(4, $result);\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertEquals(1, $result[0]['user']['id']);\n        self::assertEquals('romanb', $result[0]['user']['name']);\n\n        self::assertArrayHasKey(0, $result[1]);\n        self::assertEquals(1, $result[1][0]['id']);\n        self::assertEquals('Cool things.', $result[1][0]['topic']);\n\n        self::assertArrayHasKey('user', $result[2]);\n        self::assertEquals(2, $result[2]['user']['id']);\n        self::assertEquals('jwage', $result[2]['user']['name']);\n\n        self::assertArrayHasKey(0, $result[3]);\n        self::assertEquals(2, $result[3][0]['id']);\n        self::assertEquals('Cool things II.', $result[3][0]['topic']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic} AS article\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedArticleEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addEntityResult(CmsArticle::class, 'a', 'article');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(4, $result);\n\n        self::assertArrayHasKey(0, $result[0]);\n        self::assertEquals(1, $result[0][0]['id']);\n        self::assertEquals('romanb', $result[0][0]['name']);\n\n        self::assertArrayHasKey('article', $result[1]);\n        self::assertEquals(1, $result[1]['article']['id']);\n        self::assertEquals('Cool things.', $result[1]['article']['topic']);\n\n        self::assertArrayHasKey(0, $result[2]);\n        self::assertEquals(2, $result[2][0]['id']);\n        self::assertEquals('jwage', $result[2][0]['name']);\n\n        self::assertArrayHasKey('article', $result[3]);\n        self::assertEquals(2, $result[3]['article']['id']);\n        self::assertEquals('Cool things II.', $result[3]['article']['topic']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name} AS user, PARTIAL a.{id, topic} AS article\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addEntityResult(CmsArticle::class, 'a', 'article');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(4, $result);\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertEquals(1, $result[0]['user']['id']);\n        self::assertEquals('romanb', $result[0]['user']['name']);\n\n        self::assertArrayHasKey('article', $result[1]);\n        self::assertEquals(1, $result[1]['article']['id']);\n        self::assertEquals('Cool things.', $result[1]['article']['topic']);\n\n        self::assertArrayHasKey('user', $result[2]);\n        self::assertEquals(2, $result[2]['user']['id']);\n        self::assertEquals('jwage', $result[2]['user']['name']);\n\n        self::assertArrayHasKey('article', $result[3]);\n        self::assertEquals(2, $result[3]['article']['id']);\n        self::assertEquals('Cool things II.', $result[3]['article']['topic']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}, COUNT(p.phonenumber) AS numPhones\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   JOIN u.phonenumbers p\n     *  GROUP BY u.status, u.id\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryNormalJoin(int|string $userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'numPhones', 'integer');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => '2',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => '1',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        // first user => 2 phonenumbers\n        self::assertArrayHasKey($userEntityKey, $result[0]);\n        self::assertEquals(2, $result[0]['numPhones']);\n\n        // second user => 1 phonenumber\n        self::assertArrayHasKey($userEntityKey, $result[1]);\n        self::assertEquals(1, $result[1]['numPhones']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   JOIN u.phonenumbers p\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryFetchJoin(int|string $userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        // first user => 2 phonenumbers\n        self::assertCount(2, $result[0][$userEntityKey]['phonenumbers']);\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n\n        // second user => 1 phonenumber\n        self::assertCount(1, $result[1][$userEntityKey]['phonenumbers']);\n        self::assertEquals('JWAGE', $result[1]['nameUpper']);\n\n        self::assertEquals(42, $result[0][$userEntityKey]['phonenumbers'][0]['phonenumber']);\n        self::assertEquals(43, $result[0][$userEntityKey]['phonenumbers'][1]['phonenumber']);\n        self::assertEquals(91, $result[1][$userEntityKey]['phonenumbers'][0]['phonenumber']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}, UPPER(u.name) nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *        INDEX BY u.id\n     *   JOIN u.phonenumbers p\n     *        INDEX BY p.phonenumber\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryFetchJoinCustomIndex(int|string $userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addIndexBy('u', 'id');\n        $rsm->addIndexBy('p', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[1]);\n        self::assertIsArray($result[2]);\n\n        // test the scalar values\n        self::assertEquals('ROMANB', $result[1]['nameUpper']);\n        self::assertEquals('JWAGE', $result[2]['nameUpper']);\n\n        // first user => 2 phonenumbers. notice the custom indexing by user id\n        self::assertCount(2, $result[1][$userEntityKey]['phonenumbers']);\n\n        // second user => 1 phonenumber. notice the custom indexing by user id\n        self::assertCount(1, $result[2][$userEntityKey]['phonenumbers']);\n\n        // test the custom indexing of the phonenumbers\n        self::assertTrue(isset($result[1][$userEntityKey]['phonenumbers']['42']));\n        self::assertTrue(isset($result[1][$userEntityKey]['phonenumbers']['43']));\n        self::assertTrue(isset($result[2][$userEntityKey]['phonenumbers']['91']));\n    }\n\n    /**\n     * select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic\n     * from User u\n     * join u.phonenumbers p\n     * join u.articles a\n     * =\n     * select u.id, u.status, p.phonenumber, upper(u.name) AS u___0, a.id, a.topic\n     * from USERS u\n     * inner join PHONENUMBERS p ON u.id = p.user_id\n     * inner join ARTICLES a ON u.id = a.user_id\n     */\n    public function testMixedQueryMultipleFetchJoin(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '3',\n                'a__topic' => 'LINQ',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '4',\n                'a__topic' => 'PHP7',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n        // first user => 2 phonenumbers, 2 articles\n        self::assertCount(2, $result[0][0]['phonenumbers']);\n        self::assertCount(2, $result[0][0]['articles']);\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n        // second user => 1 phonenumber, 2 articles\n        self::assertCount(1, $result[1][0]['phonenumbers']);\n        self::assertCount(2, $result[1][0]['articles']);\n        self::assertEquals('JWAGE', $result[1]['nameUpper']);\n\n        self::assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);\n        self::assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);\n        self::assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);\n\n        self::assertEquals('Getting things done!', $result[0][0]['articles'][0]['topic']);\n        self::assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']);\n        self::assertEquals('LINQ', $result[1][0]['articles'][0]['topic']);\n        self::assertEquals('PHP7', $result[1][0]['articles'][1]['topic']);\n    }\n\n    /**\n     * select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic,\n     * c.id, c.topic\n     * from User u\n     * join u.phonenumbers p\n     * join u.articles a\n     * left join a.comments c\n     * =\n     * select u.id, u.status, p.phonenumber, upper(u.name) AS u___0, a.id, a.topic,\n     * c.id, c.topic\n     * from USERS u\n     * inner join PHONENUMBERS p ON u.id = p.user_id\n     * inner join ARTICLES a ON u.id = a.user_id\n     * left outer join COMMENTS c ON a.id = c.article_id\n     */\n    public function testMixedQueryMultipleDeepMixedFetchJoin(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsComment::class,\n            'c',\n            'a',\n            'comments',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n                'c__id' => '1',\n                'c__topic' => 'First!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n                'c__id' => '1',\n                'c__topic' => 'First!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '3',\n                'a__topic' => 'LINQ',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '4',\n                'a__topic' => 'PHP7',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        // first user => 2 phonenumbers, 2 articles, 1 comment on first article\n        self::assertCount(2, $result[0][0]['phonenumbers']);\n        self::assertCount(2, $result[0][0]['articles']);\n        self::assertCount(1, $result[0][0]['articles'][0]['comments']);\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n        // second user => 1 phonenumber, 2 articles, no comments\n        self::assertCount(1, $result[1][0]['phonenumbers']);\n        self::assertCount(2, $result[1][0]['articles']);\n        self::assertEquals('JWAGE', $result[1]['nameUpper']);\n\n        self::assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);\n        self::assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);\n        self::assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);\n\n        self::assertEquals('Getting things done!', $result[0][0]['articles'][0]['topic']);\n        self::assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']);\n        self::assertEquals('LINQ', $result[1][0]['articles'][0]['topic']);\n        self::assertEquals('PHP7', $result[1][0]['articles'][1]['topic']);\n\n        self::assertEquals('First!', $result[0][0]['articles'][0]['comments'][0]['topic']);\n\n        self::assertTrue(isset($result[0][0]['articles'][0]['comments']));\n\n        // empty comment collections\n        self::assertIsArray($result[0][0]['articles'][1]['comments']);\n        self::assertCount(0, $result[0][0]['articles'][1]['comments']);\n        self::assertIsArray($result[1][0]['articles'][0]['comments']);\n        self::assertCount(0, $result[1][0]['articles'][0]['comments']);\n        self::assertIsArray($result[1][0]['articles'][1]['comments']);\n        self::assertCount(0, $result[1][0]['articles'][1]['comments']);\n    }\n\n    /**\n     * Tests that the hydrator does not rely on a particular order of the rows\n     * in the result set.\n     *\n     * DQL:\n     * select c.id, c.position, c.name, b.id, b.position\n     * from \\Doctrine\\Tests\\Models\\Forum\\ForumCategory c inner join c.boards b\n     * order by c.position asc, b.position asc\n     *\n     * Checks whether the boards are correctly assigned to the categories.\n     *\n     * The 'evil' result set that confuses the object population is displayed below.\n     *\n     * c.id  | c.position | c.name   | boardPos | b.id | b.category_id (just for clarity)\n     *  1    | 0          | First    | 0        |   1  | 1\n     *  2    | 0          | Second   | 0        |   2  | 2   <--\n     *  1    | 0          | First    | 1        |   3  | 1\n     *  1    | 0          | First    | 2        |   4  | 1\n     */\n    public function testEntityQueryCustomResultSetOrder(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(ForumCategory::class, 'c');\n        $rsm->addJoinedEntityResult(\n            ForumBoard::class,\n            'b',\n            'c',\n            'boards',\n        );\n\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__position', 'position');\n        $rsm->addFieldResult('c', 'c__name', 'name');\n        $rsm->addFieldResult('b', 'b__id', 'id');\n        $rsm->addFieldResult('b', 'b__position', 'position');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '1',\n                'b__position' => '0',\n                //'b__category_id' => '1'\n            ],\n            [\n                'c__id' => '2',\n                'c__position' => '0',\n                'c__name' => 'Second',\n                'b__id' => '2',\n                'b__position' => '0',\n                //'b__category_id' => '2'\n            ],\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '3',\n                'b__position' => '1',\n                //'b__category_id' => '1'\n            ],\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '4',\n                'b__position' => '2',\n                //'b__category_id' => '1'\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n        self::assertTrue(isset($result[0]['boards']));\n        self::assertCount(3, $result[0]['boards']);\n        self::assertTrue(isset($result[1]['boards']));\n        self::assertCount(1, $result[1]['boards']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id,status}, a.id, a.topic, c.id as cid, c.topic as ctopic\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   LEFT JOIN u.articles a\n     *   LEFT JOIN a.comments c\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testChainedJoinWithScalars(int|string $entityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $entityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('a__id', 'id', 'integer');\n        $rsm->addScalarResult('a__topic', 'topic', 'string');\n        $rsm->addScalarResult('c__id', 'cid', 'integer');\n        $rsm->addScalarResult('c__topic', 'ctopic', 'string');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'a__id' => '1',\n                'a__topic' => 'The First',\n                'c__id' => '1',\n                'c__topic' => 'First Comment',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'a__id' => '1',\n                'a__topic' => 'The First',\n                'c__id' => '2',\n                'c__topic' => 'Second Comment',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'a__id' => '42',\n                'a__topic' => 'The Answer',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(3, $result);\n\n        self::assertCount(2, $result[0][$entityKey]); // User array\n        self::assertEquals(1, $result[0]['id']);\n        self::assertEquals('The First', $result[0]['topic']);\n        self::assertEquals(1, $result[0]['cid']);\n        self::assertEquals('First Comment', $result[0]['ctopic']);\n\n        self::assertCount(2, $result[1][$entityKey]); // User array, duplicated\n        self::assertEquals(1, $result[1]['id']); // duplicated\n        self::assertEquals('The First', $result[1]['topic']); // duplicated\n        self::assertEquals(2, $result[1]['cid']);\n        self::assertEquals('Second Comment', $result[1]['ctopic']);\n\n        self::assertCount(2, $result[2][$entityKey]); // User array, duplicated\n        self::assertEquals(42, $result[2]['id']);\n        self::assertEquals('The Answer', $result[2]['topic']);\n        self::assertNull($result[2]['cid']);\n        self::assertNull($result[2]['ctopic']);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testResultIteration(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $iterator = $hydrator->toIterable($stmt, $rsm);\n        $rowNum   = 0;\n\n        foreach ($iterator as $row) {\n            if ($rowNum === 0) {\n                self::assertEquals(1, $row['id']);\n                self::assertEquals('romanb', $row['name']);\n            } elseif ($rowNum === 1) {\n                self::assertEquals(2, $row['id']);\n                self::assertEquals('jwage', $row['name']);\n            }\n\n            ++$rowNum;\n        }\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testResultIterationWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $iterator = $hydrator->toIterable($stmt, $rsm);\n        $rowNum   = 0;\n\n        foreach ($iterator as $row) {\n            self::assertArrayHasKey('user', $row);\n\n            if ($rowNum === 0) {\n                self::assertEquals(1, $row['user']['id']);\n                self::assertEquals('romanb', $row['user']['name']);\n            } elseif ($rowNum === 1) {\n                self::assertEquals(2, $row['user']['id']);\n                self::assertEquals('jwage', $row['user']['name']);\n            }\n\n            ++$rowNum;\n        }\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, name}\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[Group('DDC-644')]\n    public function testSkipUnknownColumns(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'foo' => 'bar', // unknown!\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(1, $result);\n        self::assertArrayHasKey('id', $result[0]);\n        self::assertArrayHasKey('name', $result[0]);\n        self::assertArrayNotHasKey('foo', $result[0]);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1358')]\n    public function testMissingIdForRootEntity(int|string $userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => null,\n                'u__status' => null,\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n            ],\n            [\n                'u__id' => null,\n                'u__status' => null,\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(4, $result, 'Should hydrate four results.');\n\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n        self::assertEquals('ROMANB', $result[1]['nameUpper']);\n        self::assertEquals('JWAGE', $result[2]['nameUpper']);\n        self::assertEquals('JWAGE', $result[3]['nameUpper']);\n\n        self::assertEquals(['id' => 1, 'status' => 'developer'], $result[0][$userEntityKey]);\n        self::assertNull($result[1][$userEntityKey]);\n        self::assertEquals(['id' => 2, 'status' => 'developer'], $result[2][$userEntityKey]);\n        self::assertNull($result[3][$userEntityKey]);\n    }\n\n    /**\n     * SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *        INDEX BY u.id\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1385')]\n    public function testIndexByAndMixedResult(int|string $userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addIndexBy('u', 'id');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ArrayHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(2, $result);\n\n        self::assertArrayHasKey(1, $result);\n        self::assertEquals(1, $result[1][$userEntityKey]['id']);\n\n        self::assertArrayHasKey(2, $result);\n        self::assertEquals(2, $result[2][$userEntityKey]['id']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/CustomHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\n\nclass CustomHydratorTest extends HydrationTestCase\n{\n    public function testCustomHydrator(): void\n    {\n        $em     = $this->getTestEntityManager();\n        $config = $em->getConfiguration();\n        $config->addCustomHydrationMode('CustomHydrator', CustomHydrator::class);\n\n        $hydrator = $em->newHydrator('CustomHydrator');\n        self::assertInstanceOf(CustomHydrator::class, $hydrator);\n        self::assertNull($config->getCustomHydrationMode('does not exist'));\n    }\n}\n\nclass CustomHydrator extends AbstractHydrator\n{\n    /**\n     * {@inheritDoc}\n     */\n    protected function hydrateAllData(): array\n    {\n        return $this->_stmt->fetchAllAssociative();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/HydrationTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass HydrationTestCase extends OrmTestCase\n{\n    protected EntityManagerInterface $entityManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    protected function createResultMock(array $resultSet): Result\n    {\n        return ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->entityManager->getConnection());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/ObjectHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\ORM\\Internal\\Hydration\\HydrationException;\nuse Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceShipping;\nuse Doctrine\\Tests\\Models\\Forum\\ForumBoard;\nuse Doctrine\\Tests\\Models\\Forum\\ForumCategory;\nuse Doctrine\\Tests\\Models\\Hydration\\EntityWithArrayDefaultArrayValueM2M;\nuse Doctrine\\Tests\\Models\\Hydration\\SimpleEntity;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function count;\nuse function property_exists;\nuse function sys_get_temp_dir;\n\nclass ObjectHydratorTest extends HydrationTestCase\n{\n    /** @phpstan-return list<array{mixed}> */\n    public static function provideDataForUserEntityResult(): array\n    {\n        return [\n            [0],\n            ['user'],\n        ];\n    }\n\n    /** @phpstan-return list<array{mixed, mixed}> */\n    public static function provideDataForMultipleRootEntityResult(): array\n    {\n        return [\n            [0, 0],\n            ['user', 0],\n            [0, 'article'],\n            ['user', 'article'],\n        ];\n    }\n\n    /** @phpstan-return list<array{mixed}> */\n    public static function provideDataForProductEntityResult(): array\n    {\n        return [\n            [0],\n            ['product'],\n        ];\n    }\n\n    /**\n     * SELECT u\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testSimpleEntityQuery(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertInstanceOf(CmsUser::class, $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[1]);\n\n        self::assertEquals(1, $result[0]->id);\n        self::assertEquals('romanb', $result[0]->name);\n\n        self::assertEquals(2, $result[1]->id);\n        self::assertEquals('jwage', $result[1]->name);\n    }\n\n    /**\n     * SELECT u AS user\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testSimpleEntityQueryWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0]['user']);\n\n        self::assertArrayHasKey('user', $result[1]);\n        self::assertInstanceOf(CmsUser::class, $result[1]['user']);\n\n        self::assertEquals(1, $result[0]['user']->id);\n        self::assertEquals('romanb', $result[0]['user']->name);\n\n        self::assertEquals(2, $result[1]['user']->id);\n        self::assertEquals('jwage', $result[1]['user']->name);\n    }\n\n    /**\n     * SELECT u, a\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQuery(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addEntityResult(CmsArticle::class, 'a');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(4, count($result));\n\n        self::assertInstanceOf(CmsUser::class, $result[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[1]);\n        self::assertInstanceOf(CmsUser::class, $result[2]);\n        self::assertInstanceOf(CmsArticle::class, $result[3]);\n\n        self::assertEquals(1, $result[0]->id);\n        self::assertEquals('romanb', $result[0]->name);\n\n        self::assertEquals(1, $result[1]->id);\n        self::assertEquals('Cool things.', $result[1]->topic);\n\n        self::assertEquals(2, $result[2]->id);\n        self::assertEquals('jwage', $result[2]->name);\n\n        self::assertEquals(2, $result[3]->id);\n        self::assertEquals('Cool things II.', $result[3]->topic);\n    }\n\n    /**\n     * SELECT u AS user, a\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addEntityResult(CmsArticle::class, 'a');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(4, count($result));\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertArrayNotHasKey(0, $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0]['user']);\n        self::assertEquals(1, $result[0]['user']->id);\n        self::assertEquals('romanb', $result[0]['user']->name);\n\n        self::assertArrayHasKey(0, $result[1]);\n        self::assertArrayNotHasKey('user', $result[1]);\n        self::assertInstanceOf(CmsArticle::class, $result[1][0]);\n        self::assertEquals(1, $result[1][0]->id);\n        self::assertEquals('Cool things.', $result[1][0]->topic);\n\n        self::assertArrayHasKey('user', $result[2]);\n        self::assertArrayNotHasKey(0, $result[2]);\n        self::assertInstanceOf(CmsUser::class, $result[2]['user']);\n        self::assertEquals(2, $result[2]['user']->id);\n        self::assertEquals('jwage', $result[2]['user']->name);\n\n        self::assertArrayHasKey(0, $result[3]);\n        self::assertArrayNotHasKey('user', $result[3]);\n        self::assertInstanceOf(CmsArticle::class, $result[3][0]);\n        self::assertEquals(2, $result[3][0]->id);\n        self::assertEquals('Cool things II.', $result[3][0]->topic);\n    }\n\n    /**\n     * SELECT u, a AS article\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedArticleEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addEntityResult(CmsArticle::class, 'a', 'article');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(4, count($result));\n\n        self::assertArrayHasKey(0, $result[0]);\n        self::assertArrayNotHasKey('article', $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0][0]);\n        self::assertEquals(1, $result[0][0]->id);\n        self::assertEquals('romanb', $result[0][0]->name);\n\n        self::assertArrayHasKey('article', $result[1]);\n        self::assertArrayNotHasKey(0, $result[1]);\n        self::assertInstanceOf(CmsArticle::class, $result[1]['article']);\n        self::assertEquals(1, $result[1]['article']->id);\n        self::assertEquals('Cool things.', $result[1]['article']->topic);\n\n        self::assertArrayHasKey(0, $result[2]);\n        self::assertArrayNotHasKey('article', $result[2]);\n        self::assertInstanceOf(CmsUser::class, $result[2][0]);\n        self::assertEquals(2, $result[2][0]->id);\n        self::assertEquals('jwage', $result[2][0]->name);\n\n        self::assertArrayHasKey('article', $result[3]);\n        self::assertArrayNotHasKey(0, $result[3]);\n        self::assertInstanceOf(CmsArticle::class, $result[3]['article']);\n        self::assertEquals(2, $result[3]['article']->id);\n        self::assertEquals('Cool things II.', $result[3]['article']->topic);\n    }\n\n    /**\n     * SELECT u AS user, a AS article\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a\n     */\n    public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addEntityResult(CmsArticle::class, 'a', 'article');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'a__id' => '1',\n                'a__topic' => 'Cool things.',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'a__id' => '2',\n                'a__topic' => 'Cool things II.',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(4, count($result));\n\n        self::assertArrayHasKey('user', $result[0]);\n        self::assertArrayNotHasKey('article', $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0]['user']);\n        self::assertEquals(1, $result[0]['user']->id);\n        self::assertEquals('romanb', $result[0]['user']->name);\n\n        self::assertArrayHasKey('article', $result[1]);\n        self::assertArrayNotHasKey('user', $result[1]);\n        self::assertInstanceOf(CmsArticle::class, $result[1]['article']);\n        self::assertEquals(1, $result[1]['article']->id);\n        self::assertEquals('Cool things.', $result[1]['article']->topic);\n\n        self::assertArrayHasKey('user', $result[2]);\n        self::assertArrayNotHasKey('article', $result[2]);\n        self::assertInstanceOf(CmsUser::class, $result[2]['user']);\n        self::assertEquals(2, $result[2]['user']->id);\n        self::assertEquals('jwage', $result[2]['user']->name);\n\n        self::assertArrayHasKey('article', $result[3]);\n        self::assertArrayNotHasKey('user', $result[3]);\n        self::assertInstanceOf(CmsArticle::class, $result[3]['article']);\n        self::assertEquals(2, $result[3]['article']->id);\n        self::assertEquals('Cool things II.', $result[3]['article']->topic);\n    }\n\n    /**\n     * SELECT u, COUNT(p.phonenumber) numPhones\n     *   FROM User u\n     *   JOIN u.phonenumbers p\n     *  GROUP BY u.id\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryNormalJoin($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'numPhones', 'integer');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => '2',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => '1',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        // first user => 2 phonenumbers\n        self::assertEquals(2, $result[0]['numPhones']);\n        self::assertInstanceOf(CmsUser::class, $result[0][$userEntityKey]);\n\n        // second user => 1 phonenumber\n        self::assertEquals(1, $result[1]['numPhones']);\n        self::assertInstanceOf(CmsUser::class, $result[1][$userEntityKey]);\n    }\n\n    /**\n     * SELECT u, p, UPPER(u.name) nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   JOIN u.phonenumbers p\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryFetchJoin($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'p__phonenumber' => '42',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'p__phonenumber' => '43',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'p__phonenumber' => '91',\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        self::assertInstanceOf(CmsUser::class, $result[0][$userEntityKey]);\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[0]);\n\n        self::assertInstanceOf(CmsUser::class, $result[1][$userEntityKey]);\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[1]);\n\n        // first user => 2 phonenumbers\n        self::assertEquals(2, count($result[0][$userEntityKey]->phonenumbers));\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n\n        // second user => 1 phonenumber\n        self::assertEquals(1, count($result[1][$userEntityKey]->phonenumbers));\n        self::assertEquals('JWAGE', $result[1]['nameUpper']);\n\n        self::assertEquals(42, $result[0][$userEntityKey]->phonenumbers[0]->phonenumber);\n        self::assertEquals(43, $result[0][$userEntityKey]->phonenumbers[1]->phonenumber);\n        self::assertEquals(91, $result[1][$userEntityKey]->phonenumbers[0]->phonenumber);\n    }\n\n    /**\n     * SELECT u, p, UPPER(u.name) nameUpper\n     *   FROM User u\n     *        INDEX BY u.id\n     *   JOIN u.phonenumbers p\n     *        INDEX BY p.phonenumber\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addIndexBy('u', 'id');\n        $rsm->addIndexBy('p', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[1]);\n        self::assertIsArray($result[2]);\n\n        // test the scalar values\n        self::assertEquals('ROMANB', $result[1]['nameUpper']);\n        self::assertEquals('JWAGE', $result[2]['nameUpper']);\n\n        self::assertInstanceOf(CmsUser::class, $result[1][$userEntityKey]);\n        self::assertInstanceOf(CmsUser::class, $result[2][$userEntityKey]);\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->phonenumbers);\n\n        // first user => 2 phonenumbers. notice the custom indexing by user id\n        self::assertEquals(2, count($result[1][$userEntityKey]->phonenumbers));\n\n        // second user => 1 phonenumber. notice the custom indexing by user id\n        self::assertEquals(1, count($result[2][$userEntityKey]->phonenumbers));\n\n        // test the custom indexing of the phonenumbers\n        self::assertTrue(isset($result[1][$userEntityKey]->phonenumbers['42']));\n        self::assertTrue(isset($result[1][$userEntityKey]->phonenumbers['43']));\n        self::assertTrue(isset($result[2][$userEntityKey]->phonenumbers['91']));\n    }\n\n    /**\n     * SELECT u, p, UPPER(u.name) nameUpper, a\n     *   FROM User u\n     *   JOIN u.phonenumbers p\n     *   JOIN u.articles a\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryMultipleFetchJoin($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '3',\n                'a__topic' => 'LINQ',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '4',\n                'a__topic' => 'PHP7',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        self::assertInstanceOf(CmsUser::class, $result[0][$userEntityKey]);\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[0]);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[1]);\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->articles);\n        self::assertInstanceOf(CmsArticle::class, $result[0][$userEntityKey]->articles[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[0][$userEntityKey]->articles[1]);\n\n        self::assertInstanceOf(CmsUser::class, $result[1][$userEntityKey]);\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[1][$userEntityKey]->phonenumbers[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[1][$userEntityKey]->articles[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[1][$userEntityKey]->articles[1]);\n    }\n\n    /**\n     * SELECT u, p, UPPER(u.name) nameUpper, a, c\n     *   FROM User u\n     *   JOIN u.phonenumbers p\n     *   JOIN u.articles a\n     *   LEFT JOIN a.comments c\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testMixedQueryMultipleDeepMixedFetchJoin($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsComment::class,\n            'c',\n            'a',\n            'comments',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n                'c__id' => '1',\n                'c__topic' => 'First!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '1',\n                'a__topic' => 'Getting things done!',\n                'c__id' => '1',\n                'c__topic' => 'First!',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '43',\n                'a__id' => '2',\n                'a__topic' => 'ZendCon',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '3',\n                'a__topic' => 'LINQ',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n                'a__id' => '4',\n                'a__topic' => 'PHP7',\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result);\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        self::assertInstanceOf(CmsUser::class, $result[0][$userEntityKey]);\n        self::assertInstanceOf(CmsUser::class, $result[1][$userEntityKey]);\n\n        // phonenumbers\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[0]);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[0][$userEntityKey]->phonenumbers[1]);\n\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->phonenumbers);\n        self::assertInstanceOf(CmsPhonenumber::class, $result[1][$userEntityKey]->phonenumbers[0]);\n\n        // articles\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->articles);\n        self::assertInstanceOf(CmsArticle::class, $result[0][$userEntityKey]->articles[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[0][$userEntityKey]->articles[1]);\n\n        self::assertInstanceOf(CmsArticle::class, $result[1][$userEntityKey]->articles[0]);\n        self::assertInstanceOf(CmsArticle::class, $result[1][$userEntityKey]->articles[1]);\n\n        // article comments\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->articles[0]->comments);\n        self::assertInstanceOf(CmsComment::class, $result[0][$userEntityKey]->articles[0]->comments[0]);\n\n        // empty comment collections\n        self::assertInstanceOf(PersistentCollection::class, $result[0][$userEntityKey]->articles[1]->comments);\n        self::assertEquals(0, count($result[0][$userEntityKey]->articles[1]->comments));\n\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->articles[0]->comments);\n        self::assertEquals(0, count($result[1][$userEntityKey]->articles[0]->comments));\n        self::assertInstanceOf(PersistentCollection::class, $result[1][$userEntityKey]->articles[1]->comments);\n        self::assertEquals(0, count($result[1][$userEntityKey]->articles[1]->comments));\n    }\n\n    /**\n     * Tests that the hydrator does not rely on a particular order of the rows\n     * in the result set.\n     *\n     * DQL:\n     * select c, b from Doctrine\\Tests\\Models\\Forum\\ForumCategory c inner join c.boards b\n     * order by c.position asc, b.position asc\n     *\n     * Checks whether the boards are correctly assigned to the categories.\n     *\n     * The 'evil' result set that confuses the object population is displayed below.\n     *\n     * c.id  | c.position | c.name   | boardPos | b.id | b.category_id (just for clarity)\n     *  1    | 0          | First    | 0        |   1  | 1\n     *  2    | 0          | Second   | 0        |   2  | 2   <--\n     *  1    | 0          | First    | 1        |   3  | 1\n     *  1    | 0          | First    | 2        |   4  | 1\n     */\n    public function testEntityQueryCustomResultSetOrder(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(ForumCategory::class, 'c');\n        $rsm->addJoinedEntityResult(\n            ForumBoard::class,\n            'b',\n            'c',\n            'boards',\n        );\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__position', 'position');\n        $rsm->addFieldResult('c', 'c__name', 'name');\n        $rsm->addFieldResult('b', 'b__id', 'id');\n        $rsm->addFieldResult('b', 'b__position', 'position');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '1',\n                'b__position' => '0',\n                //'b__category_id' => '1'\n            ],\n            [\n                'c__id' => '2',\n                'c__position' => '0',\n                'c__name' => 'Second',\n                'b__id' => '2',\n                'b__position' => '0',\n                //'b__category_id' => '2'\n            ],\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '3',\n                'b__position' => '1',\n                //'b__category_id' => '1'\n            ],\n            [\n                'c__id' => '1',\n                'c__position' => '0',\n                'c__name' => 'First',\n                'b__id' => '4',\n                'b__position' => '2',\n                //'b__category_id' => '1'\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertInstanceOf(ForumCategory::class, $result[0]);\n        self::assertInstanceOf(ForumCategory::class, $result[1]);\n\n        self::assertTrue($result[0] !== $result[1]);\n\n        self::assertEquals(1, $result[0]->getId());\n        self::assertEquals(2, $result[1]->getId());\n\n        self::assertTrue(property_exists($result[0], 'boards'));\n        self::assertEquals(3, count($result[0]->boards));\n\n        self::assertTrue(property_exists($result[1], 'boards'));\n        self::assertEquals(1, count($result[1]->boards));\n    }\n\n    /**\n     * SELECT u\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[Group('DDC-644')]\n    public function testSkipUnknownColumns(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'foo' => 'bar', // unknown!\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(1, count($result));\n        self::assertInstanceOf(CmsUser::class, $result[0]);\n    }\n\n    /**\n     * SELECT u.id, u.name\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    public function testScalarQueryWithoutResultVariables($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addScalarResult('sclr0', 'id', 'integer');\n        $rsm->addScalarResult('sclr1', 'name', 'string');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'sclr0' => '1',\n                'sclr1' => 'romanb',\n            ],\n            [\n                'sclr0' => '2',\n                'sclr1' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result[0]);\n        self::assertIsArray($result[1]);\n\n        self::assertEquals(1, $result[0]['id']);\n        self::assertEquals('romanb', $result[0]['name']);\n\n        self::assertEquals(2, $result[1]['id']);\n        self::assertEquals('jwage', $result[1]['name']);\n    }\n\n    /**\n     * SELECT p\n     *   FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p\n     */\n    public function testCreatesProxyForLazyLoadingWithForeignKeys(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(ECommerceProduct::class, 'p');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'p__name', 'name');\n        $rsm->addMetaResult('p', 'p__shipping_id', 'shipping_id', false, 'integer');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'p__id' => '1',\n                'p__name' => 'Doctrine Book',\n                'p__shipping_id' => 42,\n            ],\n        ];\n\n        // extending the proxy factory to spy on getProxy()\n        $proxyFactory = new class (\n            $this->entityManager,\n            sys_get_temp_dir(),\n            'Proxies',\n            ProxyFactory::AUTOGENERATE_ALWAYS,\n        ) extends ProxyFactory {\n            public function getProxy(string $className, array $identifier): object\n            {\n                TestCase::assertSame(ECommerceShipping::class, $className);\n                TestCase::assertSame(['id' => 42], $identifier);\n\n                return parent::getProxy($className, $identifier);\n            }\n        };\n\n        $this->entityManager->setProxyFactory($proxyFactory);\n\n        // configuring lazy loading\n        $metadata                                         = $this->entityManager->getClassMetadata(ECommerceProduct::class);\n        $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(1, count($result));\n\n        self::assertInstanceOf(ECommerceProduct::class, $result[0]);\n    }\n\n    /**\n     * SELECT p AS product\n     *   FROM Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct p\n     */\n    public function testCreatesProxyForLazyLoadingWithForeignKeysWithAliasedProductEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(ECommerceProduct::class, 'p', 'product');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'p__name', 'name');\n        $rsm->addMetaResult('p', 'p__shipping_id', 'shipping_id', false, 'integer');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'p__id' => '1',\n                'p__name' => 'Doctrine Book',\n                'p__shipping_id' => 42,\n            ],\n        ];\n\n        $proxyInstance = new ECommerceShipping();\n\n        // extending the proxy factory to spy on getProxy()\n        $proxyFactory = new class (\n            $this->entityManager,\n            sys_get_temp_dir(),\n            'Proxies',\n            ProxyFactory::AUTOGENERATE_ALWAYS,\n        ) extends ProxyFactory {\n            public function getProxy(string $className, array $identifier): object\n            {\n                TestCase::assertSame(ECommerceShipping::class, $className);\n                TestCase::assertSame(['id' => 42], $identifier);\n\n                return parent::getProxy($className, $identifier);\n            }\n        };\n\n        $this->entityManager->setProxyFactory($proxyFactory);\n\n        // configuring lazy loading\n        $metadata                                         = $this->entityManager->getClassMetadata(ECommerceProduct::class);\n        $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY;\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(1, count($result));\n\n        self::assertIsArray($result[0]);\n        self::assertInstanceOf(ECommerceProduct::class, $result[0]['product']);\n    }\n\n    /**\n     * SELECT u, a, c\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   LEFT JOIN u.articles a\n     *   LEFT JOIN a.comments c\n     */\n    public function testChainedJoinWithEmptyCollections(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsComment::class,\n            'c',\n            'a',\n            'comments',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'a__id' => null,\n                'a__topic' => null,\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'a__id' => null,\n                'a__topic' => null,\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertInstanceOf(CmsUser::class, $result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[1]);\n\n        self::assertEquals(0, $result[0]->articles->count());\n        self::assertEquals(0, $result[1]->articles->count());\n    }\n\n    /**\n     * SELECT u AS user, a, c\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   LEFT JOIN u.articles a\n     *   LEFT JOIN a.comments c\n     */\n    public function testChainedJoinWithEmptyCollectionsWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addJoinedEntityResult(\n            CmsArticle::class,\n            'a',\n            'u',\n            'articles',\n        );\n        $rsm->addJoinedEntityResult(\n            CmsComment::class,\n            'c',\n            'a',\n            'comments',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__topic', 'topic');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addFieldResult('c', 'c__topic', 'topic');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'a__id' => null,\n                'a__topic' => null,\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'a__id' => null,\n                'a__topic' => null,\n                'c__id' => null,\n                'c__topic' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0]['user']);\n\n        self::assertIsArray($result[1]);\n        self::assertInstanceOf(CmsUser::class, $result[1]['user']);\n\n        self::assertEquals(0, $result[0]['user']->articles->count());\n        self::assertEquals(0, $result[1]['user']->articles->count());\n    }\n\n    /**\n     * SELECT u\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testResultIteration(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $hydrator = new ObjectHydrator($this->entityManager);\n\n        $iterableResult = $hydrator->toIterable(\n            $this->createResultMock($resultSet),\n            $rsm,\n        );\n        $rowNum         = 0;\n\n        foreach ($iterableResult as $row) {\n            self::assertInstanceOf(CmsUser::class, $row);\n\n            if ($rowNum === 0) {\n                self::assertEquals(1, $row->id);\n                self::assertEquals('romanb', $row->name);\n            } elseif ($rowNum === 1) {\n                self::assertEquals(2, $row->id);\n                self::assertEquals('jwage', $row->name);\n            }\n\n            ++$rowNum;\n        }\n\n        self::assertSame(2, $rowNum);\n\n        $iterableResult = $hydrator->toIterable(\n            $this->createResultMock($resultSet),\n            $rsm,\n        );\n        $rowNum         = 0;\n\n        foreach ($iterableResult as $user) {\n            self::assertInstanceOf(CmsUser::class, $user);\n\n            if ($rowNum === 0) {\n                self::assertEquals(1, $user->id);\n                self::assertEquals('romanb', $user->name);\n            }\n\n            if ($rowNum === 1) {\n                self::assertEquals(2, $user->id);\n                self::assertEquals('jwage', $user->name);\n            }\n\n            ++$rowNum;\n        }\n\n        self::assertSame(2, $rowNum);\n    }\n\n    /**\n     * SELECT u\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    public function testResultIterationWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $hydrator       = new ObjectHydrator($this->entityManager);\n        $rowNum         = 0;\n        $iterableResult = $hydrator->toIterable(\n            ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class)),\n            $rsm,\n        );\n\n        foreach ($iterableResult as $row) {\n            self::assertCount(1, $row);\n            self::assertArrayHasKey('user', $row);\n            self::assertInstanceOf(CmsUser::class, $row['user']);\n\n            if ($rowNum === 0) {\n                self::assertEquals(1, $row['user']->id);\n                self::assertEquals('romanb', $row['user']->name);\n            } elseif ($rowNum === 1) {\n                self::assertEquals(2, $row['user']->id);\n                self::assertEquals('jwage', $row['user']->name);\n            }\n\n            ++$rowNum;\n        }\n\n        self::assertSame(2, $rowNum);\n\n        $rowNum         = 0;\n        $iterableResult = $hydrator->toIterable(\n            $this->createResultMock($resultSet),\n            $rsm,\n        );\n\n        foreach ($iterableResult as $row) {\n            self::assertCount(1, $row);\n            self::assertArrayHasKey('user', $row);\n            self::assertInstanceOf(CmsUser::class, $row['user']);\n\n            if ($rowNum === 0) {\n                self::assertEquals(1, $row['user']->id);\n                self::assertEquals('romanb', $row['user']->name);\n            }\n\n            if ($rowNum === 1) {\n                self::assertEquals(2, $row['user']->id);\n                self::assertEquals('jwage', $row['user']->name);\n            }\n\n            ++$rowNum;\n        }\n\n        self::assertSame(2, $rowNum);\n    }\n\n    /**\n     * Checks if multiple joined multiple-valued collections is hydrated correctly.\n     *\n     * SELECT u, g, p\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[Group('DDC-809')]\n    public function testManyToManyHydration(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addJoinedEntityResult(CmsGroup::class, 'g', 'u', 'groups');\n        $rsm->addFieldResult('g', 'g__id', 'id');\n        $rsm->addFieldResult('g', 'g__name', 'name');\n        $rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 1111,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 1111,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 2222,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 2222,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '2',\n                'g__name' => 'TestGroupA',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '4',\n                'g__name' => 'TestGroupC',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '2',\n                'g__name' => 'TestGroupA',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '4',\n                'g__name' => 'TestGroupC',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 4444,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertContainsOnly(CmsUser::class, $result);\n\n        self::assertEquals(2, count($result[0]->groups));\n        self::assertEquals(2, count($result[0]->phonenumbers));\n\n        self::assertEquals(4, count($result[1]->groups));\n        self::assertEquals(2, count($result[1]->phonenumbers));\n    }\n\n    /**\n     * Checks if multiple joined multiple-valued collections is hydrated correctly.\n     *\n     * SELECT u As user, g, p\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[Group('DDC-809')]\n    public function testManyToManyHydrationWithAliasedUserEntity(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', 'user');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addJoinedEntityResult(CmsGroup::class, 'g', 'u', 'groups');\n        $rsm->addFieldResult('g', 'g__id', 'id');\n        $rsm->addFieldResult('g', 'g__name', 'name');\n        $rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 1111,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 1111,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 2222,\n            ],\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 2222,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '2',\n                'g__name' => 'TestGroupA',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '4',\n                'g__name' => 'TestGroupC',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 3333,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '2',\n                'g__name' => 'TestGroupA',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '3',\n                'g__name' => 'TestGroupB',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '4',\n                'g__name' => 'TestGroupC',\n                'p__phonenumber' => 4444,\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n                'g__id' => '5',\n                'g__name' => 'TestGroupD',\n                'p__phonenumber' => 4444,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertIsArray($result[0]);\n        self::assertInstanceOf(CmsUser::class, $result[0]['user']);\n        self::assertIsArray($result[1]);\n        self::assertInstanceOf(CmsUser::class, $result[1]['user']);\n\n        self::assertEquals(2, count($result[0]['user']->groups));\n        self::assertEquals(2, count($result[0]['user']->phonenumbers));\n\n        self::assertEquals(4, count($result[1]['user']->groups));\n        self::assertEquals(2, count($result[1]['user']->phonenumbers));\n    }\n\n    /**\n     * SELECT u, UPPER(u.name) as nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1358')]\n    public function testMissingIdForRootEntity($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => null,\n                'u__status' => null,\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n            ],\n            [\n                'u__id' => null,\n                'u__status' => null,\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(4, count($result), 'Should hydrate four results.');\n\n        self::assertEquals('ROMANB', $result[0]['nameUpper']);\n        self::assertEquals('ROMANB', $result[1]['nameUpper']);\n        self::assertEquals('JWAGE', $result[2]['nameUpper']);\n        self::assertEquals('JWAGE', $result[3]['nameUpper']);\n\n        self::assertInstanceOf(CmsUser::class, $result[0][$userEntityKey]);\n        self::assertNull($result[1][$userEntityKey]);\n\n        self::assertInstanceOf(CmsUser::class, $result[2][$userEntityKey]);\n        self::assertNull($result[3][$userEntityKey]);\n    }\n\n    /**\n     * SELECT u, p, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   LEFT JOIN u.phonenumbers u\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1358')]\n    public function testMissingIdForCollectionValuedChildEntity($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsPhonenumber::class,\n            'p',\n            'u',\n            'phonenumbers',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => '42',\n            ],\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'p__phonenumber' => null,\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => '91',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n                'p__phonenumber' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertEquals(1, $result[0][$userEntityKey]->phonenumbers->count());\n        self::assertEquals(1, $result[1][$userEntityKey]->phonenumbers->count());\n    }\n\n    /**\n     * SELECT u, a, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *   JOIN u.address a\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1358')]\n    public function testMissingIdForSingleValuedChildEntity($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addJoinedEntityResult(\n            CmsAddress::class,\n            'a',\n            'u',\n            'address',\n        );\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__city', 'city');\n        $rsm->addMetaResult('a', 'user_id', 'user_id', false, 'string');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n                'a__id' => 1,\n                'a__city' => 'Berlin',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'BENJAMIN',\n                'a__id' => null,\n                'a__city' => null,\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertInstanceOf(CmsAddress::class, $result[0][$userEntityKey]->address);\n        self::assertNull($result[1][$userEntityKey]->address);\n    }\n\n    /**\n     * SELECT u, UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     *        INDEX BY u.id\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1385')]\n    public function testIndexByAndMixedResult($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__status', 'status');\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addIndexBy('u', 'id');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            [\n                'u__id' => '1',\n                'u__status' => 'developer',\n                'sclr0' => 'ROMANB',\n            ],\n            [\n                'u__id' => '2',\n                'u__status' => 'developer',\n                'sclr0' => 'JWAGE',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(2, count($result));\n\n        self::assertArrayHasKey(1, $result);\n        self::assertEquals(1, $result[1][$userEntityKey]->id);\n\n        self::assertArrayHasKey(2, $result);\n        self::assertEquals(2, $result[2][$userEntityKey]->id);\n    }\n\n    /**\n     * SELECT UPPER(u.name) AS nameUpper\n     *   FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\n     */\n    #[DataProvider('provideDataForUserEntityResult')]\n    #[Group('DDC-1385')]\n    public function testIndexByScalarsOnly($userEntityKey): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u', $userEntityKey ?: null);\n        $rsm->addScalarResult('sclr0', 'nameUpper', 'string');\n        $rsm->addIndexByScalar('sclr0');\n\n        // Faked result set\n        $resultSet = [\n            //row1\n            ['sclr0' => 'ROMANB'],\n            ['sclr0' => 'JWAGE'],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertEquals(\n            [\n                'ROMANB' => ['nameUpper' => 'ROMANB'],\n                'JWAGE'  => ['nameUpper' => 'JWAGE'],\n            ],\n            $result,\n        );\n    }\n\n    #[Group('DDC-1470')]\n    public function testMissingMetaMappingException(): void\n    {\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage('The meta mapping for the discriminator column \"c_discr\" is missing for \"Doctrine\\Tests\\Models\\Company\\CompanyFixContract\" using the DQL alias \"c\".');\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CompanyFixContract::class, 'c');\n        $rsm->addJoinedEntityResult(CompanyEmployee::class, 'e', 'c', 'salesPerson');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->setDiscriminatorColumn('c', 'c_discr');\n\n        $resultSet = [\n            [\n                'c__id'   => '1',\n                'c_discr' => 'fix',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    #[Group('DDC-1470')]\n    public function testMissingDiscriminatorColumnException(): void\n    {\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage('The discriminator column \"discr\" is missing for \"Doctrine\\Tests\\Models\\Company\\CompanyEmployee\" using the DQL alias \"e\".');\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CompanyFixContract::class, 'c');\n        $rsm->addJoinedEntityResult(CompanyEmployee::class, 'e', 'c', 'salesPerson');\n        $rsm->addFieldResult('c', 'c__id', 'id');\n        $rsm->addMetaResult('c', 'c_discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('c', 'c_discr');\n        $rsm->addFieldResult('e', 'e__id', 'id');\n        $rsm->addFieldResult('e', 'e__name', 'name');\n        $rsm->addMetaResult('e ', 'e_discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('e', 'e_discr');\n\n        $resultSet = [\n            [\n                'c__id'   => '1',\n                'c_discr' => 'fix',\n                'e__id'   => '1',\n                'e__name' => 'Fabio B. Silva',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    #[Group('DDC-3076')]\n    public function testInvalidDiscriminatorValueException(): void\n    {\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage('The discriminator value \"subworker\" is invalid. It must be one of \"person\", \"manager\", \"employee\".');\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CompanyPerson::class, 'p');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'p__name', 'name');\n        $rsm->addMetaResult('p', 'discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('p', 'discr');\n\n        $resultSet = [\n            [\n                'p__id'   => '1',\n                'p__name' => 'Fabio B. Silva',\n                'discr'   => 'subworker',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    public function testFetchJoinCollectionValuedAssociationWithDefaultArrayValue(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(EntityWithArrayDefaultArrayValueM2M::class, 'e1', null);\n        $rsm->addJoinedEntityResult(SimpleEntity::class, 'e2', 'e1', 'collection');\n        $rsm->addFieldResult('e1', 'a1__id', 'id');\n        $rsm->addFieldResult('e2', 'e2__id', 'id');\n\n        $resultSet = [\n            [\n                'a1__id' => '1',\n                'e2__id' => '1',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertCount(1, $result);\n        self::assertInstanceOf(EntityWithArrayDefaultArrayValueM2M::class, $result[0]);\n        self::assertInstanceOf(PersistentCollection::class, $result[0]->collection);\n        self::assertCount(1, $result[0]->collection);\n        self::assertInstanceOf(SimpleEntity::class, $result[0]->collection[0]);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/ResultSetMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUser;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Description of ResultSetMappingTest\n */\nclass ResultSetMappingTest extends OrmTestCase\n{\n    private ResultSetMapping $_rsm;\n    private EntityManagerInterface $entityManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->_rsm          = new ResultSetMapping();\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    /**\n     * For SQL: SELECT id, status, username, name FROM cms_users\n     */\n    public function testBasicResultSetMapping(): void\n    {\n        $this->_rsm->addEntityResult(\n            CmsUser::class,\n            'u',\n        );\n        $this->_rsm->addFieldResult('u', 'id', 'id');\n        $this->_rsm->addFieldResult('u', 'status', 'status');\n        $this->_rsm->addFieldResult('u', 'username', 'username');\n        $this->_rsm->addFieldResult('u', 'name', 'name');\n\n        self::assertFalse($this->_rsm->isScalarResult('id'));\n        self::assertFalse($this->_rsm->isScalarResult('status'));\n        self::assertFalse($this->_rsm->isScalarResult('username'));\n        self::assertFalse($this->_rsm->isScalarResult('name'));\n\n        self::assertEquals($this->_rsm->getClassName('u'), CmsUser::class);\n        $class = $this->_rsm->getDeclaringClass('id');\n        self::assertEquals($class, CmsUser::class);\n\n        self::assertEquals('u', $this->_rsm->getEntityAlias('id'));\n        self::assertEquals('u', $this->_rsm->getEntityAlias('status'));\n        self::assertEquals('u', $this->_rsm->getEntityAlias('username'));\n        self::assertEquals('u', $this->_rsm->getEntityAlias('name'));\n\n        self::assertEquals('id', $this->_rsm->getFieldName('id'));\n        self::assertEquals('status', $this->_rsm->getFieldName('status'));\n        self::assertEquals('username', $this->_rsm->getFieldName('username'));\n        self::assertEquals('name', $this->_rsm->getFieldName('name'));\n    }\n\n    /**\n     * Fluent interface test, not a real result set mapping\n     */\n    #[Group('DDC-1057')]\n    public function testFluentInterface(): void\n    {\n        $rms = $this->_rsm;\n\n        $this->_rsm->addEntityResult(CmsUser::class, 'u');\n        $this->_rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers');\n        $this->_rsm->addFieldResult('u', 'id', 'id');\n        $this->_rsm->addFieldResult('u', 'name', 'name');\n        $this->_rsm->setDiscriminatorColumn('name', 'name');\n        $this->_rsm->addIndexByColumn('id', 'id');\n        $this->_rsm->addIndexBy('username', 'username');\n        $this->_rsm->addIndexByScalar('sclr0');\n        $this->_rsm->addScalarResult('sclr0', 'numPhones');\n        $this->_rsm->addMetaResult('a', 'user_id', 'user_id');\n\n        self::assertTrue($rms->hasIndexBy('id'));\n        self::assertTrue($rms->isFieldResult('id'));\n        self::assertTrue($rms->isFieldResult('name'));\n        self::assertTrue($rms->isScalarResult('sclr0'));\n        self::assertTrue($rms->isRelation('p'));\n        self::assertTrue($rms->hasParentAlias('p'));\n        self::assertTrue($rms->isMixedResult());\n    }\n\n    #[Group('DDC-117')]\n    public function testIndexByMetadataColumn(): void\n    {\n        $this->_rsm->addEntityResult(LegacyUser::class, 'u');\n        $this->_rsm->addJoinedEntityResult(LegacyUserReference::class, 'lu', 'u', '_references');\n        $this->_rsm->addMetaResult('lu', '_source', '_source', true, 'integer');\n        $this->_rsm->addMetaResult('lu', '_target', '_target', true, 'integer');\n        $this->_rsm->addIndexBy('lu', '_source');\n\n        self::assertTrue($this->_rsm->hasIndexBy('lu'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/ScalarColumnHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\Exception\\MultipleSelectorsFoundException;\nuse Doctrine\\ORM\\Internal\\Hydration\\ScalarColumnHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\n\nuse function sprintf;\n\nclass ScalarColumnHydratorTest extends HydrationTestCase\n{\n    /**\n     * Select u.id from CmsUser u\n     */\n    public function testEmptyResultTest(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n\n        $stmt     = $this->createResultMock([]);\n        $hydrator = new ScalarColumnHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        $this->assertIsArray($result);\n        $this->assertEmpty($result);\n    }\n\n    /**\n     * Select u.id from CmsUser u\n     */\n    public function testSingleColumnEntityQueryWithoutScalarMap(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n\n        $resultSet = [\n            ['u__id' => '1'],\n            ['u__id' => '2'],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarColumnHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        $this->assertIsArray($result);\n        $this->assertCount(2, $result);\n\n        $this->assertEquals(1, $result[0]);\n        $this->assertEquals(2, $result[1]);\n    }\n\n    /**\n     * Select u.id from CmsUser u\n     */\n    public function testSingleColumnEntityQueryWithScalarMap(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addScalarResult('sclr0', 'id');\n        $rsm->addIndexByScalar('sclr0');\n\n        $resultSet = [\n            ['u__id' => '1'],\n            ['u__id' => '2'],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarColumnHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n\n        $this->assertIsArray($result);\n        $this->assertCount(2, $result);\n\n        $this->assertEquals(1, $result[0]);\n        $this->assertEquals(2, $result[1]);\n    }\n\n    /**\n     * Select u.id, u.name from CmsUser u\n     */\n    public function testMultipleColumnEntityQueryThrowsException(): void\n    {\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        $resultSet = [\n            [\n                'u__id'   => '1',\n                'u__name' => 'Gregoire',\n            ],\n            [\n                'u__id'   => '2',\n                'u__name' => 'Bhushan',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarColumnHydrator($this->entityManager);\n\n        $this->expectException(MultipleSelectorsFoundException::class);\n        $this->expectExceptionMessage(sprintf(\n            MultipleSelectorsFoundException::MULTIPLE_SELECTORS_FOUND_EXCEPTION,\n            'id, name',\n        ));\n\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/ScalarHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\Internal\\Hydration\\ScalarHydrator;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass ScalarHydratorTest extends HydrationTestCase\n{\n    /**\n     * Select u.id, u.name from CmsUser u\n     */\n    public function testNewHydrationSimpleEntityQuery(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        // Faked result set\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n            ],\n            [\n                'u__id' => '2',\n                'u__name' => 'jwage',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarHydrator($this->entityManager);\n\n        $result = $hydrator->hydrateAll($stmt, $rsm);\n\n        self::assertIsArray($result);\n        self::assertCount(2, $result);\n        self::assertEquals('romanb', $result[0]['u_name']);\n        self::assertEquals(1, $result[0]['u_id']);\n        self::assertEquals('jwage', $result[1]['u_name']);\n        self::assertEquals(2, $result[1]['u_id']);\n    }\n\n    #[Group('DDC-407')]\n    public function testHydrateScalarResults(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addScalarResult('foo1', 'foo', 'string');\n        $rsm->addScalarResult('bar2', 'bar', 'string');\n        $rsm->addScalarResult('baz3', 'baz', 'string');\n\n        $resultSet = [\n            [\n                'foo1' => 'A',\n                'bar2' => 'B',\n                'baz3' => 'C',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarHydrator($this->entityManager);\n\n        self::assertCount(1, $hydrator->hydrateAll($stmt, $rsm));\n    }\n\n    #[Group('DDC-644')]\n    public function testSkipUnknownColumns(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n        $rsm->addScalarResult('foo1', 'foo', 'string');\n        $rsm->addScalarResult('bar2', 'bar', 'string');\n        $rsm->addScalarResult('baz3', 'baz', 'string');\n\n        $resultSet = [\n            [\n                'u__id' => '1',\n                'u__name' => 'romanb',\n                'foo1' => 'A',\n                'bar2' => 'B',\n                'baz3' => 'C',\n                'foo' => 'bar', // Unknown!\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new ScalarHydrator($this->entityManager);\n\n        self::assertCount(1, $hydrator->hydrateAll($stmt, $rsm));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/SimpleObjectHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Internal\\Hydration\\HydrationException;\nuse Doctrine\\ORM\\Internal\\Hydration\\SimpleObjectHydrator;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\DbalTypes\\GH8565EmployeePayloadType;\nuse Doctrine\\Tests\\DbalTypes\\GH8565ManagerPayloadType;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\Enums\\Scale;\nuse Doctrine\\Tests\\Models\\Enums\\Unit;\nuse Doctrine\\Tests\\Models\\GH8565\\GH8565Employee;\nuse Doctrine\\Tests\\Models\\GH8565\\GH8565Manager;\nuse Doctrine\\Tests\\Models\\GH8565\\GH8565Person;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Employee;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Manager;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Person;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass SimpleObjectHydratorTest extends HydrationTestCase\n{\n    #[Group('DDC-1470')]\n    public function testMissingDiscriminatorColumnException(): void\n    {\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage('The discriminator column \"discr\" is missing for \"Doctrine\\Tests\\Models\\Company\\CompanyPerson\" using the DQL alias \"p\".');\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CompanyPerson::class, 'p');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'p__name', 'name');\n        $rsm->addMetaResult('p ', 'discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('p', 'discr');\n        $resultSet = [\n            [\n                'u__id'   => '1',\n                'u__name' => 'Fabio B. Silva',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    public function testExtraFieldInResultSetShouldBeIgnore(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsAddress::class, 'a');\n        $rsm->addFieldResult('a', 'a__id', 'id');\n        $rsm->addFieldResult('a', 'a__city', 'city');\n        $resultSet = [\n            [\n                'a__id'   => '1',\n                'a__city' => 'Cracow',\n                'doctrine_rownum' => '1',\n            ],\n        ];\n\n        $expectedEntity       = new CmsAddress();\n        $expectedEntity->id   = 1;\n        $expectedEntity->city = 'Cracow';\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n        self::assertEquals($result[0], $expectedEntity);\n    }\n\n    #[Group('DDC-3076')]\n    public function testInvalidDiscriminatorValueException(): void\n    {\n        $this->expectException(HydrationException::class);\n        $this->expectExceptionMessage('The discriminator value \"subworker\" is invalid. It must be one of \"person\", \"manager\", \"employee\".');\n        $rsm = new ResultSetMapping();\n\n        $rsm->addEntityResult(CompanyPerson::class, 'p');\n\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'p__name', 'name');\n        $rsm->addMetaResult('p', 'discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('p', 'discr');\n\n        $resultSet = [\n            [\n                'p__id'   => '1',\n                'p__name' => 'Fabio B. Silva',\n                'discr'   => 'subworker',\n            ],\n        ];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    #[Group('issue-5989')]\n    public function testNullValueShouldNotOverwriteFieldWithSameNameInJoinedInheritance(): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(Issue5989Person::class, 'p');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'm__tags', 'tags', Issue5989Manager::class);\n        $rsm->addFieldResult('p', 'e__tags', 'tags', Issue5989Employee::class);\n        $rsm->addMetaResult('p', 'discr', 'discr', false, 'string');\n        $resultSet = [\n            [\n                'p__id'   => '1',\n                'm__tags' => 'tag1,tag2',\n                'e__tags' => null,\n                'discr'   => 'manager',\n            ],\n        ];\n\n        $expectedEntity       = new Issue5989Manager();\n        $expectedEntity->id   = 1;\n        $expectedEntity->tags = ['tag1', 'tag2'];\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n        self::assertEquals($result[0], $expectedEntity);\n    }\n\n    public function testWrongValuesShouldNotBeConvertedToPhpValue(): void\n    {\n        DBALType::addType(GH8565EmployeePayloadType::NAME, GH8565EmployeePayloadType::class);\n        DBALType::addType(GH8565ManagerPayloadType::NAME, GH8565ManagerPayloadType::class);\n\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(GH8565Person::class, 'p');\n        $rsm->addFieldResult('p', 'p__id', 'id');\n        $rsm->addFieldResult('p', 'm__type', 'type', GH8565Manager::class);\n        $rsm->addFieldResult('p', 'e__type', 'type', GH8565Employee::class);\n        $rsm->addMetaResult('p', 'discr', 'discr', false, 'string');\n        $rsm->setDiscriminatorColumn('p', 'type');\n        $resultSet = [\n            [\n                'p__id'   => '1',\n                'm__type' => 'type field',\n                'e__type' => 'type field',\n                'e__tags' => null,\n                'discr'   => 'manager',\n            ],\n        ];\n\n        $expectedEntity       = new GH8565Manager();\n        $expectedEntity->id   = 1;\n        $expectedEntity->type = 'type field';\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $result   = $hydrator->hydrateAll($stmt, $rsm);\n        self::assertEquals($result[0], $expectedEntity);\n    }\n\n    public function testNotListedValueInEnumArray(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Case \"unknown_case\" is not listed in enum \"Doctrine\\Tests\\Models\\Enums\\Unit\"');\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(Scale::class, 's');\n        $rsm->addFieldResult('s', 's__id', 'id');\n        $rsm->addFieldResult('s', 's__supported_units', 'supportedUnits');\n        $rsm->addEnumResult('s__supported_units', Unit::class);\n        $resultSet = [\n            [\n                's__id' => 1,\n                's__supported_units' => 'g,m,unknown_case',\n            ],\n        ];\n\n        $stmt     = ArrayResultFactory::createWrapperResultFromArray($resultSet);\n        $hydrator = new SimpleObjectHydrator($this->entityManager);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Hydration/SingleScalarHydratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Hydration;\n\nuse Doctrine\\ORM\\Internal\\Hydration\\SingleScalarHydrator;\nuse Doctrine\\ORM\\NonUniqueResultException;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass SingleScalarHydratorTest extends HydrationTestCase\n{\n    /** @return Generator<int, array{list<array<string,mixed>>,mixed}> */\n    public static function validResultSetProvider(): Generator\n    {\n        // SELECT u.name FROM CmsUser u WHERE u.id = 1\n        yield [\n            [\n                ['u__name' => 'romanb'],\n            ],\n            'romanb',\n        ];\n\n        // SELECT u.id FROM CmsUser u WHERE u.id = 1\n        yield [\n            [\n                ['u__id' => '1'],\n            ],\n            1,\n        ];\n\n        // SELECT\n        //   u.id,\n        //   COUNT(u.postsCount + u.likesCount) AS HIDDEN score\n        // FROM CmsUser u\n        // WHERE u.id = 1\n        yield [\n            [\n                [\n                    'u__id' => '1',\n                    'score' => 10, // Ignored since not part of ResultSetMapping (cf. HIDDEN keyword)\n                ],\n            ],\n            1,\n        ];\n    }\n\n    /** @param list<array<string, mixed>> $resultSet */\n    #[DataProvider('validResultSetProvider')]\n    public function testHydrateSingleScalarFromFieldMappingWithValidResultSet(array $resultSet, mixed $expectedResult): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SingleScalarHydrator($this->entityManager);\n\n        $result = $hydrator->hydrateAll($stmt, $rsm);\n        $this->assertEquals($expectedResult, $result);\n    }\n\n    /** @param list<array<string, mixed>> $resultSet */\n    #[DataProvider('validResultSetProvider')]\n    public function testHydrateSingleScalarFromScalarMappingWithValidResultSet(array $resultSet, mixed $expectedResult): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addScalarResult('u__id', 'id', 'string');\n        $rsm->addScalarResult('u__name', 'name', 'string');\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SingleScalarHydrator($this->entityManager);\n\n        $result = $hydrator->hydrateAll($stmt, $rsm);\n        $this->assertEquals($expectedResult, $result);\n    }\n\n    /** @return Generator<int, array{list<array<string,mixed>>}> */\n    public static function invalidResultSetProvider(): Generator\n    {\n        // Single row (OK), multiple columns (NOT OK)\n        yield [\n            [\n                [\n                    'u__id'   => '1',\n                    'u__name' => 'romanb',\n                ],\n            ],\n        ];\n\n        // Multiple rows (NOT OK), single column (OK)\n        yield [\n            [\n                ['u__id' => '1'],\n                ['u__id' => '2'],\n            ],\n        ];\n\n        // Multiple rows (NOT OK), single column with HIDDEN result (OK)\n        yield [\n            [\n                [\n                    'u__id' => '1',\n                    'score' => 10, // Ignored since not part of ResultSetMapping\n                ],\n                [\n                    'u__id' => '2',\n                    'score' => 10, // Ignored since not part of ResultSetMapping\n                ],\n            ],\n            1,\n        ];\n\n        // Multiple row (NOT OK), multiple columns (NOT OK)\n        yield [\n            [\n                [\n                    'u__id'   => '1',\n                    'u__name' => 'romanb',\n                ],\n                [\n                    'u__id'   => '2',\n                    'u__name' => 'romanb',\n                ],\n            ],\n        ];\n    }\n\n    /** @param list<array<string, mixed>> $resultSet */\n    #[DataProvider('invalidResultSetProvider')]\n    public function testHydrateSingleScalarFromFieldMappingWithInvalidResultSet(array $resultSet): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addEntityResult(CmsUser::class, 'u');\n        $rsm->addFieldResult('u', 'u__id', 'id');\n        $rsm->addFieldResult('u', 'u__name', 'name');\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SingleScalarHydrator($this->entityManager);\n\n        $this->expectException(NonUniqueResultException::class);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n\n    /** @param list<array<string, mixed>> $resultSet */\n    #[DataProvider('invalidResultSetProvider')]\n    public function testHydrateSingleScalarFromScalarMappingWithInvalidResultSet(array $resultSet): void\n    {\n        $rsm = new ResultSetMapping();\n        $rsm->addScalarResult('u__id', 'id', 'string');\n        $rsm->addScalarResult('u__name', 'name', 'string');\n\n        $stmt     = $this->createResultMock($resultSet);\n        $hydrator = new SingleScalarHydrator($this->entityManager);\n\n        $this->expectException(NonUniqueResultException::class);\n        $hydrator->hydrateAll($stmt, $rsm);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Id/AssignedGeneratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Id;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\n/**\n * AssignedGeneratorTest\n */\nclass AssignedGeneratorTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n\n    private AssignedGenerator $assignedGen;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n        $this->assignedGen   = new AssignedGenerator();\n    }\n\n    #[DataProvider('entitiesWithoutId')]\n    public function testThrowsExceptionIfIdNotAssigned($entity): void\n    {\n        $this->expectException(ORMException::class);\n\n        $this->assignedGen->generateId($this->entityManager, $entity);\n    }\n\n    public static function entitiesWithoutId(): array\n    {\n        return [\n            'single'    => [new AssignedSingleIdEntity()],\n            'composite' => [new AssignedCompositeIdEntity()],\n        ];\n    }\n\n    public function testCorrectIdGeneration(): void\n    {\n        $entity       = new AssignedSingleIdEntity();\n        $entity->myId = 1;\n        $id           = $this->assignedGen->generateId($this->entityManager, $entity);\n        self::assertEquals(['myId' => 1], $id);\n\n        $entity        = new AssignedCompositeIdEntity();\n        $entity->myId2 = 2;\n        $entity->myId1 = 4;\n        $id            = $this->assignedGen->generateId($this->entityManager, $entity);\n        self::assertEquals(['myId1' => 4, 'myId2' => 2], $id);\n    }\n}\n\n#[Entity]\nclass AssignedSingleIdEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $myId;\n}\n\n#[Entity]\nclass AssignedCompositeIdEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $myId1;\n\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $myId2;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Id/SequenceGeneratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Id;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Id\\SequenceGenerator;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass SequenceGeneratorTest extends OrmTestCase\n{\n    public function testGeneration(): void\n    {\n        $sequenceGenerator = new SequenceGenerator('seq', 10);\n\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('getSequenceNextValSQL')\n            ->willReturn('');\n\n        $connection = $this->getMockBuilder(Connection::class)\n            ->onlyMethods(['fetchOne', 'getDatabasePlatform'])\n            ->setConstructorArgs([[], $this->createMock(Driver::class)])\n            ->getMock();\n        $connection->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        // Sequence values should be generated once per ten identifiers\n        $connection->expects($this->exactly(5))\n            ->method('fetchOne')\n            ->willReturnCallback(static function () use (&$i) {\n                self::assertEquals(0, $i % 10);\n\n                return $i;\n            });\n\n        $entityManager = $this->createTestEntityManagerWithConnection($connection);\n\n        for ($i = 0; $i < 42; ++$i) {\n            $id = $sequenceGenerator->generateId($entityManager, null);\n\n            self::assertSame($i, $id);\n            self::assertSame((int) ($i / 10) * 10 + 10, $sequenceGenerator->getCurrentMaxValue());\n            self::assertSame($i + 1, $sequenceGenerator->getNextValue());\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Internal/HydrationCompleteHandlerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Internal;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\ListenersInvoker;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Internal\\HydrationCompleteHandler;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Event\\LifecycleEventArgs;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\nuse stdClass;\n\nuse function assert;\nuse function in_array;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Internal\\HydrationCompleteHandler}\n */\n#[CoversClass(HydrationCompleteHandler::class)]\nclass HydrationCompleteHandlerTest extends TestCase\n{\n    private ListenersInvoker&MockObject $listenersInvoker;\n    private EntityManagerInterface&MockObject $entityManager;\n    private HydrationCompleteHandler $handler;\n\n    protected function setUp(): void\n    {\n        $this->listenersInvoker = $this->createMock(ListenersInvoker::class);\n        $this->entityManager    = $this->createMock(EntityManagerInterface::class);\n        $this->handler          = new HydrationCompleteHandler($this->listenersInvoker, $this->entityManager);\n    }\n\n    #[DataProvider('invocationFlagProvider')]\n    public function testDefersPostLoadOfEntity(int $listenersFlag): void\n    {\n        $metadata = $this->createMock(ClassMetadata::class);\n        assert($metadata instanceof ClassMetadata);\n        $entity        = new stdClass();\n        $entityManager = $this->entityManager;\n\n        $this\n            ->listenersInvoker\n            ->expects(self::any())\n            ->method('getSubscribedSystems')\n            ->with($metadata)\n            ->willReturn($listenersFlag);\n\n        $this->handler->deferPostLoadInvoking($metadata, $entity);\n\n        $this\n            ->listenersInvoker\n            ->expects(self::once())\n            ->method('invoke')\n            ->with(\n                $metadata,\n                Events::postLoad,\n                $entity,\n                self::callback(static fn (LifecycleEventArgs $args) => $entity === $args->getObject() && $entityManager === $args->getObjectManager()),\n                $listenersFlag,\n            );\n\n        $this->handler->hydrationComplete();\n    }\n\n    #[DataProvider('invocationFlagProvider')]\n    public function testDefersPostLoadOfEntityOnlyOnce(int $listenersFlag): void\n    {\n        $metadata = $this->createMock(ClassMetadata::class);\n        assert($metadata instanceof ClassMetadata);\n        $entity = new stdClass();\n\n        $this\n            ->listenersInvoker\n            ->expects(self::any())\n            ->method('getSubscribedSystems')\n            ->with($metadata)\n            ->willReturn($listenersFlag);\n\n        $this->handler->deferPostLoadInvoking($metadata, $entity);\n\n        $this->listenersInvoker->expects(self::once())->method('invoke');\n\n        $this->handler->hydrationComplete();\n        $this->handler->hydrationComplete();\n    }\n\n    #[DataProvider('invocationFlagProvider')]\n    public function testDefersMultiplePostLoadOfEntity(int $listenersFlag): void\n    {\n        $metadata1     = $this->createMock(ClassMetadata::class);\n        $metadata2     = $this->createMock(ClassMetadata::class);\n        $entity1       = new stdClass();\n        $entity2       = new stdClass();\n        $entityManager = $this->entityManager;\n\n        $this\n            ->listenersInvoker\n            ->expects(self::any())\n            ->method('getSubscribedSystems')\n            ->with(self::logicalOr($metadata1, $metadata2))\n            ->willReturn($listenersFlag);\n\n        $this->handler->deferPostLoadInvoking($metadata1, $entity1);\n        $this->handler->deferPostLoadInvoking($metadata2, $entity2);\n\n        $this\n            ->listenersInvoker\n            ->expects(self::exactly(2))\n            ->method('invoke')\n            ->with(\n                self::logicalOr($metadata1, $metadata2),\n                Events::postLoad,\n                self::logicalOr($entity1, $entity2),\n                self::callback(static fn (LifecycleEventArgs $args) => in_array($args->getObject(), [$entity1, $entity2], true)\n                    && $entityManager === $args->getObjectManager()),\n                $listenersFlag,\n            );\n\n        $this->handler->hydrationComplete();\n    }\n\n    public function testSkipsDeferredPostLoadOfMetadataWithNoInvokedListeners(): void\n    {\n        $metadata = $this->createMock(ClassMetadata::class);\n        assert($metadata instanceof ClassMetadata);\n        $entity = new stdClass();\n\n        $this\n            ->listenersInvoker\n            ->expects(self::any())\n            ->method('getSubscribedSystems')\n            ->with($metadata)\n            ->willReturn(ListenersInvoker::INVOKE_NONE);\n\n        $this->handler->deferPostLoadInvoking($metadata, $entity);\n\n        $this->listenersInvoker->expects(self::never())->method('invoke');\n\n        $this->handler->hydrationComplete();\n    }\n\n    /** @phpstan-return list<array{int}> */\n    public static function invocationFlagProvider(): array\n    {\n        return [\n            [ListenersInvoker::INVOKE_LISTENERS],\n            [ListenersInvoker::INVOKE_CALLBACKS],\n            [ListenersInvoker::INVOKE_MANAGER],\n            [ListenersInvoker::INVOKE_LISTENERS | ListenersInvoker::INVOKE_CALLBACKS],\n            [ListenersInvoker::INVOKE_LISTENERS | ListenersInvoker::INVOKE_MANAGER],\n            [ListenersInvoker::INVOKE_LISTENERS | ListenersInvoker::INVOKE_CALLBACKS | ListenersInvoker::INVOKE_MANAGER],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Internal/Node.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Internal;\n\nclass Node\n{\n    /** @var string */\n    public $name;\n\n    public function __construct(string $name)\n    {\n        $this->name = $name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Internal/StronglyConnectedComponentsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Internal;\n\nuse Doctrine\\ORM\\Internal\\StronglyConnectedComponents;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass StronglyConnectedComponentsTest extends OrmTestCase\n{\n    /** @var array<string, Node> */\n    private $nodes = [];\n\n    /** @var StronglyConnectedComponents */\n    private $stronglyConnectedComponents;\n\n    protected function setUp(): void\n    {\n        $this->stronglyConnectedComponents = new StronglyConnectedComponents();\n    }\n\n    public function testFindStronglyConnectedComponents(): void\n    {\n        // A -> B <-> C -> D <-> E\n        $this->addNodes('A', 'B', 'C', 'D', 'E');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C');\n        $this->addEdge('C', 'B');\n        $this->addEdge('C', 'D');\n        $this->addEdge('D', 'E');\n        $this->addEdge('E', 'D');\n\n        $this->stronglyConnectedComponents->findStronglyConnectedComponents();\n\n        $this->assertNodesAreInSameComponent('B', 'C');\n        $this->assertNodesAreInSameComponent('D', 'E');\n        $this->assertNodesAreNotInSameComponent('A', 'B');\n        $this->assertNodesAreNotInSameComponent('A', 'D');\n    }\n\n    public function testFindStronglyConnectedComponents2(): void\n    {\n        // A -> B -> C -> D -> B\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C');\n        $this->addEdge('C', 'D');\n        $this->addEdge('D', 'B');\n\n        $this->stronglyConnectedComponents->findStronglyConnectedComponents();\n\n        $this->assertNodesAreInSameComponent('B', 'C');\n        $this->assertNodesAreInSameComponent('C', 'D');\n        $this->assertNodesAreNotInSameComponent('A', 'B');\n    }\n\n    public function testFindStronglyConnectedComponents3(): void\n    {\n        //           v---------.\n        // A -> B -> C -> D -> E\n        //      ^--------´\n\n        $this->addNodes('A', 'B', 'C', 'D', 'E');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C');\n        $this->addEdge('C', 'D');\n        $this->addEdge('D', 'E');\n        $this->addEdge('E', 'C');\n        $this->addEdge('D', 'B');\n\n        $this->stronglyConnectedComponents->findStronglyConnectedComponents();\n\n        $this->assertNodesAreInSameComponent('B', 'C');\n        $this->assertNodesAreInSameComponent('C', 'D');\n        $this->assertNodesAreInSameComponent('D', 'E');\n        $this->assertNodesAreInSameComponent('E', 'B');\n        $this->assertNodesAreNotInSameComponent('A', 'B');\n    }\n\n    private function addNodes(string ...$names): void\n    {\n        foreach ($names as $name) {\n            $node               = new Node($name);\n            $this->nodes[$name] = $node;\n            $this->stronglyConnectedComponents->addNode($node);\n        }\n    }\n\n    private function addEdge(string $from, string $to, bool $optional = false): void\n    {\n        $this->stronglyConnectedComponents->addEdge($this->nodes[$from], $this->nodes[$to], $optional);\n    }\n\n    private function assertNodesAreInSameComponent(string $first, string $second): void\n    {\n        self::assertSame(\n            $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$first]),\n            $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]),\n        );\n    }\n\n    private function assertNodesAreNotInSameComponent(string $first, string $second): void\n    {\n        self::assertNotSame(\n            $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$first]),\n            $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Internal/TopologicalSortTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Internal;\n\nuse Doctrine\\ORM\\Internal\\TopologicalSort;\nuse Doctrine\\ORM\\Internal\\TopologicalSort\\CycleDetectedException;\nuse Doctrine\\Tests\\OrmTestCase;\n\nuse function array_map;\nuse function array_search;\nuse function array_values;\n\nclass TopologicalSortTest extends OrmTestCase\n{\n    /** @var array<string, Node> */\n    private $nodes = [];\n\n    /** @var TopologicalSort */\n    private $topologicalSort;\n\n    protected function setUp(): void\n    {\n        $this->topologicalSort = new TopologicalSort();\n    }\n\n    public function testSimpleOrdering(): void\n    {\n        $this->addNodes('C', 'B', 'A', 'E');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C');\n        $this->addEdge('E', 'A');\n\n        // There is only 1 valid ordering for this constellation\n        self::assertSame(['C', 'B', 'A', 'E'], $this->computeResult());\n    }\n\n    public function testSkipOptionalEdgeToBreakCycle(): void\n    {\n        $this->addNodes('A', 'B');\n\n        $this->addEdge('A', 'B', true);\n        $this->addEdge('B', 'A', false);\n\n        self::assertSame(['A', 'B'], $this->computeResult());\n    }\n\n    public function testBreakCycleByBacktracking(): void\n    {\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C', true);\n        $this->addEdge('C', 'D');\n        $this->addEdge('D', 'A'); // closes the cycle\n\n        // We can only break B -> C, so the result must be C -> D -> A -> B\n        self::assertSame(['B', 'A', 'D', 'C'], $this->computeResult());\n    }\n\n    public function testCycleRemovedByEliminatingLastOptionalEdge(): void\n    {\n        // The cycle-breaking algorithm is currently very naive. It breaks the cycle\n        // at the last optional edge while it backtracks. In this example, we might\n        // get away with one extra update if we'd break A->B; instead, we break up\n        // B->C and B->D.\n\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('A', 'B', true);\n        $this->addEdge('B', 'C', true);\n        $this->addEdge('C', 'A');\n        $this->addEdge('B', 'D', true);\n        $this->addEdge('D', 'A');\n\n        self::assertSame(['B', 'A', 'C', 'D'], $this->computeResult());\n    }\n\n    public function testGH7180Example(): void\n    {\n        // Example given in https://github.com/doctrine/orm/pull/7180#issuecomment-381341943\n\n        $this->addNodes('E', 'F', 'D', 'G');\n\n        $this->addEdge('D', 'G');\n        $this->addEdge('D', 'F', true);\n        $this->addEdge('F', 'E');\n        $this->addEdge('E', 'D');\n\n        self::assertSame(['G', 'D', 'E', 'F'], $this->computeResult());\n    }\n\n    public function testCommitOrderingFromGH7259Test(): void\n    {\n        // this test corresponds to the GH7259Test::testPersistFileBeforeVersion functional test\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('D', 'A');\n        $this->addEdge('A', 'B');\n        $this->addEdge('D', 'C');\n        $this->addEdge('A', 'D', true);\n\n        // There is only multiple valid ordering for this constellation, but\n        // the D -> A -> B ordering is important to break the cycle\n        // on the nullable link.\n        $correctOrders = [\n            ['C', 'B', 'A', 'D'],\n            ['B', 'C', 'A', 'D'],\n            ['B', 'A', 'C', 'D'],\n        ];\n\n        self::assertContains($this->computeResult(), $correctOrders);\n    }\n\n    public function testCommitOrderingFromGH8349Case1Test(): void\n    {\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('D', 'A');\n        $this->addEdge('A', 'B', true);\n        $this->addEdge('B', 'D', true);\n        $this->addEdge('B', 'C', true);\n        $this->addEdge('C', 'D', true);\n\n        // Many orderings are possible here, but the bottom line is A must be before D (it's the only hard requirement).\n        $result = $this->computeResult();\n\n        $indexA = array_search('A', $result, true);\n        $indexD = array_search('D', $result, true);\n        self::assertTrue($indexD > $indexA);\n    }\n\n    public function testCommitOrderingFromGH8349Case2Test(): void\n    {\n        $this->addNodes('A', 'B');\n\n        $this->addEdge('B', 'A');\n        $this->addEdge('B', 'A', true); // interesting: We have two edges in that direction\n        $this->addEdge('A', 'B', true);\n\n        // The B -> A requirement determines the result here\n        self::assertSame(['A', 'B'], $this->computeResult());\n    }\n\n    public function testNodesMaintainOrderWhenNoDepencency(): void\n    {\n        $this->addNodes('A', 'B', 'C');\n\n        // Nodes that are not constrained by dependencies shall maintain the order\n        // in which they were added\n        self::assertSame(['A', 'B', 'C'], $this->computeResult());\n    }\n\n    public function testNodesReturnedInDepthFirstOrder(): void\n    {\n        $this->addNodes('A', 'B', 'C');\n        $this->addEdge('A', 'B');\n        $this->addEdge('A', 'C');\n\n        // We start on A and find that it has two dependencies on B and C,\n        // added (as dependencies) in that order.\n        // So, first we continue the DFS on B, because that edge was added first.\n        // This gives the result order B, C, A.\n        self::assertSame(['B', 'C', 'A'], $this->computeResult());\n    }\n\n    public function testNodesReturnedInDepthFirstOrderWithEdgesInDifferentOrderThanNodes(): void\n    {\n        $this->addNodes('A', 'B', 'C');\n        $this->addEdge('A', 'C');\n        $this->addEdge('A', 'B');\n\n        // This is like testNodesReturnedInDepthFirstOrder, but it shows that for the two\n        // nodes B and C that A depends upon, the result will follow the order in which\n        // the edges were added.\n        self::assertSame(['C', 'B', 'A'], $this->computeResult());\n    }\n\n    public function testNodesReturnedInDepthFirstOrderWithDependingNodeLast(): void\n    {\n        $this->addNodes('B', 'C', 'A');\n        $this->addEdge('A', 'B');\n        $this->addEdge('A', 'C');\n\n        // This again is like testNodesReturnedInDepthFirstOrder, but this\n        // time the node A that depends on B and C is added as the last node.\n        // That means processing can go over B and C in the order they were given.\n        // The order in which edges are added is not relevant (!), since at the time\n        // the edges are evaluated, the nodes they point to have already been finished.\n        self::assertSame(['B', 'C', 'A'], $this->computeResult());\n    }\n\n    public function testNodesReturnedInDepthFirstOrderWithDependingNodeLastAndEdgeOrderInversed(): void\n    {\n        $this->addNodes('B', 'C', 'A');\n        $this->addEdge('A', 'C');\n        $this->addEdge('A', 'B');\n\n        // This again is like testNodesReturnedInDepthFirstOrderWithDependingNodeLast, but adds\n        // the edges in the opposing order. Still, the result order is the same (!).\n        // This may be surprising when comparing with testNodesReturnedInDepthFirstOrderWithEdgesInDifferentOrderThanNodes,\n        // where the result order depends upon the _edge_ order.\n        self::assertSame(['B', 'C', 'A'], $this->computeResult());\n    }\n\n    public function testDetectSmallCycle(): void\n    {\n        $this->addNodes('A', 'B');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'A');\n\n        $this->expectException(CycleDetectedException::class);\n        $this->computeResult();\n    }\n\n    public function testMultipleEdges(): void\n    {\n        // There may be more than one association between two given entities.\n        // For the commit order, we only need to track this once, since the\n        // result is the same (one entity must be processed before the other).\n        //\n        // In case one of the associations is optional and the other one is not,\n        // we must honor the non-optional one, regardless of the order in which\n        // they were declared.\n\n        $this->addNodes('A', 'B');\n\n        $this->addEdge('A', 'B', true); // optional comes first\n        $this->addEdge('A', 'B', false);\n        $this->addEdge('B', 'A', false);\n        $this->addEdge('B', 'A', true); // optional comes last\n\n        // Both edges A -> B and B -> A are non-optional, so this is a cycle\n        // that cannot be broken.\n\n        $this->expectException(CycleDetectedException::class);\n        $this->computeResult();\n    }\n\n    public function testDetectLargerCycleNotIncludingStartNode(): void\n    {\n        $this->addNodes('A', 'B', 'C', 'D');\n\n        $this->addEdge('A', 'B');\n        $this->addEdge('B', 'C');\n        $this->addEdge('C', 'D');\n        $this->addEdge('D', 'B');\n\n        // The sort has to start with the last node being added to make it possible that\n        // the result is in the order the nodes were added (if permitted by edges).\n        // That means the cycle will be detected when starting at D, so it is D -> B -> C -> D.\n\n        try {\n            $this->computeResult();\n        } catch (CycleDetectedException $exception) {\n            self::assertEquals(\n                [$this->nodes['B'], $this->nodes['C'], $this->nodes['D'], $this->nodes['B']],\n                $exception->getCycle(),\n            );\n        }\n    }\n\n    private function addNodes(string ...$names): void\n    {\n        foreach ($names as $name) {\n            $node               = new Node($name);\n            $this->nodes[$name] = $node;\n            $this->topologicalSort->addNode($node);\n        }\n    }\n\n    private function addEdge(string $from, string $to, bool $optional = false): void\n    {\n        $this->topologicalSort->addEdge($this->nodes[$from], $this->nodes[$to], $optional);\n    }\n\n    /** @return list<string> */\n    private function computeResult(): array\n    {\n        return array_map(static function (Node $n): string {\n            return $n->name;\n        }, array_values($this->topologicalSort->sort()));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Internal/UnitOfWork/InsertBatchTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Internal\\UnitOfWork;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Id\\AssignedGenerator;\nuse Doctrine\\ORM\\Id\\IdentityGenerator;\nuse Doctrine\\ORM\\Internal\\UnitOfWork\\InsertBatch;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\Stub;\nuse PHPUnit\\Framework\\TestCase;\n\n#[CoversClass(InsertBatch::class)]\n#[Group('#11977')]\nfinal class InsertBatchTest extends TestCase\n{\n    private EntityManagerInterface&Stub $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->createStub(EntityManagerInterface::class);\n\n        $entityAMetadata = new ClassMetadata(EntityA::class);\n        $entityBMetadata = new ClassMetadata(EntityB::class);\n        $entityCMetadata = new ClassMetadata(EntityC::class);\n\n        $entityAMetadata->idGenerator = new AssignedGenerator();\n        $entityBMetadata->idGenerator = new AssignedGenerator();\n        $entityCMetadata->idGenerator = new IdentityGenerator();\n\n        $this->entityManager->method('getClassMetadata')\n            ->willReturnMap([\n                [EntityA::class, $entityAMetadata],\n                [EntityB::class, $entityBMetadata],\n                [EntityC::class, $entityCMetadata],\n            ]);\n    }\n\n    public function testWillProduceEmptyBatchOnNoGivenEntities(): void\n    {\n        self::assertEmpty(InsertBatch::batchByEntityType($this->entityManager, []));\n    }\n\n    public function testWillBatchSameEntityOperationsInSingleBatch(): void\n    {\n        $batches = InsertBatch::batchByEntityType(\n            $this->entityManager,\n            [\n                new EntityA(),\n                new EntityA(),\n                new EntityA(),\n            ],\n        );\n\n        self::assertCount(1, $batches);\n        self::assertSame(EntityA::class, $batches[0]->class->name);\n        self::assertCount(3, $batches[0]->entities);\n    }\n\n    public function testWillBatchInterleavedEntityOperationsInGroups(): void\n    {\n        $batches = InsertBatch::batchByEntityType(\n            $this->entityManager,\n            [\n                new EntityA(),\n                new EntityA(),\n                new EntityB(),\n                new EntityB(),\n                new EntityA(),\n                new EntityA(),\n            ],\n        );\n\n        self::assertCount(3, $batches);\n        self::assertSame(EntityA::class, $batches[0]->class->name);\n        self::assertCount(2, $batches[0]->entities);\n        self::assertSame(EntityB::class, $batches[1]->class->name);\n        self::assertCount(2, $batches[1]->entities);\n        self::assertSame(EntityA::class, $batches[2]->class->name);\n        self::assertCount(2, $batches[2]->entities);\n    }\n\n    public function testWillNotBatchOperationsForAGeneratedIdentifierEntity(): void\n    {\n        $batches = InsertBatch::batchByEntityType(\n            $this->entityManager,\n            [\n                new EntityC(),\n                new EntityC(),\n                new EntityC(),\n            ],\n        );\n\n        self::assertCount(3, $batches);\n        self::assertSame(EntityC::class, $batches[0]->class->name);\n        self::assertCount(1, $batches[0]->entities);\n        self::assertSame(EntityC::class, $batches[1]->class->name);\n        self::assertCount(1, $batches[1]->entities);\n        self::assertSame(EntityC::class, $batches[2]->class->name);\n        self::assertCount(1, $batches[2]->entities);\n    }\n\n    public function testWillIsolateBatchesForEntitiesWithGeneratedIdentifiers(): void\n    {\n        $batches = InsertBatch::batchByEntityType(\n            $this->entityManager,\n            [\n                new EntityA(),\n                new EntityA(),\n                new EntityC(),\n                new EntityC(),\n                new EntityA(),\n                new EntityA(),\n            ],\n        );\n\n        self::assertCount(4, $batches);\n        self::assertSame(EntityA::class, $batches[0]->class->name);\n        self::assertCount(2, $batches[0]->entities);\n        self::assertSame(EntityC::class, $batches[1]->class->name);\n        self::assertCount(1, $batches[1]->entities);\n        self::assertSame(EntityC::class, $batches[2]->class->name);\n        self::assertCount(1, $batches[2]->entities);\n        self::assertSame(EntityA::class, $batches[3]->class->name);\n        self::assertCount(2, $batches[3]->entities);\n    }\n}\n\nclass EntityA\n{\n}\n\nclass EntityB\n{\n}\n\nclass EntityC\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/LazyCriteriaCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\LazyCriteriaCollection;\nuse Doctrine\\ORM\\Persisters\\Entity\\EntityPersister;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\n\n#[CoversClass(LazyCriteriaCollection::class)]\nclass LazyCriteriaCollectionTest extends TestCase\n{\n    private EntityPersister&MockObject $persister;\n    private Criteria $criteria;\n    private LazyCriteriaCollection $lazyCriteriaCollection;\n\n    protected function setUp(): void\n    {\n        $this->persister              = $this->createMock(EntityPersister::class);\n        $this->criteria               = Criteria::create(true);\n        $this->lazyCriteriaCollection = new LazyCriteriaCollection($this->persister, $this->criteria);\n    }\n\n    public function testCountIsCached(): void\n    {\n        $this->persister->expects(self::once())->method('count')->with($this->criteria)->willReturn(10);\n\n        self::assertSame(10, $this->lazyCriteriaCollection->count());\n        self::assertSame(10, $this->lazyCriteriaCollection->count());\n        self::assertSame(10, $this->lazyCriteriaCollection->count());\n    }\n\n    public function testCountIsCachedEvenWithZeroResult(): void\n    {\n        $this->persister->expects(self::once())->method('count')->with($this->criteria)->willReturn(0);\n\n        self::assertSame(0, $this->lazyCriteriaCollection->count());\n        self::assertSame(0, $this->lazyCriteriaCollection->count());\n        self::assertSame(0, $this->lazyCriteriaCollection->count());\n    }\n\n    public function testCountUsesWrappedCollectionWhenInitialized(): void\n    {\n        $this\n            ->persister\n            ->expects(self::once())\n            ->method('loadCriteria')\n            ->with($this->criteria)\n            ->willReturn(['foo', 'bar', 'baz']);\n\n        // should never call the persister's count\n        $this->persister->expects(self::never())->method('count');\n\n        self::assertSame(['foo', 'bar', 'baz'], $this->lazyCriteriaCollection->toArray());\n\n        self::assertSame(3, $this->lazyCriteriaCollection->count());\n    }\n\n    public function testMatchingUsesThePersisterOnlyOnce(): void\n    {\n        $foo = new LazyCriteriaCollectionTestObject();\n        $bar = new LazyCriteriaCollectionTestObject();\n        $baz = new LazyCriteriaCollectionTestObject();\n\n        $foo->val = 'foo';\n        $bar->val = 'bar';\n        $baz->val = 'baz';\n\n        $this\n            ->persister\n            ->expects(self::once())\n            ->method('loadCriteria')\n            ->with($this->criteria)\n            ->willReturn([$foo, $bar, $baz]);\n\n        $criteria = Criteria::create(true);\n\n        $criteria->andWhere($criteria->expr()->eq('val', 'foo'));\n\n        $filtered = $this->lazyCriteriaCollection->matching($criteria);\n\n        self::assertInstanceOf(Collection::class, $filtered);\n        self::assertEquals([$foo], $filtered->toArray());\n\n        self::assertEquals([$foo], $this->lazyCriteriaCollection->matching($criteria)->toArray());\n    }\n\n    public function testIsEmptyUsesCountWhenNotInitialized(): void\n    {\n        $this->persister->expects(self::once())->method('count')->with($this->criteria)->willReturn(0);\n\n        self::assertTrue($this->lazyCriteriaCollection->isEmpty());\n    }\n\n    public function testIsEmptyIsFalseIfCountIsNotZero(): void\n    {\n        $this->persister->expects(self::once())->method('count')->with($this->criteria)->willReturn(1);\n\n        self::assertFalse($this->lazyCriteriaCollection->isEmpty());\n    }\n\n    public function testIsEmptyUsesWrappedCollectionWhenInitialized(): void\n    {\n        $this\n            ->persister\n            ->expects(self::once())\n            ->method('loadCriteria')\n            ->with($this->criteria)\n            ->willReturn(['foo', 'bar', 'baz']);\n\n        // should never call the persister's count\n        $this->persister->expects(self::never())->method('count');\n\n        self::assertSame(['foo', 'bar', 'baz'], $this->lazyCriteriaCollection->toArray());\n\n        self::assertFalse($this->lazyCriteriaCollection->isEmpty());\n    }\n}\n\nclass LazyCriteriaCollectionTestObject\n{\n    /** @var mixed */\n    public $val;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Mapping\\AnsiQuoteStrategy;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1845')]\n#[Group('DDC-2459')]\nclass AnsiQuoteStrategyTest extends OrmTestCase\n{\n    private AnsiQuoteStrategy $strategy;\n\n    private AbstractPlatform $platform;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $em             = $this->getTestEntityManager();\n        $this->platform = $em->getConnection()->getDatabasePlatform();\n        $this->strategy = new AnsiQuoteStrategy();\n    }\n\n    private function createClassMetadata(string $className): ClassMetadata\n    {\n        $class = new ClassMetadata($className);\n        $class->initializeReflection(new RuntimeReflectionService());\n\n        return $class;\n    }\n\n    public function testGetColumnName(): void\n    {\n        $class = $this->createClassMetadata(CmsUser::class);\n        $class->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n        $class->mapField(['fieldName' => 'id', 'columnName' => 'id', 'id' => true]);\n\n        self::assertEquals('id', $this->strategy->getColumnName('id', $class, $this->platform));\n        self::assertEquals('name', $this->strategy->getColumnName('name', $class, $this->platform));\n    }\n\n    public function testGetTableName(): void\n    {\n        $class = $this->createClassMetadata(CmsUser::class);\n\n        $class->setPrimaryTable(['name' => 'cms_user']);\n        self::assertEquals('cms_user', $this->strategy->getTableName($class, $this->platform));\n    }\n\n    public function testJoinTableName(): void\n    {\n        $class = $this->createClassMetadata(CmsAddress::class);\n\n        $class->mapManyToMany(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n                'inversedBy'    => 'users',\n                'joinTable'     => ['name' => 'cmsaddress_cmsuser'],\n            ],\n        );\n\n        self::assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName($class->associationMappings['user'], $class, $this->platform));\n    }\n\n    public function testIdentifierColumnNames(): void\n    {\n        $class = $this->createClassMetadata(CmsAddress::class);\n\n        $class->mapField(\n            [\n                'id'            => true,\n                'fieldName'     => 'id',\n                'columnName'    => 'id',\n            ],\n        );\n\n        self::assertEquals(['id'], $this->strategy->getIdentifierColumnNames($class, $this->platform));\n    }\n\n    public function testColumnAlias(): void\n    {\n        self::assertEquals('columnName_1', $this->strategy->getColumnAlias('columnName', 1, $this->platform));\n    }\n\n    public function testJoinColumnName(): void\n    {\n        $class = $this->createClassMetadata(DDC117ArticleDetails::class);\n\n        $class->mapOneToOne(\n            [\n                'id'            => true,\n                'fieldName'     => 'article',\n                'targetEntity'  => DDC117Article::class,\n                'joinColumns'    => [\n                    ['name' => 'article', 'referencedColumnName' => 'id'],\n                ],\n            ],\n        );\n\n        $joinColumn = $class->associationMappings['article']->joinColumns[0];\n        self::assertEquals('article', $this->strategy->getJoinColumnName($joinColumn, $class, $this->platform));\n    }\n\n    public function testReferencedJoinColumnName(): void\n    {\n        $cm = $this->createClassMetadata(DDC117ArticleDetails::class);\n\n        $cm->mapOneToOne(\n            [\n                'id'            => true,\n                'fieldName'     => 'article',\n                'targetEntity'  => DDC117Article::class,\n                'joinColumns'    => [\n                    ['name' => 'article', 'referencedColumnName' => 'id'],\n                ],\n            ],\n        );\n\n        $joinColumn = $cm->associationMappings['article']->joinColumns[0];\n        self::assertEquals('id', $this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform));\n    }\n\n    public function testGetSequenceName(): void\n    {\n        $class      = $this->createClassMetadata(CmsUser::class);\n        $definition = [\n            'sequenceName'      => 'user_id_seq',\n            'allocationSize'    => 1,\n            'initialValue'      => 2,\n        ];\n\n        $class->setSequenceGeneratorDefinition($definition);\n\n        self::assertEquals('user_id_seq', $this->strategy->getSequenceName($definition, $class, $this->platform));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/AssociationMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse OutOfRangeException;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class AssociationMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new MyAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->cascade           = ['persist'];\n        $mapping->fetch             = ClassMetadata::FETCH_EAGER;\n        $mapping->inherited         = self::class;\n        $mapping->declared          = self::class;\n        $mapping->cache             = ['usage' => ClassMetadata::CACHE_USAGE_READ_ONLY];\n        $mapping->id                = true;\n        $mapping->isOnDeleteCascade = true;\n        $mapping->originalClass     = self::class;\n        $mapping->originalField     = 'foo';\n        $mapping->orphanRemoval     = true;\n        $mapping->unique            = true;\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof AssociationMapping);\n\n        self::assertSame(['persist'], $resurrectedMapping->cascade);\n        self::assertSame(ClassMetadata::FETCH_EAGER, $resurrectedMapping->fetch);\n        self::assertSame(self::class, $resurrectedMapping->inherited);\n        self::assertSame(self::class, $resurrectedMapping->declared);\n        self::assertSame(['usage' => ClassMetadata::CACHE_USAGE_READ_ONLY], $resurrectedMapping->cache);\n        self::assertTrue($resurrectedMapping->id);\n        self::assertTrue($resurrectedMapping->isOnDeleteCascade);\n        self::assertSame(self::class, $resurrectedMapping->originalClass);\n        self::assertSame('foo', $resurrectedMapping->originalField);\n        self::assertTrue($resurrectedMapping->orphanRemoval);\n        self::assertTrue($resurrectedMapping->unique);\n    }\n\n    public function testItThrowsWhenAccessingUnknownProperty(): void\n    {\n        $mapping = new MyAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $this->expectException(OutOfRangeException::class);\n\n        $mapping['foo'];\n    }\n\n    public function testItThrowsWhenSettingUnknownProperty(): void\n    {\n        $mapping = new MyAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $this->expectException(OutOfRangeException::class);\n\n        $mapping['foo'] = 'bar';\n    }\n\n    public function testItThrowsWhenUnsettingUnknownProperty(): void\n    {\n        $mapping = new MyAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $this->expectException(OutOfRangeException::class);\n\n        unset($mapping['foo']);\n    }\n}\n\nclass MyAssociationMapping extends AssociationMapping\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/AttributeDriverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Attribute;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\MappingAttribute;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ClassNames;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\ORM\\Mapping\\Fixtures\\AttributeEntityWithNestedJoinColumns;\nuse InvalidArgumentException;\nuse stdClass;\n\nclass AttributeDriverTest extends MappingDriverTestCase\n{\n    protected function loadDriver(): MappingDriver\n    {\n        return AttributeDriverFactory::createAttributeDriver();\n    }\n\n    public function testDriverCanAcceptClassLocator(): void\n    {\n        if (! AttributeDriverFactory::isClassLocatorSupported()) {\n            self::markTestSkipped('This test is only relevant for versions of doctrine/persistence >= 4.1');\n        }\n\n        $classLocator = new ClassNames([City::class]);\n\n        $driver = new AttributeDriver($classLocator);\n\n        self::assertSame([], $driver->getPaths(), 'Directory paths must be empty, since file paths are used');\n        self::assertSame([City::class], $driver->getAllClassNames());\n    }\n\n    public function testOriginallyNestedAttributesDeclaredWithoutOriginalParent(): void\n    {\n        $factory = $this->createClassMetadataFactory();\n\n        $metadata = $factory->getMetadataFor(AttributeEntityWithoutOriginalParents::class);\n\n        self::assertEquals(\n            [\n                'name' => 'AttributeEntityWithoutOriginalParents',\n                'uniqueConstraints' => ['foo' => ['columns' => ['id']]],\n                'indexes' => ['bar' => ['columns' => ['id']]],\n            ],\n            $metadata->table,\n        );\n        self::assertEquals(['assoz_id', 'assoz_id'], $metadata->associationMappings['assoc']->joinTableColumns);\n    }\n\n    public function testIsTransient(): void\n    {\n        $driver = $this->loadDriver();\n\n        self::assertTrue($driver->isTransient(stdClass::class));\n\n        self::assertTrue($driver->isTransient(AttributeTransientClass::class));\n\n        self::assertFalse($driver->isTransient(AttributeEntityWithoutOriginalParents::class));\n\n        self::assertFalse($driver->isTransient(AttributeEntityStartingWithRepeatableAttributes::class));\n    }\n\n    public function testManyToManyAssociationWithNestedJoinColumns(): void\n    {\n        $factory = $this->createClassMetadataFactory();\n\n        $metadata = $factory->getMetadataFor(AttributeEntityWithNestedJoinColumns::class);\n\n        self::assertEquals(\n            [\n                JoinColumnMapping::fromMappingArray([\n                    'name' => 'assoz_id',\n                    'referencedColumnName' => 'assoz_id',\n                    'unique' => false,\n                    'nullable' => null,\n                    'onDelete' => null,\n                    'columnDefinition' => null,\n                ]),\n            ],\n            $metadata->associationMappings['assoc']->joinTable->joinColumns,\n        );\n\n        self::assertEquals(\n            [\n                JoinColumnMapping::fromMappingArray([\n                    'name' => 'inverse_assoz_id',\n                    'referencedColumnName' => 'inverse_assoz_id',\n                    'unique' => false,\n                    'nullable' => null,\n                    'onDelete' => null,\n                    'columnDefinition' => null,\n                ]),\n            ],\n            $metadata->associationMappings['assoc']->joinTable->inverseJoinColumns,\n        );\n    }\n\n    public function testItThrowsWhenSettingReportFieldsWhereDeclaredToFalse(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new AttributeDriver([], false);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\UniqueConstraint(name: 'foo', columns: ['id'])]\n#[ORM\\Index(name: 'bar', columns: ['id'])]\nclass AttributeEntityWithoutOriginalParents\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var Collection<AttributeEntityWithoutOriginalParents> */\n    #[ORM\\ManyToMany(targetEntity: self::class)]\n    #[ORM\\JoinColumn(name: 'assoz_id', referencedColumnName: 'assoz_id')]\n    #[ORM\\InverseJoinColumn(name: 'assoz_id', referencedColumnName: 'assoz_id')]\n    public $assoc;\n}\n\n#[ORM\\Index(name: 'bar', columns: ['id'])]\n#[ORM\\Index(name: 'baz', columns: ['id'])]\n#[ORM\\Entity]\nclass AttributeEntityStartingWithRepeatableAttributes\n{\n}\n\n#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]\nclass AttributeTransientAttribute implements MappingAttribute\n{\n}\n\n#[AttributeTransientAttribute]\nclass AttributeTransientClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/AttributeReaderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeReader;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse LogicException;\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionClass;\nuse ReflectionProperty;\n\nclass AttributeReaderTest extends TestCase\n{\n    public function testItThrowsWhenGettingRepeatableAttributeWithTheWrongMethod(): void\n    {\n        $reader   = new AttributeReader();\n        $property = new ReflectionProperty(TestEntity::class, 'id');\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(\n            'The attribute \"Doctrine\\ORM\\Mapping\\Index\" is repeatable. Call getPropertyAttributeCollection() instead.',\n        );\n        $reader->getPropertyAttribute($property, ORM\\Index::class);\n    }\n\n    public function testItThrowsWhenGettingNonRepeatableAttributeWithTheWrongMethod(): void\n    {\n        $reader   = new AttributeReader();\n        $property = new ReflectionProperty(TestEntity::class, 'id');\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(\n            'The attribute \"Doctrine\\ORM\\Mapping\\Id\" is not repeatable. Call getPropertyAttribute() instead.',\n        );\n        $reader->getPropertyAttributeCollection($property, ORM\\Id::class);\n    }\n\n    public function testJoinTableOptions(): void\n    {\n        $reader   = new AttributeReader();\n        $property = new ReflectionProperty(TestEntity::class, 'tags');\n\n        $joinTable = $reader->getPropertyAttribute($property, ORM\\JoinTable::class);\n        self::assertSame([\n            'charset' => 'ascii',\n            'collation' => 'ascii_general_ci',\n        ], $joinTable->options);\n    }\n\n    public function testJoinColumnOptions(): void\n    {\n        $reader   = new AttributeReader();\n        $property = new ReflectionProperty(TestEntity::class, 'tags');\n\n        $joinColumns = $reader->getPropertyAttributeCollection($property, ORM\\JoinColumn::class);\n        self::assertCount(1, $joinColumns);\n        self::assertSame([\n            'charset' => 'latin1',\n            'collation' => 'latin1_swedish_ci',\n        ], $joinColumns[0]->options);\n\n        $inverseJoinColumns = $reader->getPropertyAttributeCollection($property, ORM\\InverseJoinColumn::class);\n        self::assertCount(1, $inverseJoinColumns);\n        self::assertSame([\n            'charset' => 'utf8mb4',\n            'collation' => 'utf8mb4_bin',\n        ], $inverseJoinColumns[0]->options);\n    }\n\n    public function testDiscriminatedColumnOptions(): void\n    {\n        $reader = new AttributeReader();\n        $class  = new ReflectionClass(TestPerson::class);\n\n        $attributes = $reader->getClassAttributes($class);\n        self::assertArrayHasKey(DiscriminatorColumn::class, $attributes);\n        self::assertSame([\n            'charset' => 'ascii',\n            'collation' => 'ascii_general_ci',\n        ], $attributes[DiscriminatorColumn::class]->options);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\Index(name: 'bar', columns: ['id'])]\nclass TestEntity\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var mixed */\n    #[ManyToMany(targetEntity: TestTag::class)]\n    #[JoinTable(name: 'artist_tags', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n    #[JoinColumn(name: 'artist_id', referencedColumnName: 'id', options: ['charset' => 'latin1', 'collation' => 'latin1_swedish_ci'])]\n    #[InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id', options: ['charset' => 'utf8mb4', 'collation' => 'utf8mb4_bin'])]\n    public $tags;\n}\n\n#[ORM\\Entity]\nclass TestTag\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n}\n\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])]\n#[DiscriminatorMap(['person' => TestPerson::class, 'employee' => TestEmployee::class])]\nclass TestPerson\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass TestEmployee extends TestPerson\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/BasicInheritanceMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\EntityRepository;\nuse Doctrine\\ORM\\Id\\SequenceGenerator as IdSequenceGenerator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Index;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\SequenceGenerator;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\UniqueConstraint;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869ChequePayment;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869CreditCardPayment;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869Payment;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869PaymentRepository;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function assert;\nuse function serialize;\nuse function sprintf;\nuse function unserialize;\n\nclass BasicInheritanceMappingTest extends OrmTestCase\n{\n    private ClassMetadataFactory $cmf;\n\n    protected function setUp(): void\n    {\n        $this->cmf = new ClassMetadataFactory();\n\n        $this->cmf->setEntityManager($this->getTestEntityManager());\n    }\n\n    public function testGetMetadataForTransientClassThrowsException(): void\n    {\n        $this->expectException(MappingException::class);\n\n        $this->cmf->getMetadataFor(TransientBaseClass::class);\n    }\n\n    public function testGetMetadataForSubclassWithTransientBaseClass(): void\n    {\n        $class = $this->cmf->getMetadataFor(EntitySubClass::class);\n\n        self::assertEmpty($class->subClasses);\n        self::assertEmpty($class->parentClasses);\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('name', $class->fieldMappings);\n    }\n\n    public function testGetMetadataForSubclassWithMappedSuperclass(): void\n    {\n        $class = $this->cmf->getMetadataFor(EntitySubClass2::class);\n\n        self::assertEmpty($class->subClasses);\n        self::assertEmpty($class->parentClasses);\n\n        self::assertArrayHasKey('mapped1', $class->fieldMappings);\n        self::assertArrayHasKey('mapped2', $class->fieldMappings);\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('name', $class->fieldMappings);\n\n        self::assertNull($class->fieldMappings['mapped1']->inherited);\n        self::assertNull($class->fieldMappings['mapped2']->inherited);\n        self::assertArrayNotHasKey('transient', $class->fieldMappings);\n\n        self::assertArrayHasKey('mappedRelated1', $class->associationMappings);\n    }\n\n    #[Group('DDC-869')]\n    public function testGetMetadataForSubclassWithMappedSuperclassWithRepository(): void\n    {\n        $class = $this->cmf->getMetadataFor(DDC869CreditCardPayment::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('value', $class->fieldMappings);\n        self::assertArrayHasKey('creditCardNumber', $class->fieldMappings);\n        self::assertEquals($class->customRepositoryClassName, DDC869PaymentRepository::class);\n\n        $class = $this->cmf->getMetadataFor(DDC869ChequePayment::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('value', $class->fieldMappings);\n        self::assertArrayHasKey('serialNumber', $class->fieldMappings);\n        self::assertEquals($class->customRepositoryClassName, DDC869PaymentRepository::class);\n\n        // override repositoryClass\n        $class = $this->cmf->getMetadataFor(SubclassWithRepository::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('value', $class->fieldMappings);\n        self::assertEquals($class->customRepositoryClassName, EntityRepository::class);\n    }\n\n    #[Group('DDC-388')]\n    public function testSerializationWithPrivateFieldsFromMappedSuperclass(): void\n    {\n        $class = $this->cmf->getMetadataFor(EntitySubClass2::class);\n\n        $class2 = unserialize(serialize($class));\n        $class2->wakeupReflection(new RuntimeReflectionService());\n\n        self::assertArrayHasKey('mapped1', $class2->propertyAccessors);\n        self::assertArrayHasKey('mapped2', $class2->propertyAccessors);\n        self::assertArrayHasKey('mappedRelated1', $class2->propertyAccessors);\n    }\n\n    #[Group('DDC-1203')]\n    public function testUnmappedSuperclassInHierarchy(): void\n    {\n        $class = $this->cmf->getMetadataFor(HierarchyD::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('a', $class->fieldMappings);\n        self::assertArrayHasKey('d', $class->fieldMappings);\n    }\n\n    #[Group('DDC-1204')]\n    public function testUnmappedEntityInHierarchy(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\n            'Entity \\'Doctrine\\Tests\\ORM\\Mapping\\HierarchyBEntity\\' has to be part of the discriminator map'\n            . ' of \\'Doctrine\\Tests\\ORM\\Mapping\\HierarchyBase\\' to be properly mapped in the inheritance hierarchy.'\n            . ' Alternatively you can make \\'Doctrine\\Tests\\ORM\\Mapping\\HierarchyBEntity\\' an abstract class to'\n            . ' avoid this exception from occurring.',\n        );\n\n        $this->cmf->getMetadataFor(HierarchyE::class);\n    }\n\n    #[Group('DDC-1204')]\n    #[Group('DDC-1203')]\n    public function testMappedSuperclassWithId(): void\n    {\n        $class = $this->cmf->getMetadataFor(SuperclassEntity::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n    }\n\n    #[Group('DDC-1156')]\n    #[Group('DDC-1218')]\n    #[Group('GH-10927')]\n    public function testSequenceDefinitionInHierarchyWithSandwichMappedSuperclass(): void\n    {\n        $class = $this->cmf->getMetadataFor(HierarchyD::class);\n        assert($class instanceof ClassMetadata);\n\n        self::assertInstanceOf(IdSequenceGenerator::class, $class->idGenerator);\n        self::assertEquals(\n            ['allocationSize' => 1, 'initialValue' => 10, 'sequenceName' => 'foo'],\n            $class->sequenceGeneratorDefinition,\n        );\n    }\n\n    /**\n     * Ensure indexes are inherited from the mapped superclass.\n     */\n    #[Group('DDC-3418')]\n    public function testMappedSuperclassIndex(): void\n    {\n        $class = $this->cmf->getMetadataFor(EntityIndexSubClass::class);\n        assert($class instanceof ClassMetadata);\n\n        self::assertArrayHasKey('mapped1', $class->fieldMappings);\n        self::assertArrayHasKey('IDX_NAME_INDEX', $class->table['uniqueConstraints']);\n        self::assertArrayHasKey('IDX_MAPPED1_INDEX', $class->table['uniqueConstraints']);\n        self::assertArrayHasKey('IDX_MAPPED2_INDEX', $class->table['indexes']);\n    }\n\n    #[DataProvider('invalidHierarchyDeclarationClasses')]\n    public function testUndeclaredHierarchyRejection(string $rootEntity, string $childClass): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(sprintf(\n            \"Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared.\",\n            $childClass,\n            $rootEntity,\n        ));\n\n        $this->cmf->getMetadataFor($childClass);\n    }\n\n    public static function invalidHierarchyDeclarationClasses(): Generator\n    {\n        yield 'concrete Entity root and child class, direct inheritance'\n            => [InvalidEntityRoot::class, InvalidEntityRootChild::class];\n\n        yield 'concrete Entity root and abstract child class, direct inheritance'\n            => [InvalidEntityRoot::class, InvalidEntityRootAbstractChild::class];\n\n        yield 'abstract Entity root and concrete child class, direct inheritance'\n            => [InvalidAbstractEntityRoot::class, InvalidAbstractEntityRootChild::class];\n\n        yield 'abstract Entity root and abstract child class, direct inheritance'\n            => [InvalidAbstractEntityRoot::class, InvalidAbstractEntityRootAbstractChild::class];\n\n        yield 'complex example (Entity Root -> Mapped Superclass -> transient class -> Entity)'\n            => [InvalidComplexRoot::class, InvalidComplexEntity::class];\n    }\n\n    #[Group('DDC-964')]\n    public function testInvalidOverrideFieldInheritedFromEntity(): void\n    {\n        $cm = $this->cmf->getMetadataFor(CompanyFixContract::class);\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessageMatches('/Overrides are only allowed for fields or associations declared in mapped superclasses or traits./');\n\n        $cm->setAttributeOverride('completed', ['name' => 'other_column_name']);\n    }\n\n    public function testInvalidOverrideAssociationInheritedFromEntity(): void\n    {\n        $cm = $this->cmf->getMetadataFor(CompanyFixContract::class);\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for Doctrine\\Tests\\Models\\Company\\CompanyFixContract::salesPerson, which was inherited from Doctrine\\Tests\\Models\\Company\\CompanyContract.');\n\n        $cm->setAssociationOverride('salesPerson', ['inversedBy' => 'other_inversed_by_name']);\n    }\n}\n\nclass TransientBaseClass\n{\n    /** @var mixed */\n    private $transient1;\n\n    /** @var mixed */\n    private $transient2;\n}\n\n#[Entity]\nclass EntitySubClass extends TransientBaseClass\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string $name;\n}\n\n#[MappedSuperclass]\nclass MappedSuperclassBase\n{\n    #[Column(type: 'integer')]\n    private int $mapped1;\n\n    #[Column(type: 'string', length: 255)]\n    private string $mapped2;\n\n    #[OneToOne(targetEntity: 'MappedSuperclassRelated1')]\n    #[JoinColumn(name: 'related1_id', referencedColumnName: 'id')]\n    private MappedSuperclassRelated1 $mappedRelated1;\n\n    /** @var mixed */\n    private $transient;\n}\n\n#[Entity]\nclass MappedSuperclassRelated1\n{\n}\n\n#[Entity]\nclass EntitySubClass2 extends MappedSuperclassBase\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string $name;\n}\n\n#[Table]\n#[Index(name: 'IDX_MAPPED2_INDEX', columns: ['mapped2'])]\n#[UniqueConstraint(name: 'IDX_MAPPED1_INDEX', columns: ['mapped1'])]\n#[MappedSuperclass]\nclass MappedSuperclassBaseIndex\n{\n    #[Column(type: 'string', length: 255)]\n    private string $mapped1;\n    #[Column(type: 'string', length: 255)]\n    private string $mapped2;\n}\n\n#[Table]\n#[UniqueConstraint(name: 'IDX_NAME_INDEX', columns: ['name'])]\n#[Entity]\nclass EntityIndexSubClass extends MappedSuperclassBaseIndex\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string $name;\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'type', type: 'string', length: 20)]\n#[DiscriminatorMap(['c' => 'HierarchyC', 'd' => 'HierarchyD', 'e' => 'HierarchyE'])]\nabstract class HierarchyBase\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'SEQUENCE')]\n    #[SequenceGenerator(sequenceName: 'foo', initialValue: 10)]\n    public $id;\n}\n\n#[MappedSuperclass]\nabstract class HierarchyASuperclass extends HierarchyBase\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $a;\n}\n\n#[Entity]\nclass HierarchyBEntity extends HierarchyBase\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $b;\n}\n\n#[Entity]\nclass HierarchyC extends HierarchyBase\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $c;\n}\n\n#[Entity]\nclass HierarchyD extends HierarchyASuperclass\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $d;\n}\n\n#[Entity]\nclass HierarchyE extends HierarchyBEntity\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $e;\n}\n\n#[Entity]\nclass SuperclassEntity extends SuperclassBase\n{\n}\n\n#[MappedSuperclass]\nabstract class SuperclassBase\n{\n    /** @var int */\n    #[Column(type: 'integer')]\n    #[Id]\n    #[GeneratedValue(strategy: 'SEQUENCE')]\n    #[SequenceGenerator(sequenceName: 'foo', initialValue: 10)]\n    public $id;\n}\n\n#[MappedSuperclass]\nabstract class MediumSuperclassBase extends SuperclassBase\n{\n}\n\n#[Entity]\nclass MediumSuperclassEntity extends MediumSuperclassBase\n{\n}\n\n#[Entity(repositoryClass: 'Doctrine\\ORM\\EntityRepository')]\nclass SubclassWithRepository extends DDC869Payment\n{\n}\n\n/** This class misses the DiscriminatorMap declaration */\n#[Entity]\nclass InvalidEntityRoot\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue]\n    public int|null $id = null;\n}\n\n#[Entity]\nclass InvalidEntityRootChild extends InvalidEntityRoot\n{\n}\n\n#[Entity]\nabstract class InvalidEntityRootAbstractChild extends InvalidEntityRoot\n{\n}\n\n/** This class misses the DiscriminatorMap declaration */\n#[Entity]\nclass InvalidAbstractEntityRoot\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue]\n    public int|null $id = null;\n}\n\n#[Entity]\nclass InvalidAbstractEntityRootChild extends InvalidAbstractEntityRoot\n{\n}\n\n#[Entity]\nabstract class InvalidAbstractEntityRootAbstractChild extends InvalidAbstractEntityRoot\n{\n}\n\n/** This class misses the DiscriminatorMap declaration */\n#[Entity]\nclass InvalidComplexRoot\n{\n    #[Column]\n    #[Id]\n    #[GeneratedValue]\n    public int|null $id = null;\n}\n\n#[MappedSuperclass]\nclass InvalidComplexMappedSuperclass extends InvalidComplexRoot\n{\n}\n\nclass InvalidComplexTransientClass extends InvalidComplexMappedSuperclass\n{\n}\n\n#[Entity]\nclass InvalidComplexEntity extends InvalidComplexTransientClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ClassMetadataBuilderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder;\nuse Doctrine\\ORM\\Mapping\\Builder\\EmbeddedBuilder;\nuse Doctrine\\ORM\\Mapping\\Builder\\FieldBuilder;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping;\nuse Doctrine\\ORM\\Mapping\\EmbeddedClassMapping;\nuse Doctrine\\ORM\\Mapping\\FieldMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\ValueObjects\\Name;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-659')]\nclass ClassMetadataBuilderTest extends OrmTestCase\n{\n    private ClassMetadata $cm;\n    private ClassMetadataBuilder $builder;\n\n    protected function setUp(): void\n    {\n        $this->cm = new ClassMetadata(CmsUser::class);\n        $this->cm->initializeReflection(new RuntimeReflectionService());\n        $this->builder = new ClassMetadataBuilder($this->cm);\n    }\n\n    public function testSetMappedSuperClass(): void\n    {\n        $this->assertIsFluent($this->builder->setMappedSuperClass());\n        self::assertTrue($this->cm->isMappedSuperclass);\n        self::assertFalse($this->cm->isEmbeddedClass);\n    }\n\n    public function testSetEmbedable(): void\n    {\n        $this->assertIsFluent($this->builder->setEmbeddable());\n        self::assertTrue($this->cm->isEmbeddedClass);\n        self::assertFalse($this->cm->isMappedSuperclass);\n    }\n\n    public function testAddEmbeddedWithOnlyRequiredParams(): void\n    {\n        $this->assertIsFluent($this->builder->addEmbedded('name', Name::class));\n\n        self::assertEquals(\n            [\n                'name' => EmbeddedClassMapping::fromMappingArray([\n                    'class' => Name::class,\n                    'columnPrefix' => null,\n                    'declaredField' => null,\n                    'originalField' => null,\n                ]),\n            ],\n            $this->cm->embeddedClasses,\n        );\n    }\n\n    public function testAddEmbeddedWithPrefix(): void\n    {\n        $this->assertIsFluent(\n            $this->builder->addEmbedded(\n                'name',\n                Name::class,\n                'nm_',\n            ),\n        );\n\n        self::assertEquals(\n            [\n                'name' => EmbeddedClassMapping::fromMappingArray([\n                    'class' => Name::class,\n                    'columnPrefix' => 'nm_',\n                    'declaredField' => null,\n                    'originalField' => null,\n                ]),\n            ],\n            $this->cm->embeddedClasses,\n        );\n    }\n\n    public function testCreateEmbeddedWithoutExtraParams(): void\n    {\n        $embeddedBuilder = $this->builder->createEmbedded('name', Name::class);\n        self::assertInstanceOf(EmbeddedBuilder::class, $embeddedBuilder);\n\n        self::assertFalse(isset($this->cm->embeddedClasses['name']));\n\n        $this->assertIsFluent($embeddedBuilder->build());\n        self::assertEquals(\n            EmbeddedClassMapping::fromMappingArray([\n                'class' => Name::class,\n                'columnPrefix' => null,\n                'declaredField' => null,\n                'originalField' => null,\n            ]),\n            $this->cm->embeddedClasses['name'],\n        );\n    }\n\n    public function testCreateEmbeddedWithColumnPrefix(): void\n    {\n        $embeddedBuilder = $this->builder->createEmbedded('name', Name::class);\n\n        self::assertEquals($embeddedBuilder, $embeddedBuilder->setColumnPrefix('nm_'));\n\n        $this->assertIsFluent($embeddedBuilder->build());\n\n        self::assertEquals(\n            EmbeddedClassMapping::fromMappingArray([\n                'class' => Name::class,\n                'columnPrefix' => 'nm_',\n                'declaredField' => null,\n                'originalField' => null,\n            ]),\n            $this->cm->embeddedClasses['name'],\n        );\n    }\n\n    public function testSetCustomRepositoryClass(): void\n    {\n        $this->assertIsFluent($this->builder->setCustomRepositoryClass(CmsGroup::class));\n        self::assertEquals(CmsGroup::class, $this->cm->customRepositoryClassName);\n    }\n\n    public function testSetReadOnly(): void\n    {\n        $this->assertIsFluent($this->builder->setReadOnly());\n        self::assertTrue($this->cm->isReadOnly);\n    }\n\n    public function testSetTable(): void\n    {\n        $this->assertIsFluent($this->builder->setTable('users'));\n        self::assertEquals('users', $this->cm->table['name']);\n    }\n\n    public function testAddIndex(): void\n    {\n        $this->assertIsFluent($this->builder->addIndex(['username', 'name'], 'users_idx'));\n        self::assertEquals(['users_idx' => ['columns' => ['username', 'name']]], $this->cm->table['indexes']);\n    }\n\n    public function testAddUniqueConstraint(): void\n    {\n        $this->assertIsFluent($this->builder->addUniqueConstraint(['username', 'name'], 'users_idx'));\n        self::assertEquals(['users_idx' => ['columns' => ['username', 'name']]], $this->cm->table['uniqueConstraints']);\n    }\n\n    public function testSetPrimaryTableRelated(): void\n    {\n        $this->builder->addUniqueConstraint(['username', 'name'], 'users_idx');\n        $this->builder->addIndex(['username', 'name'], 'users_idx');\n        $this->builder->setTable('users');\n\n        self::assertEquals(\n            [\n                'name' => 'users',\n                'indexes' => ['users_idx' => ['columns' => ['username', 'name']]],\n                'uniqueConstraints' => ['users_idx' => ['columns' => ['username', 'name']]],\n            ],\n            $this->cm->table,\n        );\n    }\n\n    public function testSetInheritanceJoined(): void\n    {\n        $this->assertIsFluent($this->builder->setJoinedTableInheritance());\n        self::assertEquals(ClassMetadata::INHERITANCE_TYPE_JOINED, $this->cm->inheritanceType);\n    }\n\n    public function testSetInheritanceSingleTable(): void\n    {\n        $this->assertIsFluent($this->builder->setSingleTableInheritance());\n        self::assertEquals(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE, $this->cm->inheritanceType);\n    }\n\n    public function testSetDiscriminatorColumn(): void\n    {\n        $this->assertIsFluent($this->builder->setDiscriminatorColumn('discr', 'string', 124, null, null));\n        self::assertEquals(DiscriminatorColumnMapping::fromMappingArray([\n            'fieldName' => 'discr',\n            'name' => 'discr',\n            'type' => 'string',\n            'length' => 124,\n            'columnDefinition' => null,\n            'enumType' => null,\n            'options' => [],\n        ]), $this->cm->discriminatorColumn);\n    }\n\n    public function testAddDiscriminatorMapClass(): void\n    {\n        $this->assertIsFluent($this->builder->addDiscriminatorMapClass('test', CmsUser::class));\n        $this->assertIsFluent($this->builder->addDiscriminatorMapClass('test2', CmsGroup::class));\n\n        self::assertEquals(\n            ['test' => CmsUser::class, 'test2' => CmsGroup::class],\n            $this->cm->discriminatorMap,\n        );\n        self::assertEquals('test', $this->cm->discriminatorValue);\n    }\n\n    public function testChangeTrackingPolicyExplicit(): void\n    {\n        $this->assertIsFluent($this->builder->setChangeTrackingPolicyDeferredExplicit());\n        self::assertEquals(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT, $this->cm->changeTrackingPolicy);\n    }\n\n    public function testAddField(): void\n    {\n        $this->assertIsFluent($this->builder->addField('name', 'string'));\n        $mapping = $this->cm->getFieldMapping('name');\n        self::assertSame('name', $mapping->fieldName);\n        self::assertSame('name', $mapping->columnName);\n        self::assertSame('string', $mapping->type);\n    }\n\n    public function testCreateField(): void\n    {\n        $fieldBuilder = $this->builder->createField('name', 'string');\n        self::assertInstanceOf(FieldBuilder::class, $fieldBuilder);\n\n        self::assertFalse(isset($this->cm->fieldMappings['name']));\n        $this->assertIsFluent($fieldBuilder->build());\n        $mapping = $this->cm->getFieldMapping('name');\n        self::assertSame('name', $mapping->fieldName);\n        self::assertSame('name', $mapping->columnName);\n        self::assertSame('string', $mapping->type);\n    }\n\n    public function testCreateVersionedField(): void\n    {\n        $this->builder->createField('name', 'integer')->columnName('username')->length(124)->nullable()->columnDefinition('foobar')->unique()->isVersionField()->build();\n        self::assertEquals(\n            FieldMapping::fromMappingArray([\n                'columnDefinition' => 'foobar',\n                'columnName' => 'username',\n                'options' => ['default' => 1],\n                'fieldName' => 'name',\n                'length' => 124,\n                'type' => 'integer',\n                'nullable' => true,\n                'unique' => true,\n            ]),\n            $this->cm->fieldMappings['name'],\n        );\n    }\n\n    public function testCreatePrimaryField(): void\n    {\n        $this->builder->createField('id', 'integer')->makePrimaryKey()->generatedValue()->build();\n\n        self::assertEquals(['id'], $this->cm->identifier);\n        self::assertEquals(FieldMapping::fromMappingArray(\n            ['columnName' => 'id', 'fieldName' => 'id', 'id' => true, 'type' => 'integer'],\n        ), $this->cm->fieldMappings['id']);\n    }\n\n    public function testCreateUnsignedOptionField(): void\n    {\n        $this->builder->createField('state', 'integer')->option('unsigned', true)->build();\n\n        self::assertEquals(FieldMapping::fromMappingArray(\n            ['fieldName' => 'state', 'type' => 'integer', 'options' => ['unsigned' => true], 'columnName' => 'state'],\n        ), $this->cm->fieldMappings['state']);\n    }\n\n    public function testAddLifecycleEvent(): void\n    {\n        $this->builder->addLifecycleEvent('getStatus', 'postLoad');\n\n        self::assertEquals(['postLoad' => ['getStatus']], $this->cm->lifecycleCallbacks);\n    }\n\n    public function testCreateManyToOne(): void\n    {\n        $this->assertIsFluent(\n            $this->builder->createManyToOne('groups', CmsGroup::class)\n                              ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                              ->cascadeAll()\n                              ->fetchExtraLazy()\n                              ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' => ManyToOneAssociationMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [\n                        0 => 'remove',\n                        1 => 'persist',\n                        2 => 'refresh',\n                        3 => 'detach',\n                    ],\n                    'fetch' => 4,\n                    'joinColumns' => [\n                        0 =>\n                        [\n                            'name' => 'group_id',\n                            'referencedColumnName' => 'id',\n                            'nullable' => true,\n                            'unique' => false,\n                            'onDelete' => 'CASCADE',\n                            'columnDefinition' => null,\n                        ],\n                    ],\n                    'type' => 2,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'sourceToTargetKeyColumns' =>\n                    ['group_id' => 'id'],\n                    'joinColumnFieldNames' =>\n                    ['group_id' => 'group_id'],\n                    'targetToSourceKeyColumns' =>\n                    ['id' => 'group_id'],\n                    'orphanRemoval' => false,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testCreateManyToOneWithIdentity(): void\n    {\n        $this->assertIsFluent(\n            $this\n                ->builder\n                ->createManyToOne('groups', CmsGroup::class)\n                ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                ->cascadeAll()\n                ->fetchExtraLazy()\n                ->makePrimaryKey()\n                ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' => ManyToOneAssociationMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [\n                        0 => 'remove',\n                        1 => 'persist',\n                        2 => 'refresh',\n                        3 => 'detach',\n                    ],\n                    'fetch' => 4,\n                    'joinColumns' => [\n                        0 =>\n                            [\n                                'name' => 'group_id',\n                                'referencedColumnName' => 'id',\n                                'nullable' => false,\n                                'unique' => false,\n                                'onDelete' => 'CASCADE',\n                                'columnDefinition' => null,\n                            ],\n                    ],\n                    'type' => 2,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'sourceToTargetKeyColumns' =>\n                        ['group_id' => 'id'],\n                    'joinColumnFieldNames' =>\n                        ['group_id' => 'group_id'],\n                    'targetToSourceKeyColumns' =>\n                        ['id' => 'group_id'],\n                    'orphanRemoval' => false,\n                    'id' => true,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testCreateOneToOne(): void\n    {\n        $this->assertIsFluent(\n            $this->builder->createOneToOne('groups', CmsGroup::class)\n                              ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                              ->cascadeAll()\n                              ->fetchExtraLazy()\n                              ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' => OneToOneOwningSideMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [\n                        0 => 'remove',\n                        1 => 'persist',\n                        2 => 'refresh',\n                        3 => 'detach',\n                    ],\n                    'fetch' => 4,\n                    'joinColumns' => [\n                        0 =>\n                        [\n                            'name' => 'group_id',\n                            'referencedColumnName' => 'id',\n                            'nullable' => true,\n                            'unique' => true,\n                            'onDelete' => 'CASCADE',\n                            'columnDefinition' => null,\n                        ],\n                    ],\n                    'type' => 1,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'sourceToTargetKeyColumns' =>\n                    ['group_id' => 'id'],\n                    'joinColumnFieldNames' =>\n                    ['group_id' => 'group_id'],\n                    'targetToSourceKeyColumns' =>\n                    ['id' => 'group_id'],\n                    'orphanRemoval' => false,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testCreateOneToOneWithIdentity(): void\n    {\n        $this->assertIsFluent(\n            $this\n                ->builder\n                ->createOneToOne('groups', CmsGroup::class)\n                ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                ->cascadeAll()\n                ->fetchExtraLazy()\n                ->makePrimaryKey()\n                ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' => OneToOneOwningSideMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [\n                        0 => 'remove',\n                        1 => 'persist',\n                        2 => 'refresh',\n                        3 => 'detach',\n                    ],\n                    'fetch' => 4,\n                    'id' => true,\n                    'joinColumns' => [\n                        0 =>\n                            [\n                                'name' => 'group_id',\n                                'referencedColumnName' => 'id',\n                                'nullable' => false,\n                                'unique' => false,\n                                'onDelete' => 'CASCADE',\n                                'columnDefinition' => null,\n                            ],\n                    ],\n                    'type' => 1,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'sourceToTargetKeyColumns' =>\n                        ['group_id' => 'id'],\n                    'joinColumnFieldNames' =>\n                        ['group_id' => 'group_id'],\n                    'targetToSourceKeyColumns' =>\n                        ['id' => 'group_id'],\n                    'orphanRemoval' => false,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testThrowsExceptionOnCreateOneToOneWithIdentityOnInverseSide(): void\n    {\n        $this->expectException(MappingException::class);\n\n        $this\n            ->builder\n            ->createOneToOne('groups', CmsGroup::class)\n            ->mappedBy('test')\n            ->fetchExtraLazy()\n            ->makePrimaryKey()\n            ->build();\n    }\n\n    public function testCreateManyToMany(): void\n    {\n        $this->assertIsFluent(\n            $this->builder->createManyToMany('groups', CmsGroup::class)\n                              ->setJoinTable('groups_users')\n                              ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                              ->addInverseJoinColumn('user_id', 'id')\n                              ->cascadeAll()\n                              ->fetchExtraLazy()\n                              ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' =>\n                ManyToManyOwningSideMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' =>\n                    [\n                        0 => 'remove',\n                        1 => 'persist',\n                        2 => 'refresh',\n                        3 => 'detach',\n                    ],\n                    'fetch' => 4,\n                    'joinTable' =>\n                    [\n                        'joinColumns' =>\n                        [\n                            0 =>\n                            [\n                                'name' => 'group_id',\n                                'referencedColumnName' => 'id',\n                                'unique' => false,\n                                'onDelete' => 'CASCADE',\n                                'columnDefinition' => null,\n                            ],\n                        ],\n                        'inverseJoinColumns' =>\n                        [\n                            0 =>\n                            [\n                                'name' => 'user_id',\n                                'referencedColumnName' => 'id',\n                                'unique' => false,\n                                'onDelete' => null,\n                                'columnDefinition' => null,\n                            ],\n                        ],\n                        'name' => 'groups_users',\n                    ],\n                    'type' => 8,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'isOnDeleteCascade' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'relationToSourceKeyColumns' =>\n                    ['group_id' => 'id'],\n                    'joinTableColumns' =>\n                    [\n                        0 => 'group_id',\n                        1 => 'user_id',\n                    ],\n                    'relationToTargetKeyColumns' =>\n                    ['user_id' => 'id'],\n                    'orphanRemoval' => false,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testThrowsExceptionOnCreateManyToManyWithIdentity(): void\n    {\n        $this->expectException(MappingException::class);\n\n        $this->builder->createManyToMany('groups', CmsGroup::class)\n                          ->makePrimaryKey()\n                          ->setJoinTable('groups_users')\n                          ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                          ->addInverseJoinColumn('user_id', 'id')\n                          ->cascadeAll()\n                          ->fetchExtraLazy()\n                          ->build();\n    }\n\n    public function testCreateOneToMany(): void\n    {\n        $this->assertIsFluent(\n            $this->builder->createOneToMany('groups', CmsGroup::class)\n                        ->mappedBy('test')\n                        ->setOrderBy(['test'])\n                        ->setIndexBy('test')\n                        ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' =>\n                OneToManyAssociationMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'mappedBy' => 'test',\n                    'orderBy' => [0 => 'test'],\n                    'indexBy' => 'test',\n                    'type' => 4,\n                    'isOwningSide' => false,\n                    'sourceEntity' => CmsUser::class,\n                    'fetch' => 2,\n                    'cascade' => [],\n                    'orphanRemoval' => false,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testThrowsExceptionOnCreateOneToManyWithIdentity(): void\n    {\n        $this->expectException(MappingException::class);\n\n        $this->builder->createOneToMany('groups', CmsGroup::class)\n                ->makePrimaryKey()\n                ->mappedBy('test')\n                ->setOrderBy(['test'])\n                ->setIndexBy('test')\n                ->build();\n    }\n\n    public function testOrphanRemovalOnCreateOneToOne(): void\n    {\n        $this->assertIsFluent(\n            $this->builder\n                ->createOneToOne('groups', CmsGroup::class)\n                ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n                ->orphanRemoval()\n                ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' => OneToOneOwningSideMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [],\n                    'fetch' => 2,\n                    'joinColumns' => [\n                        0 =>\n                        [\n                            'name' => 'group_id',\n                            'referencedColumnName' => 'id',\n                            'nullable' => true,\n                            'unique' => true,\n                            'onDelete' => 'CASCADE',\n                            'columnDefinition' => null,\n                        ],\n                    ],\n                    'type' => 1,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'sourceToTargetKeyColumns' =>\n                    ['group_id' => 'id'],\n                    'joinColumnFieldNames' =>\n                    ['group_id' => 'group_id'],\n                    'targetToSourceKeyColumns' =>\n                    ['id' => 'group_id'],\n                    'orphanRemoval' => true,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testOrphanRemovalOnCreateOneToMany(): void\n    {\n        $this->assertIsFluent(\n            $this->builder\n                ->createOneToMany('groups', CmsGroup::class)\n                ->mappedBy('test')\n                ->orphanRemoval()\n                ->build(),\n        );\n\n        self::assertEquals(\n            [\n                'groups' =>\n                OneToManyAssociationMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'mappedBy' => 'test',\n                    'type' => 4,\n                    'isOwningSide' => false,\n                    'sourceEntity' => CmsUser::class,\n                    'fetch' => 2,\n                    'cascade' => [],\n                    'orphanRemoval' => true,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function testExceptionOnOrphanRemovalOnManyToOne(): void\n    {\n        $this->expectException(MappingException::class);\n\n        $this->builder\n            ->createManyToOne('groups', CmsGroup::class)\n            ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n            ->orphanRemoval()\n            ->build();\n    }\n\n    public function testOrphanRemovalOnManyToMany(): void\n    {\n        $this->builder\n            ->createManyToMany('groups', CmsGroup::class)\n            ->addJoinColumn('group_id', 'id', true, false, 'CASCADE')\n            ->orphanRemoval()\n            ->build();\n\n        self::assertEquals(\n            [\n                'groups' => ManyToManyOwningSideMapping::fromMappingArray([\n                    'fieldName' => 'groups',\n                    'targetEntity' => CmsGroup::class,\n                    'cascade' => [],\n                    'fetch' => 2,\n                    'joinTable' => [\n                        'joinColumns' => [\n                            0 => [\n                                'name' => 'group_id',\n                                'referencedColumnName' => 'id',\n                                'unique' => false,\n                                'onDelete' => 'CASCADE',\n                                'columnDefinition' => null,\n                            ],\n                        ],\n                        'inverseJoinColumns' => [\n                            0 => [\n                                'name' => 'cmsgroup_id',\n                                'referencedColumnName' => 'id',\n                                'onDelete' => 'CASCADE',\n                            ],\n                        ],\n                        'name' => 'cmsuser_cmsgroup',\n                    ],\n                    'type' => 8,\n                    'inversedBy' => null,\n                    'isOwningSide' => true,\n                    'sourceEntity' => CmsUser::class,\n                    'isOnDeleteCascade' => true,\n                    'relationToSourceKeyColumns' => ['group_id' => 'id'],\n                    'joinTableColumns' => [\n                        0 => 'group_id',\n                        1 => 'cmsgroup_id',\n                    ],\n                    'relationToTargetKeyColumns' => ['cmsgroup_id' => 'id'],\n                    'orphanRemoval' => true,\n                ]),\n            ],\n            $this->cm->associationMappings,\n        );\n    }\n\n    public function assertIsFluent($ret): void\n    {\n        self::assertSame($this->builder, $ret, 'Return Value has to be same instance as used builder');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Event\\OnClassMetadataNotFoundEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Id\\AbstractIdGenerator;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Mocks\\MetadataDriverMock;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\DDC4006\\DDC4006User;\nuse Doctrine\\Tests\\Models\\JoinedInheritanceType\\AnotherChildClass;\nuse Doctrine\\Tests\\Models\\JoinedInheritanceType\\ChildClass;\nuse Doctrine\\Tests\\Models\\JoinedInheritanceType\\RootClass;\nuse Doctrine\\Tests\\Models\\Quote\\Address;\nuse Doctrine\\Tests\\Models\\Quote\\Group;\nuse Doctrine\\Tests\\Models\\Quote\\Phone;\nuse Doctrine\\Tests\\Models\\Quote\\User;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Doctrine\\Tests\\TestUtil;\nuse Exception;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Assert;\nuse ReflectionClass;\n\nuse function array_search;\nuse function assert;\nuse function count;\nuse function method_exists;\nuse function sprintf;\n\nclass ClassMetadataFactoryTest extends OrmTestCase\n{\n    public function testGetMetadataForSingleClass(): void\n    {\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('supportsSequences')\n            ->willReturn(true);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        $conn = new Connection([], $driver);\n\n        $mockDriver    = new MetadataDriverMock();\n        $entityManager = $this->createEntityManager($mockDriver, $conn);\n\n        $cm1 = $this->createValidClassMetadata();\n\n        // SUT\n        $cmf = new ClassMetadataFactory();\n        $cmf->setEntityManager($entityManager);\n        $cmf->setMetadataFor($cm1->name, $cm1);\n\n        // Prechecks\n        self::assertEquals([], $cm1->parentClasses);\n        self::assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm1->inheritanceType);\n        self::assertTrue($cm1->hasField('name'));\n        self::assertEquals(2, count($cm1->associationMappings));\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $cm1->generatorType);\n        self::assertEquals('group', $cm1->table['name']);\n\n        // Go\n        $cmMap1 = $cmf->getMetadataFor($cm1->name);\n\n        self::assertSame($cm1, $cmMap1);\n        self::assertEquals('group', $cmMap1->table['name']);\n        self::assertTrue($cmMap1->table['quoted']);\n        self::assertEquals([], $cmMap1->parentClasses);\n        self::assertTrue($cmMap1->hasField('name'));\n    }\n\n    public function testGetMetadataForReturnsLoadedCustomIdGenerator(): void\n    {\n        $cm1 = $this->createValidClassMetadata();\n        $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);\n        $cm1->customGeneratorDefinition = ['class' => CustomIdGenerator::class];\n        $cmf                            = $this->createTestFactory();\n        $cmf->setMetadataForClass($cm1->name, $cm1);\n\n        $actual = $cmf->getMetadataFor($cm1->name);\n\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_CUSTOM, $actual->generatorType);\n        self::assertInstanceOf(CustomIdGenerator::class, $actual->idGenerator);\n    }\n\n    /** @param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $preferences */\n    private function setUpCmfForPlatform(AbstractPlatform $platform, array $preferences = []): ClassMetadataFactoryTestSubject\n    {\n        $cmf    = new ClassMetadataFactoryTestSubject();\n        $driver = $this->createMock(Driver::class);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n        $entityManager = $this->createEntityManager(\n            new MetadataDriverMock(),\n            new Connection([], $driver),\n        );\n        $cmf->setEntityManager($entityManager);\n        $entityManager->getConfiguration()->setIdentityGenerationPreferences($preferences);\n\n        return $cmf;\n    }\n\n    public function testPostgresSticksWithSequencesWhenDbal3IsUsed(): void\n    {\n        if (! method_exists(AbstractPlatform::class, 'getIdentitySequenceName')) {\n            self::markTestSkipped('This test requires DBAL 3');\n        }\n\n        $cm = $this->createValidClassMetadata();\n        $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n        $cmf = $this->setUpCmfForPlatform(new PostgreSQLPlatform());\n        $cmf->setMetadataForClass($cm->name, $cm);\n\n        $metadata = $cmf->getMetadataFor($cm->name);\n\n        self::assertSame(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $metadata->generatorType);\n    }\n\n    public function testPostgresSwitchesToIdentityColumnsWhenDbal4IsUsed(): void\n    {\n        if (method_exists(AbstractPlatform::class, 'getIdentitySequenceName')) {\n            self::markTestSkipped('This test requires DBAL 4');\n        }\n\n        $cm = $this->createValidClassMetadata();\n        $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n        $cmf = $this->setUpCmfForPlatform(new PostgreSQLPlatform());\n        $cmf->setMetadataForClass($cm->name, $cm);\n\n        $metadata = $cmf->getMetadataFor($cm->name);\n\n        self::assertSame(ClassMetadata::GENERATOR_TYPE_IDENTITY, $metadata->generatorType);\n    }\n\n    public function testGetMetadataForThrowsExceptionOnUnknownCustomGeneratorClass(): void\n    {\n        $cm1 = $this->createValidClassMetadata();\n        $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);\n        $cm1->customGeneratorDefinition = ['class' => 'NotExistingGenerator'];\n        $cmf                            = $this->createTestFactory();\n        $cmf->setMetadataForClass($cm1->name, $cm1);\n        $this->expectException(ORMException::class);\n\n        $actual = $cmf->getMetadataFor($cm1->name);\n    }\n\n    public function testGetMetadataForThrowsExceptionOnMissingCustomGeneratorDefinition(): void\n    {\n        $cm1 = $this->createValidClassMetadata();\n        $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);\n        $cmf = $this->createTestFactory();\n        $cmf->setMetadataForClass($cm1->name, $cm1);\n        $this->expectException(ORMException::class);\n\n        $actual = $cmf->getMetadataFor($cm1->name);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1512')]\n    public function testIsTransient(): void\n    {\n        $cmf    = new ClassMetadataFactory();\n        $driver = $this->createMock(MappingDriver::class);\n        $driver->expects(self::exactly(2))\n            ->method('isTransient')\n            ->willReturnMap([\n                [CmsUser::class, true],\n                [CmsArticle::class, false],\n            ]);\n\n        $em = $this->createEntityManager($driver);\n\n        self::assertTrue($em->getMetadataFactory()->isTransient(CmsUser::class));\n        self::assertFalse($em->getMetadataFactory()->isTransient(CmsArticle::class));\n    }\n\n    public function testAddDefaultDiscriminatorMap(): void\n    {\n        self::markTestSkipped('This test is just incorrect and must be fixed');\n\n        $cmf    = new ClassMetadataFactory();\n        $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/JoinedInheritanceType/']);\n        $em     = $this->createEntityManager($driver);\n        $cmf->setEntityManager($em);\n\n        $rootMetadata                 = $cmf->getMetadataFor(RootClass::class);\n        $childMetadata                = $cmf->getMetadataFor(ChildClass::class);\n        $anotherChildMetadata         = $cmf->getMetadataFor(AnotherChildClass::class);\n        $rootDiscriminatorMap         = $rootMetadata->discriminatorMap;\n        $childDiscriminatorMap        = $childMetadata->discriminatorMap;\n        $anotherChildDiscriminatorMap = $anotherChildMetadata->discriminatorMap;\n\n        $rootClass         = RootClass::class;\n        $childClass        = ChildClass::class;\n        $anotherChildClass = AnotherChildClass::class;\n\n        $rootClassKey         = array_search($rootClass, $rootDiscriminatorMap, true);\n        $childClassKey        = array_search($childClass, $rootDiscriminatorMap, true);\n        $anotherChildClassKey = array_search($anotherChildClass, $rootDiscriminatorMap, true);\n\n        self::assertEquals('rootclass', $rootClassKey);\n        self::assertEquals('childclass', $childClassKey);\n        self::assertEquals('anotherchildclass', $anotherChildClassKey);\n\n        self::assertEquals($childDiscriminatorMap, $rootDiscriminatorMap);\n        self::assertEquals($anotherChildDiscriminatorMap, $rootDiscriminatorMap);\n    }\n\n    public function testGetAllMetadataWorksWithBadConnection(): void\n    {\n        // DDC-3551\n        $conn = $this->createMock(Connection::class);\n\n        if (method_exists($conn, 'getEventManager')) {\n            $conn->method('getEventManager')\n                ->willReturn(new EventManager());\n        }\n\n        $mockDriver = new MetadataDriverMock();\n        $em         = $this->createEntityManager($mockDriver, $conn);\n\n        $conn->expects(self::any())\n            ->method('getDatabasePlatform')\n            ->willThrowException(new Exception('Exception thrown in test when calling getDatabasePlatform'));\n\n        $cmf = new ClassMetadataFactory();\n        $cmf->setEntityManager($em);\n\n        // getting all the metadata should work, even if get DatabasePlatform blows up\n        $metadata = $cmf->getAllMetadata();\n        // this will just be an empty array - there was no error\n        self::assertEquals([], $metadata);\n    }\n\n    protected function createEntityManager(MappingDriver $metadataDriver, $conn = null): EntityManagerMock\n    {\n        $config = new Configuration();\n        TestUtil::configureProxies($config);\n        $eventManager = new EventManager();\n        if (! $conn) {\n            $platform = $this->createMock(AbstractPlatform::class);\n            $platform->method('supportsIdentityColumns')\n                ->willReturn(true);\n\n            $driver = $this->createMock(Driver::class);\n            $driver->method('getDatabasePlatform')\n                ->willReturn($platform);\n\n            $conn = new Connection([], $driver, $config, $eventManager);\n        }\n\n        $config->setMetadataDriverImpl($metadataDriver);\n\n        return new EntityManagerMock($conn, $config);\n    }\n\n    protected function createTestFactory(): ClassMetadataFactoryTestSubject\n    {\n        $mockDriver    = new MetadataDriverMock();\n        $entityManager = $this->createEntityManager($mockDriver);\n        $cmf           = new ClassMetadataFactoryTestSubject();\n        $cmf->setEntityManager($entityManager);\n\n        return $cmf;\n    }\n\n    protected function createValidClassMetadata(): ClassMetadata\n    {\n        // Self-made metadata\n        $cm1 = new ClassMetadata(TestEntity1::class);\n        $cm1->initializeReflection(new RuntimeReflectionService());\n        $cm1->setPrimaryTable(['name' => '`group`']);\n        // Add a mapped field\n        $cm1->mapField(['fieldName' => 'name', 'type' => 'string']);\n        // Add a mapped field\n        $cm1->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]);\n        // and a mapped association\n        $cm1->mapOneToOne(['fieldName' => 'other', 'targetEntity' => 'TestEntity1', 'mappedBy' => 'this']);\n        // and an association on the owning side\n        $joinColumns = [\n            ['name' => 'other_id', 'referencedColumnName' => 'id'],\n        ];\n        $cm1->mapOneToOne(\n            ['fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns],\n        );\n        // and an id generator type\n        $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        return $cm1;\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1845')]\n    public function testQuoteMetadata(): void\n    {\n        $cmf    = new ClassMetadataFactory();\n        $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/Quote/']);\n        $em     = $this->createEntityManager($driver);\n        $cmf->setEntityManager($em);\n\n        $userMetadata    = $cmf->getMetadataFor(User::class);\n        $phoneMetadata   = $cmf->getMetadataFor(Phone::class);\n        $groupMetadata   = $cmf->getMetadataFor(Group::class);\n        $addressMetadata = $cmf->getMetadataFor(Address::class);\n\n        // Phone Class Metadata\n        self::assertTrue($phoneMetadata->fieldMappings['number']->quoted);\n        self::assertEquals('phone-number', $phoneMetadata->fieldMappings['number']->columnName);\n\n        $user = $phoneMetadata->associationMappings['user'];\n        self::assertTrue($user->joinColumns[0]->quoted);\n        self::assertEquals('user-id', $user->joinColumns[0]->name);\n        self::assertEquals('user-id', $user->joinColumns[0]->referencedColumnName);\n\n        // User Group Metadata\n        self::assertTrue($groupMetadata->fieldMappings['id']->quoted);\n        self::assertTrue($groupMetadata->fieldMappings['name']->quoted);\n\n        self::assertEquals('user-id', $userMetadata->fieldMappings['id']->columnName);\n        self::assertEquals('user-name', $userMetadata->fieldMappings['name']->columnName);\n\n        $user = $groupMetadata->associationMappings['parent'];\n        self::assertTrue($user->joinColumns[0]->quoted);\n        self::assertEquals('parent-id', $user->joinColumns[0]->name);\n        self::assertEquals('group-id', $user->joinColumns[0]->referencedColumnName);\n\n        // Address Class Metadata\n        self::assertTrue($addressMetadata->fieldMappings['id']->quoted);\n        self::assertTrue($addressMetadata->fieldMappings['zip']->quoted);\n\n        self::assertEquals('address-id', $addressMetadata->fieldMappings['id']->columnName);\n        self::assertEquals('address-zip', $addressMetadata->fieldMappings['zip']->columnName);\n\n        $user = $addressMetadata->associationMappings['user'];\n        self::assertTrue($user->joinColumns[0]->quoted);\n        self::assertEquals('user-id', $user->joinColumns[0]->name);\n        self::assertEquals('user-id', $user->joinColumns[0]->referencedColumnName);\n\n        // User Class Metadata\n        self::assertTrue($userMetadata->fieldMappings['id']->quoted);\n        self::assertTrue($userMetadata->fieldMappings['name']->quoted);\n\n        self::assertEquals('user-id', $userMetadata->fieldMappings['id']->columnName);\n        self::assertEquals('user-name', $userMetadata->fieldMappings['name']->columnName);\n\n        $groups = $userMetadata->associationMappings['groups'];\n        self::assertTrue($groups->joinTable->quoted);\n        self::assertTrue($groups->joinTable->joinColumns[0]->quoted);\n        self::assertEquals('quote-users-groups', $groups->joinTable->name);\n        self::assertEquals('user-id', $groups->joinTable->joinColumns[0]->name);\n        self::assertEquals('user-id', $groups->joinTable->joinColumns[0]->referencedColumnName);\n\n        self::assertTrue($groups->joinTable->inverseJoinColumns[0]->quoted);\n        self::assertEquals('group-id', $groups->joinTable->inverseJoinColumns[0]->name);\n        self::assertEquals('group-id', $groups->joinTable->inverseJoinColumns[0]->referencedColumnName);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-3385')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('1181')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('385')]\n    public function testFallbackLoadingCausesEventTriggeringThatCanModifyFetchedMetadata(): void\n    {\n        $metadata = $this->createMock(ClassMetadata::class);\n        assert($metadata instanceof ClassMetadata);\n        $cmf          = new ClassMetadataFactory();\n        $mockDriver   = new MetadataDriverMock();\n        $em           = $this->createEntityManager($mockDriver);\n        $eventManager = $em->getEventManager();\n\n        $cmf->setEntityManager($em);\n\n        $eventManager->addEventListener([Events::onClassMetadataNotFound], new class ($metadata, $em) {\n            /** @var ClassMetadata */\n            private $metadata;\n            /** @var EntityManagerInterface */\n            private $em;\n\n            public function __construct(ClassMetadata $metadata, EntityManagerInterface $em)\n            {\n                $this->metadata = $metadata;\n                $this->em       = $em;\n            }\n\n            public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args): void\n            {\n                Assert::assertNull($args->getFoundMetadata());\n                Assert::assertSame('Foo', $args->getClassName());\n                Assert::assertSame($this->em, $args->getObjectManager());\n\n                $args->setFoundMetadata($this->metadata);\n            }\n        });\n\n        self::assertSame($metadata, $cmf->getMetadataFor('Foo'));\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-3427')]\n    public function testAcceptsEntityManagerInterfaceInstances(): void\n    {\n        $classMetadataFactory = new ClassMetadataFactory();\n\n        $entityManager = $this->createMock(EntityManagerInterface::class);\n        $classMetadataFactory->setEntityManager($entityManager);\n\n        // not really the cleanest way to check it, but we won't add a getter to the CMF just for the sake of testing.\n        $class    = new ReflectionClass(ClassMetadataFactory::class);\n        $property = $class->getProperty('em');\n        self::assertSame($entityManager, $property->getValue($classMetadataFactory));\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-4006')]\n    public function testInheritsIdGeneratorMappingFromEmbeddable(): void\n    {\n        $cmf    = new ClassMetadataFactory();\n        $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/DDC4006/']);\n        $em     = $this->createEntityManager($driver);\n        $cmf->setEntityManager($em);\n\n        $userMetadata = $cmf->getMetadataFor(DDC4006User::class);\n\n        self::assertTrue($userMetadata->isIdGeneratorIdentity());\n    }\n\n    public function testInvalidSubClassCase(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Entity class \\'Doctrine\\Tests\\ORM\\Mapping\\cube\\' used in the discriminator map of class \\'Doctrine\\Tests\\ORM\\Mapping\\Shape\\' does not exist.');\n\n        $cmf    = new ClassMetadataFactory();\n        $driver = $this->createAttributeDriver([__DIR__]);\n        $em     = $this->createEntityManager($driver);\n        $cmf->setEntityManager($em);\n\n        $userMetadata = $cmf->getMetadataFor(Shape::class);\n    }\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['cube' => cube::class])]\n#[DiscriminatorColumn(name: 'discr', length: 32, type: 'string')]\nabstract class Shape\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\nfinal class Cube extends Shape\n{\n}\n\n/* Test subject class with overridden factory method for mocking purposes */\nclass ClassMetadataFactoryTestSubject extends ClassMetadataFactory\n{\n    /** @var array<class-string<object>, ClassMetadata> */\n    private array $mockMetadata = [];\n\n    protected function newClassMetadataInstance(string $className): ClassMetadata\n    {\n        if (! isset($this->mockMetadata[$className])) {\n            throw new InvalidArgumentException(sprintf(\n                'No mock metadata found for class %s.',\n                $className,\n            ));\n        }\n\n        return $this->mockMetadata[$className];\n    }\n\n    /** @param class-string<object> $className */\n    public function setMetadataForClass(string $className, ClassMetadata $metadata): void\n    {\n        $this->mockMetadata[$className] = $metadata;\n    }\n}\n\nclass TestEntity1\n{\n    private int $id;\n\n    private string $name;\n\n    /** @var mixed */\n    private $other;\n\n    /** @var mixed */\n    private $association;\n\n    /** @var mixed */\n    private $embedded;\n}\n\nclass CustomIdGenerator extends AbstractIdGenerator\n{\n    public function generateId(EntityManagerInterface $em, object|null $entity): string\n    {\n        return 'foo';\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ClassMetadataLoadEventTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Event\\LoadClassMetadataEventArgs;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessor;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass ClassMetadataLoadEventTest extends OrmTestCase\n{\n    #[Group('DDC-1610')]\n    public function testEvent(): void\n    {\n        $em              = $this->getTestEntityManager();\n        $metadataFactory = $em->getMetadataFactory();\n        $evm             = $em->getEventManager();\n        $evm->addEventListener(Events::loadClassMetadata, $this);\n        $classMetadata = $metadataFactory->getMetadataFor(LoadEventTestEntity::class);\n        self::assertTrue($classMetadata->hasField('about'));\n        self::assertArrayHasKey('about', $classMetadata->propertyAccessors);\n        self::assertInstanceOf(PropertyAccessor::class, $classMetadata->propertyAccessors['about']);\n    }\n\n    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void\n    {\n        $classMetadata = $eventArgs->getClassMetadata();\n        $field         = [\n            'fieldName' => 'about',\n            'type' => 'string',\n            'length' => 255,\n        ];\n        $classMetadata->mapField($field);\n    }\n}\n\n#[Table(name: 'load_event_test_entity')]\n#[Entity]\nclass LoadEventTestEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    #[Column(type: 'string', length: 255)]\n    private string $name;\n\n    /** @var mixed */\n    private $about;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ClassMetadataTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse ArrayObject;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ChainTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping;\nuse Doctrine\\ORM\\Mapping\\Driver\\XmlDriver;\nuse Doctrine\\ORM\\Mapping\\JoinTableMapping;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Persistence\\Mapping\\StaticReflectionService;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObjectType;\nuse Doctrine\\Tests\\DbalTypes\\CustomIntType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\CMS\\One;\nuse Doctrine\\Tests\\Models\\CMS\\Three;\nuse Doctrine\\Tests\\Models\\CMS\\Two;\nuse Doctrine\\Tests\\Models\\CMS\\UserRepository;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContractListener;\nuse Doctrine\\Tests\\Models\\Customer\\CustomerType;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\Models\\DDC6412\\DDC6412File;\nuse Doctrine\\Tests\\Models\\DDC964\\DDC964Admin;\nuse Doctrine\\Tests\\Models\\DDC964\\DDC964Guest;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\AbstractContentItem;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\Directory;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLeg;\nuse Doctrine\\Tests\\Models\\TypedProperties\\Contact;\nuse Doctrine\\Tests\\Models\\TypedProperties\\UserTyped;\nuse Doctrine\\Tests\\Models\\TypedProperties\\UserTypedWithCustomTypedField;\nuse Doctrine\\Tests\\ORM\\Mapping\\TypedFieldMapper\\CustomIntAsStringTypedFieldMapper;\nuse Doctrine\\Tests\\OrmTestCase;\nuse DoctrineGlobalArticle;\nuse LogicException;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group as TestGroup;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse ReflectionClass;\nuse stdClass;\n\nuse function array_keys;\nuse function assert;\nuse function class_exists;\nuse function count;\nuse function defined;\nuse function serialize;\nuse function str_contains;\nuse function str_replace;\nuse function strtolower;\nuse function strtoupper;\nuse function unserialize;\n\nuse const CASE_UPPER;\n\nrequire_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';\n\nclass ClassMetadataTest extends OrmTestCase\n{\n    use VerifyDeprecations;\n\n    public function testClassMetadataInstanceSerialization(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        // Test initial state\n        self::assertTrue(count($cm->getPropertyAccessors()) === 0);\n        self::assertInstanceOf(ReflectionClass::class, $cm->reflClass);\n        self::assertEquals(CmsUser::class, $cm->name);\n        self::assertEquals(CmsUser::class, $cm->rootEntityName);\n        self::assertEquals([], $cm->subClasses);\n        self::assertEquals([], $cm->parentClasses);\n        self::assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType);\n\n        // Customize state\n        $cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);\n        $cm->setSubclasses(['One', 'Two', 'Three']);\n        $cm->setParentClasses(['UserParent']);\n        $cm->setCustomRepositoryClass('UserRepository');\n        $cm->setDiscriminatorColumn(['name' => 'disc', 'type' => 'integer']);\n        $cm->mapOneToOne(['fieldName' => 'phonenumbers', 'targetEntity' => 'CmsAddress', 'mappedBy' => 'foo']);\n        $cm->markReadOnly();\n        $cm->mapField(['fieldName' => 'status', 'notInsertable' => true, 'generated' => ClassMetadata::GENERATED_ALWAYS]);\n\n        self::assertTrue($cm->requiresFetchAfterChange);\n        self::assertEquals(1, count($cm->associationMappings));\n\n        $serialized = serialize($cm);\n        $cm         = unserialize($serialized);\n        $cm->wakeupReflection(new RuntimeReflectionService());\n\n        // Check state\n        self::assertTrue(count($cm->getPropertyAccessors()) > 0);\n        self::assertEquals('Doctrine\\Tests\\Models\\CMS', $cm->namespace);\n        self::assertInstanceOf(ReflectionClass::class, $cm->reflClass);\n        self::assertEquals(CmsUser::class, $cm->name);\n        self::assertEquals('UserParent', $cm->rootEntityName);\n        self::assertEquals([One::class, Two::class, Three::class], $cm->subClasses);\n        self::assertEquals(['UserParent'], $cm->parentClasses);\n        self::assertEquals(UserRepository::class, $cm->customRepositoryClassName);\n        self::assertEquals(DiscriminatorColumnMapping::fromMappingArray([\n            'name' => 'disc',\n            'type' => 'integer',\n            'fieldName' => 'disc',\n        ]), $cm->discriminatorColumn);\n        self::assertTrue($cm->associationMappings['phonenumbers']->isOneToOne());\n        self::assertEquals(1, count($cm->associationMappings));\n        $oneOneMapping = $cm->getAssociationMapping('phonenumbers');\n        self::assertTrue($oneOneMapping->fetch === ClassMetadata::FETCH_LAZY);\n        self::assertEquals('phonenumbers', $oneOneMapping->fieldName);\n        self::assertEquals(CmsAddress::class, $oneOneMapping->targetEntity);\n        self::assertTrue($cm->isReadOnly);\n        self::assertTrue($cm->requiresFetchAfterChange);\n    }\n\n    public function testFieldIsNullable(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        // Explicit Nullable\n        $cm->mapField(['fieldName' => 'status', 'nullable' => true, 'type' => 'string', 'length' => 50]);\n        self::assertTrue($cm->isNullable('status'));\n\n        // Explicit Not Nullable\n        $cm->mapField(['fieldName' => 'username', 'nullable' => false, 'type' => 'string', 'length' => 50]);\n        self::assertFalse($cm->isNullable('username'));\n\n        // Implicit Not Nullable\n        $cm->mapField(['fieldName' => 'name', 'type' => 'string', 'length' => 50]);\n        self::assertFalse($cm->isNullable('name'), 'By default a field should not be nullable.');\n    }\n\n    public function testFieldIsNullableByType(): void\n    {\n        $cm = new ClassMetadata(UserTyped::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapOneToOne(['fieldName' => 'email', 'joinColumns' => [['name' => 'email_id', 'referencedColumnName' => 'id']]]);\n        self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('email')->targetEntity);\n\n        $cm->mapManyToOne(['fieldName' => 'mainEmail']);\n        self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('mainEmail')->targetEntity);\n\n        $cm->mapEmbedded(['fieldName' => 'contact']);\n        self::assertEquals(Contact::class, $cm->embeddedClasses['contact']->class);\n    }\n\n    public function testFieldTypeFromReflection(): void\n    {\n        $cm = new ClassMetadata(UserTyped::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        // Integer\n        $cm->mapField(['fieldName' => 'id']);\n        self::assertEquals('integer', $cm->getTypeOfField('id'));\n\n        // String\n        $cm->mapField(['fieldName' => 'username', 'length' => 50]);\n        self::assertEquals('string', $cm->getTypeOfField('username'));\n\n        // DateInterval object\n        $cm->mapField(['fieldName' => 'dateInterval']);\n        self::assertEquals('dateinterval', $cm->getTypeOfField('dateInterval'));\n\n        // DateTime object\n        $cm->mapField(['fieldName' => 'dateTime']);\n        self::assertEquals('datetime', $cm->getTypeOfField('dateTime'));\n\n        // DateTimeImmutable object\n        $cm->mapField(['fieldName' => 'dateTimeImmutable']);\n        self::assertEquals('datetime_immutable', $cm->getTypeOfField('dateTimeImmutable'));\n\n        // array as JSON\n        $cm->mapField(['fieldName' => 'array']);\n        self::assertEquals('json', $cm->getTypeOfField('array'));\n\n        // bool\n        $cm->mapField(['fieldName' => 'boolean']);\n        self::assertEquals('boolean', $cm->getTypeOfField('boolean'));\n\n        // float\n        $cm->mapField(['fieldName' => 'float']);\n        self::assertEquals('float', $cm->getTypeOfField('float'));\n\n        // number, requires DBAL 4.3+\n        if (defined(Types::class . '::NUMBER')) {\n            $cm->mapField(['fieldName' => 'bodyHeight']);\n            self::assertEquals('number', $cm->getTypeOfField('bodyHeight'));\n        }\n    }\n\n    #[TestGroup('GH10313')]\n    public function testFieldTypeFromReflectionDefaultTypedFieldMapper(): void\n    {\n        $cm = new ClassMetadata(\n            UserTypedWithCustomTypedField::class,\n            null,\n            new DefaultTypedFieldMapper(\n                [\n                    CustomIdObject::class => CustomIdObjectType::class,\n                    'int' => CustomIntType::class,\n                ],\n            ),\n        );\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'customId']);\n        $cm->mapField(['fieldName' => 'customIntTypedField']);\n        self::assertEquals(CustomIdObjectType::class, $cm->getTypeOfField('customId'));\n        self::assertEquals(CustomIntType::class, $cm->getTypeOfField('customIntTypedField'));\n    }\n\n    #[TestGroup('GH10313')]\n    public function testFieldTypeFromReflectionChainTypedFieldMapper(): void\n    {\n        $cm = new ClassMetadata(\n            UserTyped::class,\n            null,\n            new ChainTypedFieldMapper(\n                new CustomIntAsStringTypedFieldMapper(),\n                new DefaultTypedFieldMapper(),\n            ),\n        );\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'id']);\n        $cm->mapField(['fieldName' => 'username']);\n        self::assertEquals(Types::STRING, $cm->getTypeOfField('id'));\n        self::assertEquals(Types::STRING, $cm->getTypeOfField('username'));\n    }\n\n    #[TestGroup('DDC-115')]\n    public function testMapAssociationInGlobalNamespace(): void\n    {\n        require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';\n\n        $cm = new ClassMetadata('DoctrineGlobalArticle');\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'author',\n                'targetEntity' => 'DoctrineGlobalUser',\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [['name' => 'bar_id', 'referencedColumnName' => 'id']],\n                    'inverseJoinColumns' => [['name' => 'baz_id', 'referencedColumnName' => 'id']],\n                ],\n            ],\n        );\n\n        self::assertEquals('DoctrineGlobalUser', $cm->associationMappings['author']->targetEntity);\n    }\n\n    public function testMapManyToManyJoinTableDefaults(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'groups',\n                'targetEntity' => 'CmsGroup',\n            ],\n        );\n\n        $assoc = $cm->associationMappings['groups'];\n        self::assertEquals(\n            JoinTableMapping::fromMappingArray([\n                'name' => 'cmsuser_cmsgroup',\n                'joinColumns' => [['name' => 'cmsuser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']],\n                'inverseJoinColumns' => [['name' => 'cmsgroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']],\n            ]),\n            $assoc->joinTable,\n        );\n        self::assertTrue($assoc->isOnDeleteCascade);\n    }\n\n    public function testSerializeManyToManyJoinTableCascade(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'groups',\n                'targetEntity' => 'CmsGroup',\n            ],\n        );\n\n        $assoc = $cm->associationMappings['groups'];\n        $assoc = unserialize(serialize($assoc));\n\n        self::assertTrue($assoc->isOnDeleteCascade);\n    }\n\n    #[TestGroup('DDC-115')]\n    public function testSetDiscriminatorMapInGlobalNamespace(): void\n    {\n        require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';\n\n        $cm = new ClassMetadata('DoctrineGlobalUser');\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->setDiscriminatorMap(['descr' => 'DoctrineGlobalArticle', 'foo' => 'DoctrineGlobalUser']);\n\n        self::assertEquals('DoctrineGlobalArticle', $cm->discriminatorMap['descr']);\n        self::assertEquals('DoctrineGlobalUser', $cm->discriminatorMap['foo']);\n    }\n\n    #[TestGroup('DDC-115')]\n    public function testSetSubClassesInGlobalNamespace(): void\n    {\n        require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';\n\n        $cm = new ClassMetadata('DoctrineGlobalUser');\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->setSubclasses(['DoctrineGlobalArticle']);\n\n        self::assertEquals('DoctrineGlobalArticle', $cm->subClasses[0]);\n    }\n\n    #[TestGroup('DDC-268')]\n    public function testSetInvalidVersionMappingThrowsException(): void\n    {\n        $field              = [];\n        $field['fieldName'] = 'foo';\n        $field['type']      = 'string';\n\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $cm->setVersionMapping($field);\n    }\n\n    public function testItThrowsOnJoinColumnForInverseOneToOne(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\n            'Doctrine\\Tests\\Models\\CMS\\CmsUser#address is a OneToOne inverse side, which does not allow join columns.',\n        );\n        $cm->mapOneToOne([\n            'fieldName' => 'address',\n            'targetEntity' => CmsAddress::class,\n            'mappedBy' => 'user',\n            'joinColumns' => ['non', 'empty', 'list'],\n        ]);\n    }\n\n    public function testGetSingleIdentifierFieldNameMultipleIdentifierEntityThrowsException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->isIdentifierComposite = true;\n\n        $this->expectException(MappingException::class);\n        $cm->getSingleIdentifierFieldName();\n    }\n\n    public function testGetSingleIdentifierFieldNameNoIdEntityThrowsException(): void\n    {\n        $cm = new ClassMetadata(DDC6412File::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $cm->getSingleIdentifierFieldName();\n    }\n\n    public function testDuplicateAssociationMappingException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $a1 = OneToManyAssociationMapping::fromMappingArray([\n            'fieldName' => 'foo',\n            'sourceEntity' => stdClass::class,\n            'targetEntity' => stdClass::class,\n            'mappedBy' => 'foo',\n        ]);\n        $a2 = OneToManyAssociationMapping::fromMappingArray([\n            'fieldName' => 'foo',\n            'sourceEntity' => stdClass::class,\n            'targetEntity' => stdClass::class,\n            'mappedBy' => 'foo',\n        ]);\n\n        $cm->addInheritedAssociationMapping($a1);\n        $this->expectException(MappingException::class);\n        $cm->addInheritedAssociationMapping($a2);\n    }\n\n    public function testDuplicateColumnNameThrowsMappingException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n\n        $this->expectException(MappingException::class);\n        $cm->mapField(['fieldName' => 'username', 'columnName' => 'name']);\n    }\n\n    public function testDuplicateColumnNameDiscriminatorColumnThrowsMappingException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n\n        $this->expectException(MappingException::class);\n        $cm->setDiscriminatorColumn(['name' => 'name']);\n    }\n\n    public function testDuplicateColumnNameDiscriminatorColumn2ThrowsMappingException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->setDiscriminatorColumn(['name' => 'name']);\n\n        $this->expectException(MappingException::class);\n        $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n    }\n\n    public function testDuplicateFieldAndAssociationMapping1ThrowsException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n\n        $this->expectException(MappingException::class);\n        $cm->mapOneToOne(['fieldName' => 'name', 'targetEntity' => 'CmsUser']);\n    }\n\n    public function testDuplicateFieldAndAssociationMapping2ThrowsException(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapOneToOne(['fieldName' => 'name', 'targetEntity' => 'CmsUser']);\n\n        $this->expectException(MappingException::class);\n        $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']);\n    }\n\n    #[TestGroup('DDC-1224')]\n    public function testGetTemporaryTableNameSchema(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->setTableName('foo.bar');\n\n        self::assertEquals('foo_bar_id_tmp', $cm->getTemporaryIdTableName());\n    }\n\n    public function testDefaultTableName(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        // When table's name is not given\n        $primaryTable = [];\n        $cm->setPrimaryTable($primaryTable);\n\n        self::assertEquals('CmsUser', $cm->getTableName());\n        self::assertEquals('CmsUser', $cm->table['name']);\n\n        $cm = new ClassMetadata(CmsAddress::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        // When joinTable's name is not given\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'user',\n                'targetEntity' => 'CmsUser',\n                'inversedBy' => 'users',\n                'joinTable' => [\n                    'joinColumns' => [['referencedColumnName' => 'id']],\n                    'inverseJoinColumns' => [['referencedColumnName' => 'id']],\n                ],\n            ],\n        );\n        self::assertEquals('cmsaddress_cmsuser', $cm->associationMappings['user']->joinTable->name);\n    }\n\n    public function testDefaultJoinColumnName(): void\n    {\n        $cm = new ClassMetadata(CmsAddress::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        // this is really dirty, but it's the simplest way to test whether\n        // joinColumn's name will be automatically set to user_id\n        $cm->mapOneToOne(\n            [\n                'fieldName' => 'user',\n                'targetEntity' => 'CmsUser',\n                'joinColumns' => [['referencedColumnName' => 'id']],\n            ],\n        );\n        self::assertEquals('user_id', $cm->associationMappings['user']->joinColumns[0]->name);\n\n        $cm = new ClassMetadata(CmsAddress::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'user',\n                'targetEntity' => 'CmsUser',\n                'inversedBy' => 'users',\n                'joinTable' => [\n                    'name' => 'user_CmsUser',\n                    'joinColumns' => [['referencedColumnName' => 'id']],\n                    'inverseJoinColumns' => [['referencedColumnName' => 'id']],\n                ],\n            ],\n        );\n        self::assertEquals('cmsaddress_id', $cm->associationMappings['user']->joinTable->joinColumns[0]->name);\n        self::assertEquals('cmsuser_id', $cm->associationMappings['user']->joinTable->inverseJoinColumns[0]->name);\n    }\n\n    #[TestGroup('DDC-559')]\n    public function testUnderscoreNamingStrategyDefaults(): void\n    {\n        $namingStrategy     = new UnderscoreNamingStrategy(CASE_UPPER);\n        $oneToOneMetadata   = new ClassMetadata(CmsAddress::class, $namingStrategy);\n        $manyToManyMetadata = new ClassMetadata(CmsAddress::class, $namingStrategy);\n\n        $oneToOneMetadata->mapOneToOne(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n            ],\n        );\n\n        $manyToManyMetadata->mapManyToMany(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n            ],\n        );\n\n        self::assertEquals(['USER_ID' => 'ID'], $oneToOneMetadata->associationMappings['user']->sourceToTargetKeyColumns);\n        self::assertEquals(['USER_ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']->joinColumnFieldNames);\n        self::assertEquals(['ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']->targetToSourceKeyColumns);\n\n        self::assertEquals('USER_ID', $oneToOneMetadata->associationMappings['user']->joinColumns[0]->name);\n        self::assertEquals('ID', $oneToOneMetadata->associationMappings['user']->joinColumns[0]->referencedColumnName);\n\n        self::assertEquals('CMS_ADDRESS_CMS_USER', $manyToManyMetadata->associationMappings['user']->joinTable->name);\n\n        self::assertEquals(['CMS_ADDRESS_ID', 'CMS_USER_ID'], $manyToManyMetadata->associationMappings['user']->joinTableColumns);\n        self::assertEquals(['CMS_ADDRESS_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']->relationToSourceKeyColumns);\n        self::assertEquals(['CMS_USER_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']->relationToTargetKeyColumns);\n\n        self::assertEquals('CMS_ADDRESS_ID', $manyToManyMetadata->associationMappings['user']->joinTable->joinColumns[0]->name);\n        self::assertEquals('CMS_USER_ID', $manyToManyMetadata->associationMappings['user']->joinTable->inverseJoinColumns[0]->name);\n\n        self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']->joinTable->joinColumns[0]->referencedColumnName);\n        self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']->joinTable->inverseJoinColumns[0]->referencedColumnName);\n\n        $cm = new ClassMetadata('DoctrineGlobalArticle', $namingStrategy);\n        $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CmsUser::class]);\n        self::assertEquals('DOCTRINE_GLOBAL_ARTICLE_CMS_USER', $cm->associationMappings['author']->joinTable->name);\n    }\n\n    #[TestGroup('DDC-886')]\n    public function testSetMultipleIdentifierSetsComposite(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => 'name']);\n        $cm->mapField(['fieldName' => 'username']);\n\n        $cm->setIdentifier(['name', 'username']);\n        self::assertTrue($cm->isIdentifierComposite);\n    }\n\n    #[TestGroup('DDC-944')]\n    public function testMappingNotFound(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"No mapping found for field 'foo' on class '\" . CmsUser::class . \"'.\");\n\n        $cm->getFieldMapping('foo');\n    }\n\n    #[TestGroup('DDC-961')]\n    public function testJoinTableMappingDefaults(): void\n    {\n        $cm = new ClassMetadata('DoctrineGlobalArticle');\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CmsUser::class]);\n\n        self::assertEquals('doctrineglobalarticle_cmsuser', $cm->associationMappings['author']->joinTable->name);\n    }\n\n    #[TestGroup('DDC-117')]\n    public function testMapIdentifierAssociation(): void\n    {\n        $cm = new ClassMetadata(DDC117ArticleDetails::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapOneToOne(\n            [\n                'fieldName' => 'article',\n                'id' => true,\n                'targetEntity' => DDC117Article::class,\n                'joinColumns' => [],\n            ],\n        );\n\n        self::assertTrue($cm->containsForeignIdentifier, \"Identifier Association should set 'containsForeignIdentifier' boolean flag.\");\n        self::assertEquals(['article'], $cm->identifier);\n    }\n\n    #[TestGroup('DDC-117')]\n    public function testOrphanRemovalIdentifierAssociation(): void\n    {\n        $cm = new ClassMetadata(DDC117ArticleDetails::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('The orphan removal option is not allowed on an association that');\n\n        $cm->mapOneToOne(\n            [\n                'fieldName' => 'article',\n                'id' => true,\n                'targetEntity' => DDC117Article::class,\n                'orphanRemoval' => true,\n                'joinColumns' => [],\n            ],\n        );\n    }\n\n    #[TestGroup('DDC-117')]\n    public function testInverseIdentifierAssociation(): void\n    {\n        $cm = new ClassMetadata(DDC117ArticleDetails::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('An inverse association is not allowed to be identifier in');\n\n        $cm->mapOneToOne(\n            [\n                'fieldName' => 'article',\n                'id' => true,\n                'mappedBy' => 'details', // INVERSE!\n                'targetEntity' => DDC117Article::class,\n                'joinColumns' => [],\n            ],\n        );\n    }\n\n    #[TestGroup('DDC-117')]\n    public function testIdentifierAssociationManyToMany(): void\n    {\n        $cm = new ClassMetadata(DDC117ArticleDetails::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Many-to-many or one-to-many associations are not allowed to be identifier in');\n\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'article',\n                'id' => true,\n                'targetEntity' => DDC117Article::class,\n                'joinColumns' => [],\n            ],\n        );\n    }\n\n    #[TestGroup('DDC-996')]\n    public function testEmptyFieldNameThrowsException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"The field or association mapping misses the 'fieldName' attribute in entity '\" . CmsUser::class . \"'.\");\n\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->mapField(['fieldName' => '']);\n    }\n\n    #[TestGroup('DDC-2451')]\n    public function testSerializeEntityListeners(): void\n    {\n        $metadata = new ClassMetadata(CompanyContract::class);\n\n        $metadata->initializeReflection(new RuntimeReflectionService());\n        $metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler');\n        $metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler');\n\n        $serialize   = serialize($metadata);\n        $unserialize = unserialize($serialize);\n\n        self::assertEquals($metadata->entityListeners, $unserialize->entityListeners);\n    }\n\n    #[TestGroup('DDC-1068')]\n    public function testClassCaseSensitivity(): void\n    {\n        $user = new CmsUser();\n        $cm   = new ClassMetadata(strtoupper(CmsUser::class));\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        self::assertEquals(CmsUser::class, $cm->name);\n    }\n\n    #[TestGroup('DDC-659')]\n    public function testLifecycleCallbackNotFound(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->addLifecycleCallback('notfound', 'postLoad');\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"Entity '\" . CmsUser::class . \"' has no method 'notfound' to be registered as lifecycle callback.\");\n\n        $cm->validateLifecycleCallbacks(new RuntimeReflectionService());\n    }\n\n    #[TestGroup('ImproveErrorMessages')]\n    public function testTargetEntityNotFound(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToOne(['fieldName' => 'address', 'targetEntity' => 'UnknownClass']);\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"The target-entity Doctrine\\\\Tests\\\\Models\\\\CMS\\\\UnknownClass cannot be found in '\" . CmsUser::class . \"#address'.\");\n\n        $cm->validateAssociations();\n    }\n\n    public function testNameIsMandatoryForDiscriminatorColumnsMappingException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Discriminator column name on entity class \\'Doctrine\\Tests\\Models\\CMS\\CmsUser\\' is not defined.');\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->setDiscriminatorColumn([]);\n    }\n\n    #[TestGroup('DDC-984')]\n    #[TestGroup('DDC-559')]\n    #[TestGroup('DDC-1575')]\n    public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategy(): void\n    {\n        $namingStrategy  = new MyNamespacedNamingStrategy();\n        $addressMetadata = new ClassMetadata(CmsAddress::class, $namingStrategy);\n        $articleMetadata = new ClassMetadata(DoctrineGlobalArticle::class, $namingStrategy);\n        $routingMetadata = new ClassMetadata(RoutingLeg::class, $namingStrategy);\n\n        $addressMetadata->initializeReflection(new RuntimeReflectionService());\n        $articleMetadata->initializeReflection(new RuntimeReflectionService());\n        $routingMetadata->initializeReflection(new RuntimeReflectionService());\n\n        $addressMetadata->mapManyToMany(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n            ],\n        );\n\n        $articleMetadata->mapManyToMany(\n            [\n                'fieldName'     => 'author',\n                'targetEntity'  => CmsUser::class,\n            ],\n        );\n\n        self::assertEquals('routing_routingleg', $routingMetadata->table['name']);\n        self::assertEquals('cms_cmsaddress_cms_cmsuser', $addressMetadata->associationMappings['user']->joinTable->name);\n        self::assertEquals('doctrineglobalarticle_cms_cmsuser', $articleMetadata->associationMappings['author']->joinTable->name);\n    }\n\n    #[TestGroup('DDC-984')]\n    #[TestGroup('DDC-559')]\n    public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategyPropertyToColumnName(): void\n    {\n        $namingStrategy = new MyPrefixNamingStrategy();\n        $metadata       = new ClassMetadata(CmsAddress::class, $namingStrategy);\n\n        $metadata->initializeReflection(new RuntimeReflectionService());\n\n        $metadata->mapField(['fieldName' => 'country']);\n        $metadata->mapField(['fieldName' => 'city']);\n\n        self::assertEquals($metadata->fieldNames, [\n            'cmsaddress_country'   => 'country',\n            'cmsaddress_city'      => 'city',\n        ]);\n    }\n\n    #[TestGroup('DDC-1746')]\n    public function testInvalidCascade(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('You have specified invalid cascade options for ' . CmsUser::class . \"::\\$address: 'invalid'; available options: 'remove', 'persist', 'refresh', and 'detach'\");\n\n        $cm->mapManyToOne(['fieldName' => 'address', 'targetEntity' => 'UnknownClass', 'cascade' => ['invalid']]);\n    }\n\n    #[TestGroup('DDC-964')]\n    public function testInvalidPropertyAssociationOverrideNameException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Invalid field override named \\'invalidPropertyName\\' for class \\'Doctrine\\Tests\\Models\\DDC964\\DDC964Admin');\n        $cm = new ClassMetadata(DDC964Admin::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToOne(['fieldName' => 'address', 'targetEntity' => 'DDC964Address']);\n\n        $cm->setAssociationOverride('invalidPropertyName', []);\n    }\n\n    #[TestGroup('DDC-964')]\n    public function testInvalidPropertyAttributeOverrideNameException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Invalid field override named \\'invalidPropertyName\\' for class \\'Doctrine\\Tests\\Models\\DDC964\\DDC964Guest\\'.');\n        $cm = new ClassMetadata(DDC964Guest::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapField(['fieldName' => 'name']);\n\n        $cm->setAttributeOverride('invalidPropertyName', []);\n    }\n\n    #[TestGroup('DDC-964')]\n    public function testInvalidOverrideAttributeFieldTypeException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('The column type of attribute \\'name\\' on class \\'Doctrine\\Tests\\Models\\DDC964\\DDC964Guest\\' could not be changed.');\n        $cm = new ClassMetadata(DDC964Guest::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapField(['fieldName' => 'name', 'type' => 'string']);\n\n        $cm->setAttributeOverride('name', ['type' => 'date']);\n    }\n\n    public function testAttributeOverrideKeepsDeclaringClass(): void\n    {\n        $cm = new ClassMetadata(Directory::class);\n        $cm->mapField(['fieldName' => 'id', 'type' => 'integer', 'declared' => AbstractContentItem::class]);\n        $cm->setAttributeOverride('id', ['columnName' => 'new_id']);\n\n        $mapping = $cm->getFieldMapping('id');\n\n        self::assertSame(AbstractContentItem::class, $mapping->declared);\n    }\n\n    public function testAssociationOverrideKeepsDeclaringClass(): void\n    {\n        $cm = new ClassMetadata(Directory::class);\n        $cm->mapManyToOne(['fieldName' => 'parentDirectory', 'targetEntity' => Directory::class, 'cascade' => ['remove'], 'declared' => Directory::class]);\n        $cm->setAssociationOverride('parentDirectory', ['cascade' => ['remove']]);\n\n        $mapping = $cm->getAssociationMapping('parentDirectory');\n\n        self::assertSame(Directory::class, $mapping->declared);\n    }\n\n    public function testAssociationOverrideCanOverrideCascade(): void\n    {\n        $cm = new ClassMetadata(Directory::class);\n        $cm->mapManyToOne(['fieldName' => 'parentDirectory', 'targetEntity' => Directory::class, 'cascade' => ['remove'], 'declared' => Directory::class]);\n        $cm->setAssociationOverride('parentDirectory', ['cascade' => ['all']]);\n\n        $mapping = $cm->getAssociationMapping('parentDirectory');\n        self::assertSame(['remove', 'persist', 'refresh', 'detach'], $mapping->cascade);\n    }\n\n    #[TestGroup('DDC-1955')]\n    public function testInvalidEntityListenerClassException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Entity Listener \"\\InvalidClassName\" declared on \"Doctrine\\Tests\\Models\\CMS\\CmsUser\" not found.');\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->addEntityListener(Events::postLoad, '\\InvalidClassName', 'postLoadHandler');\n    }\n\n    #[TestGroup('DDC-1955')]\n    public function testInvalidEntityListenerMethodException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Entity Listener \"Doctrine\\Tests\\Models\\Company\\CompanyContractListener\" declared on \"Doctrine\\Tests\\Models\\CMS\\CmsUser\" has no method \"invalidMethod\".');\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $cm->addEntityListener(Events::postLoad, CompanyContractListener::class, 'invalidMethod');\n    }\n\n    public function testManyToManySelfReferencingNamingStrategyDefaults(): void\n    {\n        $cm = new ClassMetadata(CustomTypeParent::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->mapManyToMany(\n            [\n                'fieldName' => 'friendsWithMe',\n                'targetEntity' => 'CustomTypeParent',\n            ],\n        );\n\n        self::assertEquals(\n            JoinTableMapping::fromMappingArray([\n                'name' => 'customtypeparent_customtypeparent',\n                'joinColumns' => [['name' => 'customtypeparent_source', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']],\n                'inverseJoinColumns' => [['name' => 'customtypeparent_target', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']],\n            ]),\n            $cm->associationMappings['friendsWithMe']->joinTable,\n        );\n        self::assertEquals(['customtypeparent_source', 'customtypeparent_target'], $cm->associationMappings['friendsWithMe']->joinTableColumns);\n        self::assertEquals(['customtypeparent_source' => 'id'], $cm->associationMappings['friendsWithMe']->relationToSourceKeyColumns);\n        self::assertEquals(['customtypeparent_target' => 'id'], $cm->associationMappings['friendsWithMe']->relationToTargetKeyColumns);\n    }\n\n    #[TestGroup('DDC-2608')]\n    public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        $this->expectException(MappingException::class);\n        $cm->setSequenceGeneratorDefinition([]);\n    }\n\n    #[TestGroup('DDC-2662')]\n    #[TestGroup('6682')]\n    public function testQuotedSequenceName(): void\n    {\n        $cm = new ClassMetadata(CmsUser::class);\n\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->setSequenceGeneratorDefinition(['sequenceName' => '`foo`']);\n\n        self::assertSame(\n            ['sequenceName' => 'foo', 'quoted' => true, 'allocationSize' => '1', 'initialValue' => '1'],\n            $cm->sequenceGeneratorDefinition,\n        );\n    }\n\n    #[DataProvider('fullTableNameProvider')]\n    public function testGetSequenceName(\n        string $expectedSequenceName,\n        string $fullTableName,\n    ): void {\n        $cm = new ClassMetadata(self::class);\n        $cm->setIdentifier(['id']);\n        $cm->setPrimaryTable(['name' => $fullTableName]);\n\n        $platform = $this->createStub(AbstractPlatform::class);\n\n        self::assertSame(\n            $expectedSequenceName,\n            $cm->getSequenceName($platform),\n        );\n    }\n\n    /** @return iterable<string, array{string, string}> */\n    public static function fullTableNameProvider(): iterable\n    {\n        yield 'quoted table name with schema' => [\n            'custom.reserved_id_seq',\n            'custom.`reserved`',\n        ];\n\n        yield 'unquoted table name with schema' => [\n            'custom.non_reserved_id_seq',\n            'custom.non_reserved',\n        ];\n\n        yield 'quoted table name without schema' => [\n            'reserved_id_seq',\n            '`reserved`',\n        ];\n\n        yield 'unquoted table name without schema' => [\n            'non_reserved_id_seq',\n            'non_reserved',\n        ];\n    }\n\n    #[TestGroup('DDC-2700')]\n    public function testIsIdentifierMappedSuperClass(): void\n    {\n        $class = new ClassMetadata(DDC2700MappedSuperClass::class);\n\n        self::assertFalse($class->isIdentifier('foo'));\n    }\n\n    #[TestGroup('DDC-3120')]\n    public function testCanInstantiateInternalPhpClassSubclass(): void\n    {\n        $classMetadata = new ClassMetadata(MyArrayObjectEntity::class);\n\n        self::assertInstanceOf(MyArrayObjectEntity::class, $classMetadata->newInstance());\n    }\n\n    #[TestGroup('DDC-3120')]\n    public function testCanInstantiateInternalPhpClassSubclassFromUnserializedMetadata(): void\n    {\n        $classMetadata = unserialize(serialize(new ClassMetadata(MyArrayObjectEntity::class)));\n        assert($classMetadata instanceof ClassMetadata);\n\n        $classMetadata->wakeupReflection(new RuntimeReflectionService());\n\n        self::assertInstanceOf(MyArrayObjectEntity::class, $classMetadata->newInstance());\n    }\n\n    public function testWakeupReflectionWithEmbeddable(): void\n    {\n        if (! class_exists(StaticReflectionService::class)) {\n            self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version');\n        }\n\n        $classMetadata = new ClassMetadata(TestEntity1::class);\n\n        $classMetadata->mapEmbedded(\n            [\n                'fieldName'    => 'embedded',\n                'class'        => TestEntity1::class,\n                'columnPrefix' => false,\n            ],\n        );\n\n        $field = [\n            'fieldName' => 'test.embeddedProperty',\n            'type' => 'string',\n            'originalClass' => TestEntity1::class,\n            'declaredField' => 'embedded',\n            'originalField' => 'name',\n        ];\n\n        $classMetadata->mapField($field);\n        $classMetadata->wakeupReflection(new StaticReflectionService());\n\n        self::assertEquals(['embedded', 'test.embeddedProperty'], array_keys($classMetadata->getPropertyAccessors()));\n    }\n\n    public function testGetColumnNamesWithGivenFieldNames(): void\n    {\n        $metadata = new ClassMetadata(CmsUser::class);\n        $metadata->initializeReflection(new RuntimeReflectionService());\n\n        $metadata->mapField(['fieldName' => 'status', 'type' => 'string', 'columnName' => 'foo']);\n        $metadata->mapField(['fieldName' => 'username', 'type' => 'string', 'columnName' => 'bar']);\n        $metadata->mapField(['fieldName' => 'name', 'type' => 'string', 'columnName' => 'baz']);\n\n        self::assertSame(['foo', 'baz'], $metadata->getColumnNames(['status', 'name']));\n    }\n\n    #[TestGroup('DDC-6460')]\n    public function testInlineEmbeddable(): void\n    {\n        $classMetadata = new ClassMetadata(TestEntity1::class);\n\n        $classMetadata->mapEmbedded(\n            [\n                'fieldName'    => 'test',\n                'class'        => TestEntity1::class,\n                'columnPrefix' => false,\n            ],\n        );\n\n        self::assertTrue($classMetadata->hasField('test'));\n    }\n\n    #[TestGroup('DDC-3305')]\n    public function testRejectsEmbeddableWithoutValidClassName(): void\n    {\n        $metadata = new ClassMetadata(TestEntity1::class);\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('The embed mapping \\'embedded\\' misses the \\'class\\' attribute.');\n        $metadata->mapEmbedded([\n            'fieldName'    => 'embedded',\n            'class'        => '',\n            'columnPrefix' => false,\n        ]);\n    }\n\n    public function testItAddingLifecycleCallbackOnEmbeddedClassIsIllegal(): void\n    {\n        $metadata                  = new ClassMetadata(self::class);\n        $metadata->isEmbeddedClass = true;\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(<<<'EXCEPTION'\n            Context: Attempt to register lifecycle callback \"foo\" on embedded class \"Doctrine\\Tests\\ORM\\Mapping\\ClassMetadataTest\".\n            Problem: Registering lifecycle callbacks on embedded classes is not allowed.\n            EXCEPTION);\n\n        $metadata->addLifecycleCallback('foo', 'bar');\n    }\n\n    #[IgnoreDeprecations]\n    public function testGettingAnFQCNForNullIsDeprecated(): void\n    {\n        $metadata = new ClassMetadata(self::class);\n\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11294');\n\n        $metadata->fullyQualifiedClassName(null);\n    }\n\n    public function testItThrowsOnInvalidCallToGetAssociationMappedByTargetField(): void\n    {\n        $metadata = new ClassMetadata(self::class);\n        $metadata->mapOneToOne(['fieldName' => 'foo', 'targetEntity' => 'bar']);\n\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(<<<'EXCEPTION'\n            Context: Calling Doctrine\\ORM\\Mapping\\ClassMetadata::getAssociationMappedByTargetField() with \"foo\", which is the owning side of an association.\n            Problem: The owning side of an association has no \"mappedBy\" field.\n            Solution: Call Doctrine\\ORM\\Mapping\\ClassMetadata::isAssociationInverseSide() to check first.\n            EXCEPTION);\n\n        $metadata->getAssociationMappedByTargetField('foo');\n    }\n\n    public function testClassNameMappingDiscriminatorValue(): void\n    {\n        $driver     = new XmlDriver(\n            __DIR__ . '/xml',\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            true,\n        );\n        $xmlElement = $driver->getElement(CustomerType::class);\n        self::assertEquals(\n            'Doctrine\\Tests\\Models\\Customer\\InternalCustomer',\n            $xmlElement->children()->{'discriminator-map'}->{'discriminator-mapping'}[0]->attributes()['value'],\n        );\n    }\n\n    #[IgnoreDeprecations]\n    public function testDiscriminatorMapWithSameClassMultipleTimesDeprecated(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/3519');\n\n        $cm = new ClassMetadata(CMS\\CmsUser::class);\n        $cm->setDiscriminatorMap(['foo' => CMS\\CmsUser::class, 'bar' => CMS\\CmsUser::class]);\n    }\n}\n\n#[MappedSuperclass]\nclass DDC2700MappedSuperClass\n{\n    #[Column]\n    private mixed $foo;\n}\n\nclass MyNamespacedNamingStrategy extends DefaultNamingStrategy\n{\n    public function classToTableName(string $className): string\n    {\n        if (str_contains($className, '\\\\')) {\n            $className = str_replace('\\\\', '_', str_replace('Doctrine\\Tests\\Models\\\\', '', $className));\n        }\n\n        return strtolower($className);\n    }\n}\n\nclass MyPrefixNamingStrategy extends DefaultNamingStrategy\n{\n    public function propertyToColumnName(string $propertyName, string $className): string\n    {\n        return strtolower($this->classToTableName($className)) . '_' . $propertyName;\n    }\n}\n\nclass MyArrayObjectEntity extends ArrayObject\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy;\nuse Doctrine\\Tests\\Models\\NonPublicSchemaJoins\\User as NonPublicSchemaUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function sprintf;\n\n/**\n * Doctrine\\Tests\\ORM\\Mapping\\DefaultQuoteStrategyTest\n */\nclass DefaultQuoteStrategyTest extends OrmTestCase\n{\n    #[Group('DDC-3590')]\n    #[Group('DDC-1316')]\n    public function testGetJoinTableName(): void\n    {\n        $em       = $this->getTestEntityManager();\n        $metadata = $em->getClassMetadata(NonPublicSchemaUser::class);\n        $strategy = new DefaultQuoteStrategy();\n        $platform = new SQLitePlatform();\n\n        self::assertSame(\n            'readers.author_reader',\n            $strategy->getJoinTableName($metadata->associationMappings['readers'], $metadata, $platform),\n        );\n    }\n\n    #[DataProvider('fullTableNameProvider')]\n    public function testGetTableName(string $expectedFullTableName, string $tableName): void\n    {\n        $classMetadata = new ClassMetadata(self::class);\n        $classMetadata->setPrimaryTable(['name' => $tableName]);\n\n        $platform = $this->createStub(AbstractPlatform::class);\n        $platform->method('quoteSingleIdentifier')\n            ->willReturnCallback(\n                static fn (string $identifier): string => sprintf('✌️%s✌️', $identifier),\n            );\n\n        $quotedTableName = (new DefaultQuoteStrategy())->getTableName(\n            $classMetadata,\n            $platform,\n        );\n\n        self::assertSame($expectedFullTableName, $quotedTableName);\n    }\n\n    /** @return iterable<string, array{string, string}> */\n    public static function fullTableNameProvider(): iterable\n    {\n        yield 'quoted table name with schema' => [\n            '✌️custom✌️.✌️reserved✌️',\n            'custom.`reserved`',\n        ];\n\n        yield 'unquoted table name with schema' => [\n            'custom.non_reserved',\n            'custom.non_reserved',\n        ];\n\n        yield 'quoted table name without schema' => [\n            '✌️reserved✌️',\n            '`reserved`',\n        ];\n\n        yield 'unquoted table name without schema' => [\n            'non_reserved',\n            'non_reserved',\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/DiscriminatorColumnMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class DiscriminatorColumnMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new DiscriminatorColumnMapping(\n            type: 'string',\n            fieldName: 'discr',\n            name: 'discr',\n        );\n\n        $mapping->length           = 255;\n        $mapping->columnDefinition = 'VARCHAR(255)';\n        $mapping->enumType         = 'MyEnum';\n        $mapping->options          = ['foo' => 'bar'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof DiscriminatorColumnMapping);\n\n        self::assertSame(255, $resurrectedMapping->length);\n        self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition);\n        self::assertSame('MyEnum', $resurrectedMapping->enumType);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->options);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/EmbeddedClassMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\EmbeddedClassMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class EmbeddedClassMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping                = new EmbeddedClassMapping(self::class);\n        $mapping->columnPrefix  = 'these';\n        $mapping->declaredField = 'values';\n        $mapping->originalField = 'make';\n        $mapping->inherited     = self::class; // no\n        $mapping->declared      = self::class; // sense\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof EmbeddedClassMapping);\n\n        self::assertSame('these', $resurrectedMapping->columnPrefix);\n        self::assertSame('values', $resurrectedMapping->declaredField);\n        self::assertSame('make', $resurrectedMapping->originalField);\n        self::assertSame(self::class, $resurrectedMapping->inherited);\n        self::assertSame(self::class, $resurrectedMapping->declared);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/EntityListenerResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContractListener;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContractListener;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1955')]\nclass EntityListenerResolverTest extends OrmTestCase\n{\n    private DefaultEntityListenerResolver $resolver;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->resolver = new DefaultEntityListenerResolver();\n    }\n\n    public function testResolve(): void\n    {\n        $className = CompanyContractListener::class;\n        $object    = $this->resolver->resolve($className);\n\n        self::assertInstanceOf($className, $object);\n        self::assertSame($object, $this->resolver->resolve($className));\n    }\n\n    public function testRegisterAndResolve(): void\n    {\n        $className = CompanyContractListener::class;\n        $object    = new $className();\n\n        $this->resolver->register($object);\n\n        self::assertSame($object, $this->resolver->resolve($className));\n    }\n\n    public function testClearOne(): void\n    {\n        $className1 = CompanyContractListener::class;\n        $className2 = CompanyFlexUltraContractListener::class;\n\n        $obj1 = $this->resolver->resolve($className1);\n        $obj2 = $this->resolver->resolve($className2);\n\n        self::assertInstanceOf($className1, $obj1);\n        self::assertInstanceOf($className2, $obj2);\n\n        self::assertSame($obj1, $this->resolver->resolve($className1));\n        self::assertSame($obj2, $this->resolver->resolve($className2));\n\n        $this->resolver->clear($className1);\n\n        self::assertInstanceOf($className1, $this->resolver->resolve($className1));\n        self::assertInstanceOf($className2, $this->resolver->resolve($className2));\n\n        self::assertNotSame($obj1, $this->resolver->resolve($className1));\n        self::assertSame($obj2, $this->resolver->resolve($className2));\n    }\n\n    public function testClearAll(): void\n    {\n        $className1 = CompanyContractListener::class;\n        $className2 = CompanyFlexUltraContractListener::class;\n\n        $obj1 = $this->resolver->resolve($className1);\n        $obj2 = $this->resolver->resolve($className2);\n\n        self::assertInstanceOf($className1, $obj1);\n        self::assertInstanceOf($className2, $obj2);\n\n        self::assertSame($obj1, $this->resolver->resolve($className1));\n        self::assertSame($obj2, $this->resolver->resolve($className2));\n\n        $this->resolver->clear();\n\n        self::assertInstanceOf($className1, $this->resolver->resolve($className1));\n        self::assertInstanceOf($className2, $this->resolver->resolve($className2));\n\n        self::assertNotSame($obj1, $this->resolver->resolve($className1));\n        self::assertNotSame($obj2, $this->resolver->resolve($className2));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/FieldBuilderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse stdClass;\n\nclass FieldBuilderTest extends OrmTestCase\n{\n    public function testCustomIdGeneratorCanBeSet(): void\n    {\n        $cmBuilder = new ClassMetadataBuilder(new ClassMetadata(CmsUser::class));\n\n        $fieldBuilder = $cmBuilder->createField('aField', 'string');\n\n        $fieldBuilder->generatedValue('CUSTOM');\n        $fieldBuilder->setCustomIdGenerator(stdClass::class);\n\n        $fieldBuilder->build();\n\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_CUSTOM, $cmBuilder->getClassMetadata()->generatorType);\n        self::assertEquals(['class' => stdClass::class], $cmBuilder->getClassMetadata()->customGeneratorDefinition);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/FieldMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\FieldMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class FieldMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new FieldMapping(\n            type: 'string',\n            fieldName: 'id',\n            columnName: 'id',\n        );\n\n        $mapping->length           = 255;\n        $mapping->id               = true;\n        $mapping->nullable         = true;\n        $mapping->notInsertable    = true;\n        $mapping->notUpdatable     = true;\n        $mapping->columnDefinition = 'VARCHAR(255)';\n        $mapping->generated        = ClassMetadata::GENERATOR_TYPE_AUTO;\n        $mapping->enumType         = 'MyEnum';\n        $mapping->precision        = 10;\n        $mapping->scale            = 2;\n        $mapping->unique           = true;\n        $mapping->index            = true;\n        $mapping->inherited        = self::class;\n        $mapping->originalClass    = self::class;\n        $mapping->originalField    = 'id';\n        $mapping->quoted           = true;\n        $mapping->declared         = self::class;\n        $mapping->declaredField    = 'id';\n        $mapping->options          = ['foo' => 'bar'];\n        $mapping->version          = true;\n        $mapping->default          = 'foo';\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof FieldMapping);\n\n        self::assertSame(255, $resurrectedMapping->length);\n        self::assertTrue($resurrectedMapping->id);\n        self::assertTrue($resurrectedMapping->nullable);\n        self::assertTrue($resurrectedMapping->notInsertable);\n        self::assertTrue($resurrectedMapping->notUpdatable);\n        self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition);\n        self::assertSame(ClassMetadata::GENERATOR_TYPE_AUTO, $resurrectedMapping->generated);\n        self::assertSame('MyEnum', $resurrectedMapping->enumType);\n        self::assertSame(10, $resurrectedMapping->precision);\n        self::assertSame(2, $resurrectedMapping->scale);\n        self::assertTrue($resurrectedMapping->unique);\n        self::assertTrue($resurrectedMapping->index);\n        self::assertSame(self::class, $resurrectedMapping->inherited);\n        self::assertSame(self::class, $resurrectedMapping->originalClass);\n        self::assertSame('id', $resurrectedMapping->originalField);\n        self::assertTrue($resurrectedMapping->quoted);\n        self::assertSame(self::class, $resurrectedMapping->declared);\n        self::assertSame('id', $resurrectedMapping->declaredField);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->options);\n        self::assertTrue($resurrectedMapping->version);\n        self::assertSame('foo', $resurrectedMapping->default);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/Fixtures/AttributeEntityWithNestedJoinColumns.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\Fixtures;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity]\nclass AttributeEntityWithNestedJoinColumns\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue]\n    public $id;\n\n    /** @var Collection<AttributeEntityWithNestedJoinColumns> */\n    #[ORM\\ManyToMany(targetEntity: self::class)]\n    #[ORM\\JoinTable(\n        name: 'assoc_table',\n        joinColumns: new ORM\\JoinColumn(name: 'assoz_id', referencedColumnName: 'assoz_id'),\n        inverseJoinColumns: new ORM\\JoinColumn(name: 'inverse_assoz_id', referencedColumnName: 'inverse_assoz_id'),\n    )]\n    public $assoc;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/InverseSideMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\InverseSideMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class InverseSideMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new MyInverseAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->mappedBy = 'bar';\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof InverseSideMapping);\n\n        self::assertSame('bar', $resurrectedMapping->mappedBy);\n    }\n}\n\nclass MyInverseAssociationMapping extends InverseSideMapping\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/JoinColumnMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class JoinColumnMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new JoinColumnMapping('foo', 'id');\n\n        $mapping->deferrable           = true;\n        $mapping->unique               = true;\n        $mapping->quoted               = true;\n        $mapping->fieldName            = 'bar';\n        $mapping->onDelete             = 'CASCADE';\n        $mapping->columnDefinition     = 'VARCHAR(255)';\n        $mapping->nullable             = true;\n        $mapping->referencedColumnName = 'baz';\n        $mapping->options              = ['foo' => 'bar'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof JoinColumnMapping);\n\n        self::assertTrue($resurrectedMapping->deferrable);\n        self::assertSame('foo', $resurrectedMapping->name);\n        self::assertTrue($resurrectedMapping->unique);\n        self::assertTrue($resurrectedMapping->quoted);\n        self::assertSame('bar', $resurrectedMapping->fieldName);\n        self::assertSame('CASCADE', $resurrectedMapping->onDelete);\n        self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition);\n        self::assertTrue($resurrectedMapping->nullable);\n        self::assertSame('baz', $resurrectedMapping->referencedColumnName);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->options);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/JoinTableMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\JoinTableMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class JoinTableMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new JoinTableMapping('bar');\n\n        $mapping->quoted             = true;\n        $mapping->joinColumns        = [new JoinColumnMapping('foo_id', 'id')];\n        $mapping->inverseJoinColumns = [new JoinColumnMapping('bar_id', 'id')];\n        $mapping->schema             = 'foo';\n        $mapping->options            = ['foo' => 'bar'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof JoinTableMapping);\n\n        self::assertTrue($resurrectedMapping->quoted);\n        self::assertCount(1, $resurrectedMapping->joinColumns);\n        self::assertCount(1, $resurrectedMapping->inverseJoinColumns);\n        self::assertSame('foo', $resurrectedMapping->schema);\n        self::assertSame('bar', $resurrectedMapping->name);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->options);\n    }\n\n    public function testConvertingItToAMappingArrayDoesNotContainNullableInformation(): void\n    {\n        $mapping = new JoinTableMapping('bar');\n\n        $mapping->joinColumns        = [new JoinColumnMapping('foo_id', 'id')];\n        $mapping->inverseJoinColumns = [new JoinColumnMapping('bar_id', 'id')];\n\n        $mappingArray = $mapping->toArray();\n        foreach ($mappingArray['joinColumns'] as $joinColumn) {\n            self::assertArrayNotHasKey('nullable', $joinColumn);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ManyToManyOwningSideMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\JoinTableMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\WithoutErrorHandler;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class ManyToManyOwningSideMappingTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new ManyToManyOwningSideMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->joinTable                  = new JoinTableMapping('bar');\n        $mapping->joinTableColumns           = ['foo', 'bar'];\n        $mapping->relationToSourceKeyColumns = ['foo' => 'bar'];\n        $mapping->relationToTargetKeyColumns = ['bar' => 'baz'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof ManyToManyOwningSideMapping);\n\n        self::assertSame($resurrectedMapping->joinTable->name, 'bar');\n        self::assertSame(['foo', 'bar'], $resurrectedMapping->joinTableColumns);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->relationToSourceKeyColumns);\n        self::assertSame(['bar' => 'baz'], $resurrectedMapping->relationToTargetKeyColumns);\n    }\n\n    /** @param array<string,mixed> $mappingArray */\n    #[DataProvider('mappingsProvider')]\n    #[WithoutErrorHandler]\n    public function testNullableDefaults(\n        bool $expectDeprecation,\n        bool $expectedValue,\n        array $mappingArray,\n    ): void {\n        $namingStrategy = new DefaultNamingStrategy();\n        if ($expectDeprecation) {\n            $this->expectDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        } else {\n            $this->expectNoDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        }\n\n        $mapping = ManyToManyOwningSideMapping::fromMappingArrayAndNamingStrategy(\n            $mappingArray,\n            $namingStrategy,\n        );\n\n        foreach ($mapping->joinTable->joinColumns as $joinColumn) {\n            self::assertSame($expectedValue, $joinColumn->nullable);\n        }\n    }\n\n    /** @return iterable<string, array{bool, bool, array<string,mixed>}> */\n    public static function mappingsProvider(): iterable\n    {\n        yield 'defaults to false' => [\n            false,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [\n                        ['name' => 'bar_id', 'referencedColumnName' => 'id'],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                    ],\n                ],\n            ],\n        ];\n\n        yield 'explicitly marked as nullable' => [\n            true,\n            false, // user's intent is ignored at the ORM level\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [\n                        ['name' => 'bar_id', 'referencedColumnName' => 'id', 'nullable' => true],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                    ],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'explicitly marked as nullable (inverse column)' => [\n            true,\n            false, // user's intent is ignored at the ORM level\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [\n                        ['name' => 'bar_id', 'referencedColumnName' => 'id'],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => true],\n                    ],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'explicitly marked as not nullable' => [\n            true,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [\n                        ['name' => 'bar_id', 'referencedColumnName' => 'id', 'nullable' => false],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                    ],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'explicitly marked as not nullable (inverse column)' => [\n            true,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinTable' => [\n                    'name' => 'bar',\n                    'joinColumns' => [\n                        ['name' => 'bar_id', 'referencedColumnName' => 'id'],\n                    ],\n                    'inverseJoinColumns' => [\n                        ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => false],\n                    ],\n                ],\n                'id' => true,\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ManyToOneAssociationMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\WithoutErrorHandler;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class ManyToOneAssociationMappingTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new ManyToOneAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->joinColumns              = [new JoinColumnMapping('foo_id', 'id')];\n        $mapping->joinColumnFieldNames     = ['foo' => 'bar'];\n        $mapping->sourceToTargetKeyColumns = ['foo' => 'bar'];\n        $mapping->targetToSourceKeyColumns = ['bar' => 'foo'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof ManyToOneAssociationMapping);\n\n        self::assertCount(1, $resurrectedMapping->joinColumns);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->joinColumnFieldNames);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->sourceToTargetKeyColumns);\n        self::assertSame(['bar' => 'foo'], $resurrectedMapping->targetToSourceKeyColumns);\n    }\n\n    /** @param array<string, mixed> $mappingArray */\n    #[DataProvider('mappingsProvider')]\n    #[WithoutErrorHandler]\n    public function testNullableDefaults(\n        bool $expectDeprecation,\n        bool $expectedValue,\n        array $mappingArray,\n    ): void {\n        $namingStrategy = new DefaultNamingStrategy();\n        if ($expectDeprecation) {\n            $this->expectDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        } else {\n            $this->expectNoDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        }\n\n        $mapping = ManyToOneAssociationMapping::fromMappingArrayAndName(\n            $mappingArray,\n            $namingStrategy,\n            self::class,\n            null,\n            false,\n        );\n\n        foreach ($mapping->joinColumns as $joinColumn) {\n            self::assertSame($expectedValue, $joinColumn->nullable);\n        }\n    }\n\n    /** @return iterable<string, array{bool, bool, array<string, mixed>}> */\n    public static function mappingsProvider(): iterable\n    {\n        yield 'not part of the identifier' => [\n            false,\n            true,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                ],\n                'id' => false,\n            ],\n        ];\n\n        yield 'part of the identifier' => [\n            false,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'part of the identifier, but explicitly marked as nullable' => [\n            true,\n            false, // user's intent is ignored at the ORM level\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => true],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'part of the identifier, but explicitly marked as not nullable' => [\n            true,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => true],\n                ],\n                'id' => true,\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/MappingDriverTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumn;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Index;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\NamingStrategy;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\TypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\UniqueConstraint;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObject;\nuse Doctrine\\Tests\\DbalTypes\\CustomIdObjectType;\nuse Doctrine\\Tests\\DbalTypes\\CustomIntType;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddressListener;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContractListener;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFixContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContractListener;\nuse Doctrine\\Tests\\Models\\DDC1476\\DDC1476EntityWithDefaultFieldType;\nuse Doctrine\\Tests\\Models\\DDC2825\\ExplicitSchemaAndTable;\nuse Doctrine\\Tests\\Models\\DDC2825\\SchemaAndTableInTableName;\nuse Doctrine\\Tests\\Models\\DDC3579\\DDC3579Admin;\nuse Doctrine\\Tests\\Models\\DDC5934\\DDC5934Contract;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869ChequePayment;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869CreditCardPayment;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869PaymentRepository;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889Class;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889Entity;\nuse Doctrine\\Tests\\Models\\DDC964\\DDC964Admin;\nuse Doctrine\\Tests\\Models\\DDC964\\DDC964Guest;\nuse Doctrine\\Tests\\Models\\Enums\\Card;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\nuse Doctrine\\Tests\\Models\\GH10288\\GH10288People;\nuse Doctrine\\Tests\\Models\\TypedProperties\\Contact;\nuse Doctrine\\Tests\\Models\\TypedProperties\\UserTyped;\nuse Doctrine\\Tests\\Models\\TypedProperties\\UserTypedWithCustomTypedField;\nuse Doctrine\\Tests\\Models\\Upsertable\\Insertable;\nuse Doctrine\\Tests\\Models\\Upsertable\\Updatable;\nuse Doctrine\\Tests\\ORM\\Mapping\\NamingStrategy\\CustomPascalNamingStrategy;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Depends;\nuse stdClass;\n\nuse function assert;\nuse function count;\nuse function str_contains;\nuse function strtolower;\n\nuse const CASE_UPPER;\n\nabstract class MappingDriverTestCase extends OrmTestCase\n{\n    abstract protected function loadDriver(): MappingDriver;\n\n    /** @param class-string<object> $entityClassName */\n    public function createClassMetadata(\n        string $entityClassName,\n        NamingStrategy|null $namingStrategy = null,\n        TypedFieldMapper|null $typedFieldMapper = null,\n    ): ClassMetadata {\n        $mappingDriver = $this->loadDriver();\n\n        $class = new ClassMetadata($entityClassName, $namingStrategy, $typedFieldMapper);\n        $class->initializeReflection(new RuntimeReflectionService());\n        $mappingDriver->loadMetadataForClass($entityClassName, $class);\n\n        return $class;\n    }\n\n    protected function createClassMetadataFactory(EntityManagerInterface|null $em = null): ClassMetadataFactory\n    {\n        $driver  = $this->loadDriver();\n        $em    ??= $this->getTestEntityManager();\n        $factory = new ClassMetadataFactory();\n        $em->getConfiguration()->setMetadataDriverImpl($driver);\n        $factory->setEntityManager($em);\n\n        return $factory;\n    }\n\n    public function testEntityTableNameAndInheritance(): ClassMetadata\n    {\n        $class = $this->createClassMetadata(User::class);\n\n        self::assertEquals('cms_users', $class->getTableName());\n        self::assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $class->inheritanceType);\n\n        return $class;\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testEntityIndexes(ClassMetadata $class): ClassMetadata\n    {\n        self::assertArrayHasKey('indexes', $class->table, 'ClassMetadata should have indexes key in table property.');\n        self::assertEquals(\n            [\n                'name_idx' => ['columns' => ['name']],\n                0 => ['columns' => ['user_email']],\n                'fields' => ['fields' => ['name', 'email']],\n            ],\n            $class->table['indexes'],\n        );\n\n        return $class;\n    }\n\n    public function testEntityIncorrectIndexes(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->createClassMetadata(UserIncorrectIndex::class);\n    }\n\n    public function testEntityIndexFlagsAndPartialIndexes(): void\n    {\n        $class = $this->createClassMetadata(Comment::class);\n\n        self::assertEquals(\n            [\n                0 => [\n                    'columns' => ['content'],\n                    'flags' => ['fulltext'],\n                    'options' => ['where' => 'content IS NOT NULL'],\n                ],\n            ],\n            $class->table['indexes'],\n        );\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testEntityUniqueConstraints(ClassMetadata $class): ClassMetadata\n    {\n        self::assertArrayHasKey(\n            'uniqueConstraints',\n            $class->table,\n            'ClassMetadata should have uniqueConstraints key in table property when Unique Constraints are set.',\n        );\n\n        self::assertEquals(\n            [\n                'search_idx' => ['columns' => ['name', 'user_email'], 'options' => ['where' => 'name IS NOT NULL']],\n                'phone_idx' => ['fields' => ['name', 'phone']],\n            ],\n            $class->table['uniqueConstraints'],\n        );\n\n        return $class;\n    }\n\n    public function testEntityIncorrectUniqueContraint(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->createClassMetadata(UserIncorrectUniqueConstraint::class);\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testEntityOptions(ClassMetadata $class): ClassMetadata\n    {\n        self::assertArrayHasKey('options', $class->table, 'ClassMetadata should have options key in table property.');\n\n        self::assertEquals(\n            [\n                'foo' => 'bar',\n                'baz' => ['key' => 'val'],\n            ],\n            $class->table['options'],\n        );\n\n        return $class;\n    }\n\n    #[Depends('testEntityOptions')]\n    public function testEntitySequence(ClassMetadata $class): void\n    {\n        self::assertIsArray($class->sequenceGeneratorDefinition, 'No Sequence Definition set on this driver.');\n        self::assertEquals(\n            [\n                'sequenceName' => 'tablename_seq',\n                'allocationSize' => 100,\n                'initialValue' => 1,\n            ],\n            $class->sequenceGeneratorDefinition,\n        );\n    }\n\n    public function testEntityCustomGenerator(): void\n    {\n        $class = $this->createClassMetadata(Animal::class);\n\n        self::assertEquals(\n            ClassMetadata::GENERATOR_TYPE_CUSTOM,\n            $class->generatorType,\n            'Generator Type',\n        );\n        self::assertEquals(\n            ['class' => stdClass::class],\n            $class->customGeneratorDefinition,\n            'Custom Generator Definition',\n        );\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testFieldMappings(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals(5, count($class->fieldMappings));\n        self::assertTrue(isset($class->fieldMappings['id']));\n        self::assertTrue(isset($class->fieldMappings['name']));\n        self::assertTrue(isset($class->fieldMappings['email']));\n        self::assertTrue(isset($class->fieldMappings['version']));\n        self::assertTrue(isset($class->fieldMappings['indexed']));\n\n        return $class;\n    }\n\n    #[Depends('testFieldMappings')]\n    public function testVersionedField(ClassMetadata $class): void\n    {\n        self::assertTrue($class->isVersioned);\n        self::assertEquals('version', $class->versionField);\n\n        self::assertFalse(isset($class->fieldMappings['version']->version));\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testFieldMappingsColumnNames(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals('id', $class->fieldMappings['id']->columnName);\n        self::assertEquals('name', $class->fieldMappings['name']->columnName);\n        self::assertEquals('user_email', $class->fieldMappings['email']->columnName);\n\n        return $class;\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testStringFieldMappings(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals('string', $class->fieldMappings['name']->type);\n        self::assertEquals(50, $class->fieldMappings['name']->length);\n        self::assertTrue($class->fieldMappings['name']->nullable);\n        self::assertTrue($class->fieldMappings['name']->unique);\n        self::assertTrue($class->fieldMappings['indexed']->index);\n\n        return $class;\n    }\n\n    public function testFieldTypeFromReflection(): void\n    {\n        $class = $this->createClassMetadata(UserTyped::class);\n\n        self::assertEquals('integer', $class->getTypeOfField('id'));\n        self::assertEquals('string', $class->getTypeOfField('username'));\n        self::assertEquals('dateinterval', $class->getTypeOfField('dateInterval'));\n        self::assertEquals('datetime', $class->getTypeOfField('dateTime'));\n        self::assertEquals('datetime_immutable', $class->getTypeOfField('dateTimeImmutable'));\n        self::assertEquals('json', $class->getTypeOfField('array'));\n        self::assertEquals('boolean', $class->getTypeOfField('boolean'));\n        self::assertEquals('float', $class->getTypeOfField('float'));\n\n        self::assertEquals(CmsEmail::class, $class->getAssociationMapping('email')->targetEntity);\n        self::assertEquals(CmsEmail::class, $class->getAssociationMapping('mainEmail')->targetEntity);\n        self::assertEquals(Contact::class, $class->embeddedClasses['contact']->class);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('GH10313')]\n    public function testCustomFieldTypeFromReflection(): void\n    {\n        $class = $this->createClassMetadata(\n            UserTypedWithCustomTypedField::class,\n            null,\n            new DefaultTypedFieldMapper(\n                [\n                    CustomIdObject::class => CustomIdObjectType::class,\n                    'int' => CustomIntType::class,\n                ],\n            ),\n        );\n\n        self::assertEquals(CustomIdObjectType::class, $class->getTypeOfField('customId'));\n        self::assertEquals(CustomIntType::class, $class->getTypeOfField('customIntTypedField'));\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testFieldOptions(ClassMetadata $class): ClassMetadata\n    {\n        $expected = ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false];\n        self::assertEquals($expected, $class->fieldMappings['name']->options);\n\n        return $class;\n    }\n\n    #[Depends('testEntityTableNameAndInheritance')]\n    public function testIdFieldOptions(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals(['foo' => 'bar', 'unsigned' => false], $class->fieldMappings['id']->options);\n\n        return $class;\n    }\n\n    #[Depends('testFieldMappings')]\n    public function testIdentifier(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals(['id'], $class->identifier);\n        self::assertEquals('integer', $class->fieldMappings['id']->type);\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $class->generatorType, 'ID-Generator is not ClassMetadata::GENERATOR_TYPE_AUTO');\n\n        return $class;\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('#6129')]\n    public function testBooleanValuesForOptionIsSetCorrectly(): ClassMetadata\n    {\n        $class = $this->createClassMetadata(User::class);\n\n        self::assertIsBool($class->fieldMappings['id']->options['unsigned']);\n        self::assertFalse($class->fieldMappings['id']->options['unsigned']);\n\n        self::assertIsBool($class->fieldMappings['name']->options['fixed']);\n        self::assertFalse($class->fieldMappings['name']->options['fixed']);\n\n        return $class;\n    }\n\n    #[Depends('testIdentifier')]\n    public function testAssociations(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals(3, count($class->associationMappings));\n\n        return $class;\n    }\n\n    #[Depends('testAssociations')]\n    public function testOwningOneToOneAssociation(ClassMetadata $class): ClassMetadata\n    {\n        self::assertTrue(isset($class->associationMappings['address']));\n        self::assertTrue($class->associationMappings['address']->isOwningSide());\n        self::assertEquals('user', $class->associationMappings['address']->inversedBy);\n        // Check cascading\n        self::assertTrue($class->associationMappings['address']->isCascadeRemove());\n        self::assertFalse($class->associationMappings['address']->isCascadePersist());\n        self::assertFalse($class->associationMappings['address']->isCascadeRefresh());\n        self::assertFalse($class->associationMappings['address']->isCascadeDetach());\n\n        return $class;\n    }\n\n    #[Depends('testOwningOneToOneAssociation')]\n    public function testInverseOneToManyAssociation(ClassMetadata $class): ClassMetadata\n    {\n        self::assertTrue(isset($class->associationMappings['phonenumbers']));\n        self::assertFalse($class->associationMappings['phonenumbers']->isOwningSide());\n        self::assertTrue($class->associationMappings['phonenumbers']->isCascadePersist());\n        self::assertTrue($class->associationMappings['phonenumbers']->isCascadeRemove());\n        self::assertFalse($class->associationMappings['phonenumbers']->isCascadeRefresh());\n        self::assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach());\n        self::assertTrue($class->associationMappings['phonenumbers']->orphanRemoval);\n\n        // Test Order By\n        self::assertEquals(['number' => 'ASC'], $class->associationMappings['phonenumbers']->orderBy);\n\n        return $class;\n    }\n\n    #[Depends('testInverseOneToManyAssociation')]\n    public function testManyToManyAssociationWithCascadeAll(ClassMetadata $class): ClassMetadata\n    {\n        self::assertTrue(isset($class->associationMappings['groups']));\n        self::assertTrue($class->associationMappings['groups']->isOwningSide());\n        // Make sure that cascade-all works as expected\n        self::assertTrue($class->associationMappings['groups']->isCascadeRemove());\n        self::assertTrue($class->associationMappings['groups']->isCascadePersist());\n        self::assertTrue($class->associationMappings['groups']->isCascadeRefresh());\n        self::assertTrue($class->associationMappings['groups']->isCascadeDetach());\n\n        self::assertFalse($class->associationMappings['groups']->isOrdered());\n\n        return $class;\n    }\n\n    #[Depends('testManyToManyAssociationWithCascadeAll')]\n    public function testLifecycleCallbacks(ClassMetadata $class): ClassMetadata\n    {\n        self::assertCount(2, $class->lifecycleCallbacks);\n        self::assertEquals($class->lifecycleCallbacks['prePersist'][0], 'doStuffOnPrePersist');\n        self::assertEquals($class->lifecycleCallbacks['postPersist'][0], 'doStuffOnPostPersist');\n\n        return $class;\n    }\n\n    #[Depends('testManyToManyAssociationWithCascadeAll')]\n    public function testLifecycleCallbacksSupportMultipleMethodNames(ClassMetadata $class): ClassMetadata\n    {\n        self::assertCount(2, $class->lifecycleCallbacks['prePersist']);\n        self::assertEquals($class->lifecycleCallbacks['prePersist'][1], 'doOtherStuffOnPrePersistToo');\n\n        return $class;\n    }\n\n    #[Depends('testLifecycleCallbacksSupportMultipleMethodNames')]\n    public function testJoinColumnUniqueAndNullable(ClassMetadata $class): ClassMetadata\n    {\n        // Non-Nullability of Join Column\n        self::assertFalse($class->associationMappings['groups']->joinTable->joinColumns[0]->nullable);\n        self::assertFalse($class->associationMappings['groups']->joinTable->joinColumns[0]->unique);\n\n        return $class;\n    }\n\n    #[Depends('testJoinColumnUniqueAndNullable')]\n    public function testColumnDefinition(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals('CHAR(32) NOT NULL', $class->fieldMappings['email']->columnDefinition);\n        self::assertEquals('INT NULL', $class->associationMappings['groups']->joinTable->inverseJoinColumns[0]->columnDefinition);\n\n        return $class;\n    }\n\n    #[Depends('testColumnDefinition')]\n    public function testJoinColumnOnDelete(ClassMetadata $class): ClassMetadata\n    {\n        self::assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]->onDelete);\n\n        return $class;\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-514')]\n    public function testDiscriminatorColumnDefaults(): void\n    {\n        if (str_contains(static::class, 'PHPMappingDriver')) {\n            self::markTestSkipped('PHP Mapping Drivers have no defaults.');\n        }\n\n        $class = $this->createClassMetadata(Animal::class);\n\n        self::assertEquals(\n            DiscriminatorColumnMapping::fromMappingArray([\n                'name' => 'discr',\n                'type' => 'string',\n                'length' => 32,\n                'fieldName' => 'discr',\n                'columnDefinition' => null,\n                'enumType' => null,\n            ]),\n            $class->discriminatorColumn,\n        );\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-869')]\n    public function testMappedSuperclassWithRepository(): void\n    {\n        $em      = $this->getTestEntityManager();\n        $factory = $this->createClassMetadataFactory($em);\n\n        $class = $factory->getMetadataFor(DDC869CreditCardPayment::class);\n\n        self::assertTrue(isset($class->fieldMappings['id']));\n        self::assertTrue(isset($class->fieldMappings['value']));\n        self::assertTrue(isset($class->fieldMappings['creditCardNumber']));\n        self::assertEquals($class->customRepositoryClassName, DDC869PaymentRepository::class);\n        self::assertInstanceOf(DDC869PaymentRepository::class, $em->getRepository(DDC869CreditCardPayment::class));\n        self::assertTrue($em->getRepository(DDC869ChequePayment::class)->isTrue());\n\n        $class = $factory->getMetadataFor(DDC869ChequePayment::class);\n\n        self::assertTrue(isset($class->fieldMappings['id']));\n        self::assertTrue(isset($class->fieldMappings['value']));\n        self::assertTrue(isset($class->fieldMappings['serialNumber']));\n        self::assertEquals($class->customRepositoryClassName, DDC869PaymentRepository::class);\n        self::assertInstanceOf(DDC869PaymentRepository::class, $em->getRepository(DDC869ChequePayment::class));\n        self::assertTrue($em->getRepository(DDC869ChequePayment::class)->isTrue());\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1476')]\n    public function testDefaultFieldType(): void\n    {\n        $factory = $this->createClassMetadataFactory();\n        $class   = $factory->getMetadataFor(DDC1476EntityWithDefaultFieldType::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('name', $class->fieldMappings);\n\n        self::assertEquals('string', $class->fieldMappings['id']->type);\n        self::assertEquals('string', $class->fieldMappings['name']->type);\n\n        self::assertEquals('id', $class->fieldMappings['id']->fieldName);\n        self::assertEquals('name', $class->fieldMappings['name']->fieldName);\n\n        self::assertEquals('id', $class->fieldMappings['id']->columnName);\n        self::assertEquals('name', $class->fieldMappings['name']->columnName);\n\n        self::assertEquals(ClassMetadata::GENERATOR_TYPE_NONE, $class->generatorType);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1170')]\n    public function testIdentifierColumnDefinition(): void\n    {\n        $class = $this->createClassMetadata(DDC1170Entity::class);\n\n        self::assertArrayHasKey('id', $class->fieldMappings);\n        self::assertArrayHasKey('value', $class->fieldMappings);\n\n        self::assertEquals('int unsigned not null', strtolower($class->fieldMappings['id']->columnDefinition));\n        self::assertEquals('varchar(255) not null', strtolower($class->fieldMappings['value']->columnDefinition));\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-559')]\n    public function testNamingStrategy(): void\n    {\n        $em      = $this->getTestEntityManager();\n        $factory = $this->createClassMetadataFactory($em);\n\n        self::assertInstanceOf(DefaultNamingStrategy::class, $em->getConfiguration()->getNamingStrategy());\n        $em->getConfiguration()->setNamingStrategy(new UnderscoreNamingStrategy(CASE_UPPER));\n        self::assertInstanceOf(UnderscoreNamingStrategy::class, $em->getConfiguration()->getNamingStrategy());\n\n        $class = $factory->getMetadataFor(DDC1476EntityWithDefaultFieldType::class);\n\n        self::assertEquals('ID', $class->getColumnName('id'));\n        self::assertEquals('NAME', $class->getColumnName('name'));\n        self::assertEquals('DDC1476_ENTITY_WITH_DEFAULT_FIELD_TYPE', $class->table['name']);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-807')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-553')]\n    public function testDiscriminatorColumnDefinition(): void\n    {\n        $class = $this->createClassMetadata(DDC807Entity::class);\n\n        self::assertEquals(\"ENUM('ONE','TWO')\", $class->discriminatorColumn->columnDefinition);\n        self::assertEquals('dtype', $class->discriminatorColumn->name);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('GH10288')]\n    public function testDiscriminatorColumnEnumTypeDefinition(): void\n    {\n        $class = $this->createClassMetadata(GH10288EnumTypePerson::class);\n\n        self::assertEquals(GH10288People::class, $class->discriminatorColumn->enumType);\n        self::assertEquals('discr', $class->discriminatorColumn->name);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-889')]\n    public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Class \"Doctrine\\Tests\\Models\\DDC889\\DDC889Class\" sub class of \"Doctrine\\Tests\\Models\\DDC889\\DDC889SuperClass\" is not a valid entity or mapped super class.');\n\n        $this->createClassMetadata(DDC889Class::class);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-889')]\n    public function testIdentifierRequiredShouldMentionParentClasses(): void\n    {\n        $factory = $this->createClassMetadataFactory();\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('No identifier/primary key specified for Entity \"Doctrine\\Tests\\Models\\DDC889\\DDC889Entity\" sub class of \"Doctrine\\Tests\\Models\\DDC889\\DDC889SuperClass\". Every Entity must have an identifier/primary key.');\n\n        $factory->getMetadataFor(DDC889Entity::class);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-3579')]\n    public function testInversedByOverrideMapping(): void\n    {\n        $factory       = $this->createClassMetadataFactory();\n        $adminMetadata = $factory->getMetadataFor(DDC3579Admin::class);\n\n        // assert groups association mappings\n        self::assertArrayHasKey('groups', $adminMetadata->associationMappings);\n        $adminGroups = $adminMetadata->associationMappings['groups'];\n\n        // assert override\n        self::assertEquals('admins', $adminGroups->inversedBy);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-5934')]\n    public function testFetchOverrideMapping(): void\n    {\n        // check override metadata\n        $contractMetadata = $this->createClassMetadataFactory()->getMetadataFor(DDC5934Contract::class);\n\n        self::assertArrayHasKey('members', $contractMetadata->associationMappings);\n        self::assertSame(ClassMetadata::FETCH_EXTRA_LAZY, $contractMetadata->associationMappings['members']->fetch);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-964')]\n    public function testAssociationOverridesMapping(): void\n    {\n        $factory       = $this->createClassMetadataFactory();\n        $adminMetadata = $factory->getMetadataFor(DDC964Admin::class);\n        $guestMetadata = $factory->getMetadataFor(DDC964Guest::class);\n\n        // assert groups association mappings\n        self::assertArrayHasKey('groups', $guestMetadata->associationMappings);\n        self::assertArrayHasKey('groups', $adminMetadata->associationMappings);\n\n        $guestGroups = $guestMetadata->associationMappings['groups'];\n        $adminGroups = $adminMetadata->associationMappings['groups'];\n\n        // assert not override attributes\n        self::assertEquals($guestGroups->fieldName, $adminGroups->fieldName);\n        self::assertEquals($guestGroups->type(), $adminGroups->type());\n        self::assertEquals($guestGroups->inversedBy, $adminGroups->inversedBy);\n        self::assertEquals($guestGroups->isOwningSide(), $adminGroups->isOwningSide());\n        self::assertEquals($guestGroups->fetch, $adminGroups->fetch);\n        self::assertEquals($guestGroups->isCascadeRemove(), $adminGroups->isCascadeRemove());\n        self::assertEquals($guestGroups->isCascadePersist(), $adminGroups->isCascadePersist());\n        self::assertEquals($guestGroups->isCascadeRefresh(), $adminGroups->isCascadeRefresh());\n        self::assertEquals($guestGroups->isCascadeDetach(), $adminGroups->isCascadeDetach());\n\n        // assert not override attributes\n        self::assertEquals('ddc964_users_groups', $guestGroups->joinTable->name);\n        self::assertEquals('user_id', $guestGroups->joinTable->joinColumns[0]->name);\n        self::assertEquals('group_id', $guestGroups->joinTable->inverseJoinColumns[0]->name);\n\n        self::assertEquals(['user_id' => 'id'], $guestGroups->relationToSourceKeyColumns);\n        self::assertEquals(['group_id' => 'id'], $guestGroups->relationToTargetKeyColumns);\n        self::assertEquals(['user_id', 'group_id'], $guestGroups->joinTableColumns);\n\n        self::assertEquals('ddc964_users_admingroups', $adminGroups->joinTable->name);\n        self::assertEquals('adminuser_id', $adminGroups->joinTable->joinColumns[0]->name);\n        self::assertEquals('admingroup_id', $adminGroups->joinTable->inverseJoinColumns[0]->name);\n\n        self::assertEquals(['adminuser_id' => 'id'], $adminGroups->relationToSourceKeyColumns);\n        self::assertEquals(['admingroup_id' => 'id'], $adminGroups->relationToTargetKeyColumns);\n        self::assertEquals(['adminuser_id', 'admingroup_id'], $adminGroups->joinTableColumns);\n\n        // assert address association mappings\n        self::assertArrayHasKey('address', $guestMetadata->associationMappings);\n        self::assertArrayHasKey('address', $adminMetadata->associationMappings);\n\n        $guestAddress = $guestMetadata->associationMappings['address'];\n        $adminAddress = $adminMetadata->associationMappings['address'];\n\n        // assert not override attributes\n        self::assertEquals($guestAddress->fieldName, $adminAddress->fieldName);\n        self::assertEquals($guestAddress->type(), $adminAddress->type());\n        self::assertEquals($guestAddress->inversedBy, $adminAddress->inversedBy);\n        self::assertEquals($guestAddress->isOwningSide(), $adminAddress->isOwningSide());\n        self::assertEquals($guestAddress->fetch, $adminAddress->fetch);\n        self::assertEquals($guestAddress->isCascadeRemove(), $adminAddress->isCascadeRemove());\n        self::assertEquals($guestAddress->isCascadePersist(), $adminAddress->isCascadePersist());\n        self::assertEquals($guestAddress->isCascadeRefresh(), $adminAddress->isCascadeRefresh());\n        self::assertEquals($guestAddress->isCascadeDetach(), $adminAddress->isCascadeDetach());\n\n        // assert override\n        self::assertEquals('address_id', $guestAddress->joinColumns[0]->name);\n        self::assertEquals(['address_id' => 'id'], $guestAddress->sourceToTargetKeyColumns);\n        self::assertEquals(['address_id' => 'address_id'], $guestAddress->joinColumnFieldNames);\n        self::assertEquals(['id' => 'address_id'], $guestAddress->targetToSourceKeyColumns);\n\n        self::assertEquals('adminaddress_id', $adminAddress->joinColumns[0]->name);\n        self::assertEquals(['adminaddress_id' => 'id'], $adminAddress->sourceToTargetKeyColumns);\n        self::assertEquals(['adminaddress_id' => 'adminaddress_id'], $adminAddress->joinColumnFieldNames);\n        self::assertEquals(['id' => 'adminaddress_id'], $adminAddress->targetToSourceKeyColumns);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-964')]\n    public function testAttributeOverridesMapping(): void\n    {\n        $factory       = $this->createClassMetadataFactory();\n        $guestMetadata = $factory->getMetadataFor(DDC964Guest::class);\n        $adminMetadata = $factory->getMetadataFor(DDC964Admin::class);\n\n        self::assertTrue($adminMetadata->fieldMappings['id']->id);\n        self::assertEquals('id', $adminMetadata->fieldMappings['id']->fieldName);\n        self::assertEquals('user_id', $adminMetadata->fieldMappings['id']->columnName);\n        self::assertEquals(['user_id' => 'id', 'user_name' => 'name'], $adminMetadata->fieldNames);\n        self::assertEquals(['id' => 'user_id', 'name' => 'user_name'], $adminMetadata->columnNames);\n        self::assertEquals(150, $adminMetadata->fieldMappings['id']->length);\n\n        self::assertEquals('name', $adminMetadata->fieldMappings['name']->fieldName);\n        self::assertEquals('user_name', $adminMetadata->fieldMappings['name']->columnName);\n        self::assertEquals(250, $adminMetadata->fieldMappings['name']->length);\n        self::assertTrue($adminMetadata->fieldMappings['name']->nullable);\n        self::assertFalse($adminMetadata->fieldMappings['name']->unique);\n\n        self::assertTrue($guestMetadata->fieldMappings['id']->id);\n        self::assertEquals('guest_id', $guestMetadata->fieldMappings['id']->columnName);\n        self::assertEquals('id', $guestMetadata->fieldMappings['id']->fieldName);\n        self::assertEquals(['guest_id' => 'id', 'guest_name' => 'name'], $guestMetadata->fieldNames);\n        self::assertEquals(['id' => 'guest_id', 'name' => 'guest_name'], $guestMetadata->columnNames);\n        self::assertEquals(140, $guestMetadata->fieldMappings['id']->length);\n\n        self::assertEquals('name', $guestMetadata->fieldMappings['name']->fieldName);\n        self::assertEquals('guest_name', $guestMetadata->fieldMappings['name']->columnName);\n        self::assertEquals(240, $guestMetadata->fieldMappings['name']->length);\n        self::assertFalse($guestMetadata->fieldMappings['name']->nullable);\n        self::assertTrue($guestMetadata->fieldMappings['name']->unique);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1955')]\n    public function testEntityListeners(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $factory    = $this->createClassMetadataFactory($em);\n        $superClass = $factory->getMetadataFor(CompanyContract::class);\n        $flexClass  = $factory->getMetadataFor(CompanyFixContract::class);\n        $fixClass   = $factory->getMetadataFor(CompanyFlexContract::class);\n        $ultraClass = $factory->getMetadataFor(CompanyFlexUltraContract::class);\n\n        self::assertArrayHasKey(Events::prePersist, $superClass->entityListeners);\n        self::assertArrayHasKey(Events::postPersist, $superClass->entityListeners);\n\n        self::assertCount(1, $superClass->entityListeners[Events::prePersist]);\n        self::assertCount(1, $superClass->entityListeners[Events::postPersist]);\n\n        $postPersist = $superClass->entityListeners[Events::postPersist][0];\n        $prePersist  = $superClass->entityListeners[Events::prePersist][0];\n\n        self::assertEquals(CompanyContractListener::class, $postPersist['class']);\n        self::assertEquals(CompanyContractListener::class, $prePersist['class']);\n        self::assertEquals('postPersistHandler', $postPersist['method']);\n        self::assertEquals('prePersistHandler', $prePersist['method']);\n\n        //Inherited listeners\n        self::assertEquals($fixClass->entityListeners, $superClass->entityListeners);\n        self::assertEquals($flexClass->entityListeners, $superClass->entityListeners);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1955')]\n    public function testEntityListenersOverride(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $factory    = $this->createClassMetadataFactory($em);\n        $ultraClass = $factory->getMetadataFor(CompanyFlexUltraContract::class);\n\n        //overridden listeners\n        self::assertArrayHasKey(Events::postPersist, $ultraClass->entityListeners);\n        self::assertArrayHasKey(Events::prePersist, $ultraClass->entityListeners);\n\n        self::assertCount(1, $ultraClass->entityListeners[Events::postPersist]);\n        self::assertCount(3, $ultraClass->entityListeners[Events::prePersist]);\n\n        $postPersist = $ultraClass->entityListeners[Events::postPersist][0];\n        $prePersist  = $ultraClass->entityListeners[Events::prePersist][0];\n\n        self::assertEquals(CompanyContractListener::class, $postPersist['class']);\n        self::assertEquals(CompanyContractListener::class, $prePersist['class']);\n        self::assertEquals('postPersistHandler', $postPersist['method']);\n        self::assertEquals('prePersistHandler', $prePersist['method']);\n\n        $prePersist = $ultraClass->entityListeners[Events::prePersist][1];\n        self::assertEquals(CompanyFlexUltraContractListener::class, $prePersist['class']);\n        self::assertEquals('prePersistHandler1', $prePersist['method']);\n\n        $prePersist = $ultraClass->entityListeners[Events::prePersist][2];\n        self::assertEquals(CompanyFlexUltraContractListener::class, $prePersist['class']);\n        self::assertEquals('prePersistHandler2', $prePersist['method']);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1955')]\n    public function testEntityListenersNamingConvention(): void\n    {\n        $em       = $this->getTestEntityManager();\n        $factory  = $this->createClassMetadataFactory($em);\n        $metadata = $factory->getMetadataFor(CmsAddress::class);\n\n        self::assertArrayHasKey(Events::postPersist, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::prePersist, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::postUpdate, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::preUpdate, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::postRemove, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::preRemove, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::postLoad, $metadata->entityListeners);\n        self::assertArrayHasKey(Events::preFlush, $metadata->entityListeners);\n\n        self::assertCount(1, $metadata->entityListeners[Events::postPersist]);\n        self::assertCount(1, $metadata->entityListeners[Events::prePersist]);\n        self::assertCount(1, $metadata->entityListeners[Events::postUpdate]);\n        self::assertCount(1, $metadata->entityListeners[Events::preUpdate]);\n        self::assertCount(1, $metadata->entityListeners[Events::postRemove]);\n        self::assertCount(1, $metadata->entityListeners[Events::preRemove]);\n        self::assertCount(1, $metadata->entityListeners[Events::postLoad]);\n        self::assertCount(1, $metadata->entityListeners[Events::preFlush]);\n\n        $postPersist = $metadata->entityListeners[Events::postPersist][0];\n        $prePersist  = $metadata->entityListeners[Events::prePersist][0];\n        $postUpdate  = $metadata->entityListeners[Events::postUpdate][0];\n        $preUpdate   = $metadata->entityListeners[Events::preUpdate][0];\n        $postRemove  = $metadata->entityListeners[Events::postRemove][0];\n        $preRemove   = $metadata->entityListeners[Events::preRemove][0];\n        $postLoad    = $metadata->entityListeners[Events::postLoad][0];\n        $preFlush    = $metadata->entityListeners[Events::preFlush][0];\n\n        self::assertEquals(CmsAddressListener::class, $postPersist['class']);\n        self::assertEquals(CmsAddressListener::class, $prePersist['class']);\n        self::assertEquals(CmsAddressListener::class, $postUpdate['class']);\n        self::assertEquals(CmsAddressListener::class, $preUpdate['class']);\n        self::assertEquals(CmsAddressListener::class, $postRemove['class']);\n        self::assertEquals(CmsAddressListener::class, $preRemove['class']);\n        self::assertEquals(CmsAddressListener::class, $postLoad['class']);\n        self::assertEquals(CmsAddressListener::class, $preFlush['class']);\n\n        self::assertEquals(Events::postPersist, $postPersist['method']);\n        self::assertEquals(Events::prePersist, $prePersist['method']);\n        self::assertEquals(Events::postUpdate, $postUpdate['method']);\n        self::assertEquals(Events::preUpdate, $preUpdate['method']);\n        self::assertEquals(Events::postRemove, $postRemove['method']);\n        self::assertEquals(Events::preRemove, $preRemove['method']);\n        self::assertEquals(Events::postLoad, $postLoad['method']);\n        self::assertEquals(Events::preFlush, $preFlush['method']);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-2183')]\n    public function testSecondLevelCacheMapping(): void\n    {\n        $em      = $this->getTestEntityManager();\n        $factory = $this->createClassMetadataFactory($em);\n        $class   = $factory->getMetadataFor(City::class);\n        self::assertArrayHasKey('usage', $class->cache);\n        self::assertArrayHasKey('region', $class->cache);\n        self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->cache['usage']);\n        self::assertEquals('doctrine_tests_models_cache_city', $class->cache['region']);\n\n        self::assertArrayHasKey('state', $class->associationMappings);\n        self::assertNotNull($class->associationMappings['state']->cache);\n        self::assertArrayHasKey('usage', $class->associationMappings['state']->cache);\n        self::assertArrayHasKey('region', $class->associationMappings['state']->cache);\n        self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['state']->cache['usage']);\n        self::assertEquals('doctrine_tests_models_cache_city__state', $class->associationMappings['state']->cache['region']);\n\n        self::assertArrayHasKey('attractions', $class->associationMappings);\n        self::assertNotNull($class->associationMappings['attractions']->cache);\n        self::assertArrayHasKey('usage', $class->associationMappings['attractions']->cache);\n        self::assertArrayHasKey('region', $class->associationMappings['attractions']->cache);\n        self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['attractions']->cache['usage']);\n        self::assertEquals('doctrine_tests_models_cache_city__attractions', $class->associationMappings['attractions']->cache['region']);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-2825')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('881')]\n    public function testSchemaDefinitionViaExplicitTableSchemaAttributeProperty(): void\n    {\n        $metadata = $this->createClassMetadataFactory()->getMetadataFor(ExplicitSchemaAndTable::class);\n        assert($metadata instanceof ClassMetadata);\n\n        self::assertSame('explicit_schema', $metadata->getSchemaName());\n        self::assertSame('explicit_table', $metadata->getTableName());\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-2825')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('881')]\n    public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAttributeProperty(): void\n    {\n        $metadata = $this->createClassMetadataFactory()->getMetadataFor(SchemaAndTableInTableName::class);\n        assert($metadata instanceof ClassMetadata);\n\n        self::assertSame('implicit_schema', $metadata->getSchemaName());\n        self::assertSame('implicit_table', $metadata->getTableName());\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-514')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1015')]\n    public function testDiscriminatorColumnDefaultLength(): void\n    {\n        if (str_contains(static::class, 'PHPMappingDriver')) {\n            self::markTestSkipped('PHP Mapping Drivers have no defaults.');\n        }\n\n        $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class);\n        self::assertEquals(255, $class->discriminatorColumn->length);\n        $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class);\n        self::assertEquals(255, $class->discriminatorColumn->length);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-514')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1015')]\n    public function testDiscriminatorColumnDefaultType(): void\n    {\n        if (str_contains(static::class, 'PHPMappingDriver')) {\n            self::markTestSkipped('PHP Mapping Drivers have no defaults.');\n        }\n\n        $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class);\n        self::assertEquals('string', $class->discriminatorColumn->type);\n        $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class);\n        self::assertEquals('string', $class->discriminatorColumn->type);\n    }\n\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-514')]\n    #[\\PHPUnit\\Framework\\Attributes\\Group('DDC-1015')]\n    public function testDiscriminatorColumnDefaultName(): void\n    {\n        if (str_contains(static::class, 'PHPMappingDriver')) {\n            self::markTestSkipped('PHP Mapping Drivers have no defaults.');\n        }\n\n        $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class);\n        self::assertEquals('dtype', $class->discriminatorColumn->name);\n        $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class);\n        self::assertEquals('dtype', $class->discriminatorColumn->name);\n    }\n\n    public function testReservedWordInTableColumn(): void\n    {\n        $metadata = $this->createClassMetadata(ReservedWordInTableColumn::class);\n\n        self::assertSame('count', $metadata->getFieldMapping('count')->columnName);\n    }\n\n    public function testInsertableColumn(): void\n    {\n        $metadata = $this->createClassMetadata(Insertable::class);\n\n        $mapping = $metadata->getFieldMapping('nonInsertableContent');\n\n        self::assertSame(ClassMetadata::GENERATED_INSERT, $mapping->generated);\n        self::assertNull($metadata->getFieldMapping('insertableContent')->notInsertable);\n    }\n\n    public function testUpdatableColumn(): void\n    {\n        $metadata = $this->createClassMetadata(Updatable::class);\n\n        $mapping = $metadata->getFieldMapping('nonUpdatableContent');\n\n        self::assertSame(ClassMetadata::GENERATED_ALWAYS, $mapping->generated);\n        self::assertNull($metadata->getFieldMapping('updatableContent')->notUpdatable);\n    }\n\n    public function testEnumType(): void\n    {\n        $metadata = $this->createClassMetadata(Card::class);\n\n        self::assertEquals(Suit::class, $metadata->fieldMappings['suit']->enumType);\n    }\n\n    public function testCustomNamingStrategyIsRespected(): void\n    {\n        $ns       = new CustomPascalNamingStrategy();\n        $metadata = $this->createClassMetadata(BlogPostComment::class, $ns);\n\n        self::assertEquals('id', $metadata->fieldNames['Id']);\n        self::assertEquals('Id', $metadata->associationMappings['blogPost']->joinColumns[0]->referencedColumnName);\n        self::assertFalse($metadata->associationMappings['blogPost']->joinColumns[0]->nullable);\n    }\n}\n\n#[ORM\\Entity()]\n#[ORM\\HasLifecycleCallbacks()]\n#[ORM\\Table(name: 'cms_users', options: ['foo' => 'bar', 'baz' => ['key' => 'val']])]\n#[ORM\\Index(name: 'name_idx', columns: ['name'])]\n#[ORM\\Index(name: '0', columns: ['user_email'])]\n#[ORM\\Index(name: 'fields', fields: ['name', 'email'])]\n#[ORM\\UniqueConstraint(name: 'search_idx', columns: ['name', 'user_email'], options: ['where' => 'name IS NOT NULL'])]\n#[ORM\\UniqueConstraint(name: 'phone_idx', fields: ['name', 'phone'])]\nclass User\n{\n    /** @var int **/\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer', options: ['foo' => 'bar', 'unsigned' => false])]\n    #[ORM\\GeneratedValue(strategy: 'AUTO')]\n    #[ORM\\SequenceGenerator(sequenceName: 'tablename_seq', initialValue: 1, allocationSize: 100)]\n    public $id;\n\n    /** @var string */\n    #[ORM\\Column(length: 50, nullable: true, unique: true, options: ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false])]\n    public $name;\n\n    /** @var string */\n    #[ORM\\Column(name: 'user_email', columnDefinition: 'CHAR(32) NOT NULL')]\n    public $email;\n\n    /** @var Address */\n    #[ORM\\OneToOne(targetEntity: 'Address', cascade: ['remove'], inversedBy: 'user')]\n    #[ORM\\JoinColumn(onDelete: 'CASCADE')]\n    public $address;\n\n    /** @var Collection<int, Phonenumber> */\n    #[ORM\\OneToMany(targetEntity: 'Phonenumber', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)]\n    #[ORM\\OrderBy(['number' => 'ASC'])]\n    public $phonenumbers;\n\n    /** @var Collection<int, Group> */\n    #[ORM\\ManyToMany(targetEntity: 'Group', cascade: ['all'])]\n    #[ORM\\JoinTable(name: 'cms_user_groups')]\n    #[ORM\\JoinColumn(name: 'user_id', referencedColumnName: 'id', unique: false)]\n    #[ORM\\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', columnDefinition: 'INT NULL')]\n    public $groups;\n\n    /** @var int */\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\Version]\n    public $version;\n\n    /** @var string */\n    #[ORM\\Column(index: true)]\n    public $indexed;\n\n    #[ORM\\PrePersist]\n    public function doStuffOnPrePersist(): void\n    {\n    }\n\n    #[ORM\\PrePersist]\n    public function doOtherStuffOnPrePersistToo(): void\n    {\n    }\n\n    #[ORM\\PostPersist]\n    public function doStuffOnPostPersist(): void\n    {\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable(\n            [\n                'name' => 'cms_users',\n                'options' => ['foo' => 'bar', 'baz' => ['key' => 'val']],\n            ],\n        );\n        $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT);\n        $metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist');\n        $metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist');\n        $metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist');\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n                'columnName' => 'id',\n                'options' => ['foo' => 'bar', 'unsigned' => false],\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type' => 'string',\n                'length' => 50,\n                'unique' => true,\n                'nullable' => true,\n                'columnName' => 'name',\n                'options' => ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false],\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'email',\n                'type' => 'string',\n                'columnName' => 'user_email',\n                'columnDefinition' => 'CHAR(32) NOT NULL',\n            ],\n        );\n        $mapping = ['fieldName' => 'version', 'type' => 'integer'];\n        $metadata->setVersionMapping($mapping);\n        $metadata->mapField($mapping);\n        $metadata->mapField([\n            'fieldName' => 'indexed',\n            'type' => 'string',\n            'columnName' => 'indexed',\n            'index' => true,\n        ]);\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n        $metadata->mapOneToOne(\n            [\n                'fieldName' => 'address',\n                'targetEntity' => Address::class,\n                'cascade' =>\n                [0 => 'remove'],\n                'mappedBy' => null,\n                'inversedBy' => 'user',\n                'joinColumns' =>\n                [\n                    0 =>\n                    [\n                        'name' => 'address_id',\n                        'referencedColumnName' => 'id',\n                        'onDelete' => 'CASCADE',\n                    ],\n                ],\n                'orphanRemoval' => false,\n            ],\n        );\n        $metadata->mapOneToMany(\n            [\n                'fieldName' => 'phonenumbers',\n                'targetEntity' => Phonenumber::class,\n                'cascade' =>\n                [1 => 'persist'],\n                'mappedBy' => 'user',\n                'orphanRemoval' => true,\n                'orderBy' =>\n                ['number' => 'ASC'],\n            ],\n        );\n        $metadata->mapManyToMany(\n            [\n                'fieldName' => 'groups',\n                'targetEntity' => Group::class,\n                'cascade' =>\n                [\n                    0 => 'remove',\n                    1 => 'persist',\n                    2 => 'refresh',\n                    3 => 'detach',\n                ],\n                'mappedBy' => null,\n                'joinTable' =>\n                [\n                    'name' => 'cms_users_groups',\n                    'joinColumns' =>\n                    [\n                        0 =>\n                        [\n                            'name' => 'user_id',\n                            'referencedColumnName' => 'id',\n                            'unique' => false,\n                            'nullable' => false,\n                        ],\n                    ],\n                    'inverseJoinColumns' =>\n                    [\n                        0 =>\n                        [\n                            'name' => 'group_id',\n                            'referencedColumnName' => 'id',\n                            'columnDefinition' => 'INT NULL',\n                        ],\n                    ],\n                ],\n                'orderBy' => [],\n            ],\n        );\n        $metadata->table['uniqueConstraints'] = [\n            'search_idx' => ['columns' => ['name', 'user_email'], 'options' => ['where' => 'name IS NOT NULL']],\n            'phone_idx' => ['fields' => ['name', 'phone']],\n        ];\n        $metadata->table['indexes']           = [\n            'name_idx' => ['columns' => ['name']],\n            0 => ['columns' => ['user_email']],\n            'fields' => ['fields' => ['name', 'email']],\n        ];\n        $metadata->setSequenceGeneratorDefinition(\n            [\n                'sequenceName' => 'tablename_seq',\n                'allocationSize' => 100,\n                'initialValue' => 1,\n            ],\n        );\n    }\n}\n\n#[Table]\n#[Index(name: 'name_idx', columns: ['name'], fields: ['email'])]\n#[Entity]\nclass UserIncorrectIndex\n{\n    /** @var int **/\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $name;\n\n    /** @var string */\n    #[Column(name: 'user_email')]\n    public $email;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable([]);\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n                'columnName' => 'id',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type' => 'string',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'email',\n                'type' => 'string',\n                'columnName' => 'user_email',\n            ],\n        );\n        $metadata->table['indexes'] = [\n            'name_idx' => ['columns' => ['name'], 'fields' => ['email']],\n        ];\n    }\n}\n\n#[Table]\n#[UniqueConstraint(name: 'name_idx', columns: ['name'], fields: ['email'])]\n#[Entity]\nclass UserIncorrectUniqueConstraint\n{\n    /** @var int **/\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $name;\n\n    /** @var string */\n    #[Column(name: 'user_email')]\n    public $email;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable([]);\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n                'columnName' => 'id',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'name',\n                'type' => 'string',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'email',\n                'type' => 'string',\n                'columnName' => 'user_email',\n            ],\n        );\n        $metadata->table['uniqueConstraints'] = [\n            'name_idx' => ['columns' => ['name'], 'fields' => ['email']],\n        ];\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'discr', length: 32, type: 'string')]\n#[ORM\\DiscriminatorMap(['cat' => 'Cat', 'dog' => 'Dog'])]\nabstract class Animal\n{\n    /** @var string */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'string')]\n    #[ORM\\GeneratedValue(strategy: 'CUSTOM')]\n    #[ORM\\CustomIdGenerator(class: stdClass::class)]\n    public $id;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);\n        $metadata->setCustomGeneratorDefinition(['class' => stdClass::class]);\n    }\n}\n\n#[ORM\\Entity]\nclass Cat extends Animal\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n    }\n}\n\n#[ORM\\Entity]\nclass Dog extends Animal\n{\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n    }\n}\n\n#[ORM\\Entity]\nclass DDC1170Entity\n{\n    public function __construct(\n        #[ORM\\Column(columnDefinition: 'VARCHAR(255) NOT NULL')]\n        private string|null $value = null,\n    ) {\n    }\n\n    #[ORM\\Id]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    #[ORM\\Column(type: 'integer', columnDefinition: 'INT UNSIGNED NOT NULL')]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getValue(): string|null\n    {\n        return $this->value;\n    }\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'                 => true,\n                'fieldName'          => 'id',\n                'columnDefinition'   => 'INT unsigned NOT NULL',\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName'         => 'value',\n                'columnDefinition'  => 'VARCHAR(255) NOT NULL',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorColumn(name: 'dtype', columnDefinition: \"ENUM('ONE','TWO')\")]\n#[ORM\\DiscriminatorMap(['ONE' => 'DDC807SubClasse1', 'TWO' => 'DDC807SubClasse2'])]\nclass DDC807Entity\n{\n    /** @var int **/\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n         $metadata->mapField(\n             [\n                 'id'                 => true,\n                 'fieldName'          => 'id',\n             ],\n         );\n\n        $metadata->setDiscriminatorColumn(\n            [\n                'name'              => 'dtype',\n                'type'              => 'string',\n                'columnDefinition'  => \"ENUM('ONE','TWO')\",\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n\nclass DDC807SubClasse1\n{\n}\nclass DDC807SubClasse2\n{\n}\n\nclass Address\n{\n}\nclass Phonenumber\n{\n}\nclass Group\n{\n}\n\n#[ORM\\Entity]\n#[ORM\\Table(name: 'Comment')]\n#[ORM\\Index(columns: ['content'], flags: ['fulltext'], options: ['where' => 'content IS NOT NULL'])]\nclass Comment\n{\n    #[ORM\\Column(type: 'text')]\n    private string $content;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n        $metadata->setPrimaryTable(\n            [\n                'indexes' => [\n                    ['columns' => ['content'], 'flags' => ['fulltext'], 'options' => ['where' => 'content IS NOT NULL']],\n                ],\n            ],\n        );\n\n        $metadata->mapField(\n            [\n                'fieldName' => 'content',\n                'type' => 'text',\n                'scale' => 0,\n                'length' => null,\n                'unique' => false,\n                'nullable' => false,\n                'precision' => 0,\n                'columnName' => 'content',\n            ],\n        );\n    }\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap(['ONE' => 'SingleTableEntityNoDiscriminatorColumnMappingSub1', 'TWO' => 'SingleTableEntityNoDiscriminatorColumnMappingSub2'])]\nclass SingleTableEntityNoDiscriminatorColumnMapping\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n\nclass SingleTableEntityNoDiscriminatorColumnMappingSub1 extends SingleTableEntityNoDiscriminatorColumnMapping\n{\n}\nclass SingleTableEntityNoDiscriminatorColumnMappingSub2 extends SingleTableEntityNoDiscriminatorColumnMapping\n{\n}\n\n#[ORM\\Entity]\n#[ORM\\InheritanceType('SINGLE_TABLE')]\n#[ORM\\DiscriminatorMap(['ONE' => 'SingleTableEntityNoDiscriminatorColumnMappingSub1', 'TWO' => 'SingleTableEntityNoDiscriminatorColumnMappingSub2'])]\n#[ORM\\DiscriminatorColumn(name: 'dtype')]\nclass SingleTableEntityIncompleteDiscriminatorColumnMapping\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n\nclass SingleTableEntityIncompleteDiscriminatorColumnMappingSub1 extends SingleTableEntityIncompleteDiscriminatorColumnMapping\n{\n}\nclass SingleTableEntityIncompleteDiscriminatorColumnMappingSub2 extends SingleTableEntityIncompleteDiscriminatorColumnMapping\n{\n}\n\n#[ORM\\Entity]\nclass ReservedWordInTableColumn\n{\n    /** @var int */\n    #[ORM\\Id]\n    #[ORM\\Column(type: 'integer')]\n    #[ORM\\GeneratedValue(strategy: 'NONE')]\n    public $id;\n\n    /** @var string|null */\n    #[ORM\\Column(name: '`count`', type: 'integer')]\n    public $count;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id' => true,\n                'fieldName' => 'id',\n                'type' => 'integer',\n            ],\n        );\n        $metadata->mapField(\n            [\n                'fieldName' => 'count',\n                'type' => 'integer',\n                'columnName' => '`count`',\n            ],\n        );\n    }\n}\n\nclass UserIncorrectAttributes extends User\n{\n}\n\nclass UserMissingAttributes extends User\n{\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorColumn(name: 'discr', enumType: GH10288People::class)]\n#[DiscriminatorMap(['boss' => GH10288EnumTypeBoss::class])]\nabstract class GH10288EnumTypePerson\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int|null $id = null;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'                 => true,\n                'fieldName'          => 'id',\n            ],\n        );\n\n        $metadata->setDiscriminatorColumn(\n            [\n                'name'     => 'discr',\n                'enumType' =>  GH10288People::class,\n            ],\n        );\n\n        $metadata->setIdGeneratorType(ORM\\ClassMetadata::GENERATOR_TYPE_NONE);\n    }\n}\n\n#[Entity]\nclass GH10288EnumTypeBoss extends GH10288EnumTypePerson\n{\n}\n\n/**\n * Two small related entities to test default namings with barebone attributes\n */\n#[Entity]\nclass BlogPost\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue(strategy: 'NONE')]\n    public int $id;\n}\n\n#[Entity]\nclass BlogPostComment\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public int $id;\n\n    #[ORM\\ManyToOne]\n    #[ORM\\JoinColumn(nullable: false)]\n    public BlogPost $blogPost;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'                => true,\n                'fieldName'         => 'id',\n                'type'              => 'integer',\n            ],\n        );\n        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n        $metadata->mapManyToOne(\n            [\n                'fieldName' => 'blogPost',\n                'targetEntity' => BlogPost::class,\n                'joinColumns' =>\n                    [\n                        0 => ['nullable' => false],\n                    ],\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/NamingStrategy/CustomPascalNamingStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\NamingStrategy;\n\nuse Doctrine\\ORM\\Mapping\\NamingStrategy;\nuse LogicException;\n\nuse function sprintf;\nuse function str_contains;\nuse function strrpos;\nuse function strtolower;\nuse function substr;\nuse function ucfirst;\n\n/**\n * Fully customized naming strategy changing all namings to a PascalCase model. Included to test some behaviours\n * regarding fully custom naming strategies.\n */\nclass CustomPascalNamingStrategy implements NamingStrategy\n{\n    /**\n     * Returns a table name for an entity class.\n     *\n     * @param string $className The fully-qualified class name\n     *\n     * @return string A table name\n     */\n    public function classToTableName(string $className): string\n    {\n        if (str_contains($className, '\\\\')) {\n            return substr($className, strrpos($className, '\\\\') + 1);\n        }\n\n        return $className;\n    }\n\n    /**\n     * Returns a column name for a property.\n     *\n     * @param string      $propertyName A property name\n     * @param string|null $className    The fully-qualified class name\n     *\n     * @return string A column name\n     */\n    public function propertyToColumnName(string $propertyName, string|null $className = null): string\n    {\n        if ($className !== null && strtolower($propertyName) === strtolower($this->classToTableName($className)) . 'id') {\n            return 'Id';\n        }\n\n        return ucfirst($propertyName);\n    }\n\n    /**\n     * Returns a column name for an embedded property.\n     */\n    public function embeddedFieldToColumnName(string $propertyName, string $embeddedColumnName, string|null $className = null, $embeddedClassName = null): string\n    {\n        throw new LogicException(sprintf('Method %s is not implemented', __METHOD__));\n    }\n\n    /**\n     * Returns the default reference column name.\n     *\n     * @return string A column name\n     */\n    public function referenceColumnName(): string\n    {\n        return 'Id';\n    }\n\n    /**\n     * Returns a join column name for a property.\n     *\n     * @return string A join column name\n     */\n    public function joinColumnName(string $propertyName, string $className): string\n    {\n        return ucfirst($propertyName) . $this->referenceColumnName();\n    }\n\n    /**\n     * Returns a join table name.\n     *\n     * @param string      $sourceEntity The source entity\n     * @param string      $targetEntity The target entity\n     * @param string|null $propertyName A property name\n     *\n     * @return string A join table name\n     */\n    public function joinTableName(string $sourceEntity, string $targetEntity, string|null $propertyName = null): string\n    {\n        return $this->classToTableName($sourceEntity) . $this->classToTableName($targetEntity);\n    }\n\n    /**\n     * Returns the foreign key column name for the given parameters.\n     *\n     * @param string      $entityName           An entity\n     * @param string|null $referencedColumnName A property\n     *\n     * @return string A join column name\n     */\n    public function joinKeyColumnName(string $entityName, string|null $referencedColumnName = null): string\n    {\n        return $this->classToTableName($entityName) . ($referencedColumnName ?: $this->referenceColumnName());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/NamingStrategy/JoinColumnClassNamingStrategy.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\NamingStrategy;\n\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\n\nuse function strtolower;\n\n/**\n * Stub naming strategy to verify `joinColumnName` proper behavior\n */\nclass JoinColumnClassNamingStrategy extends DefaultNamingStrategy\n{\n    public function joinColumnName(string $propertyName, string|null $className = null): string\n    {\n        return strtolower($this->classToTableName($className))\n            . '_' . $propertyName\n            . '_' . $this->referenceColumnName();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/NamingStrategyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\NamingStrategy;\nuse Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy;\nuse Doctrine\\Tests\\ORM\\Mapping\\NamingStrategy\\CustomPascalNamingStrategy;\nuse Doctrine\\Tests\\ORM\\Mapping\\NamingStrategy\\JoinColumnClassNamingStrategy;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse const CASE_LOWER;\nuse const CASE_UPPER;\n\n#[Group('DDC-559')]\nclass NamingStrategyTest extends OrmTestCase\n{\n    private static function defaultNaming(): DefaultNamingStrategy\n    {\n        return new DefaultNamingStrategy();\n    }\n\n    private static function underscoreNamingLower(): UnderscoreNamingStrategy\n    {\n        return new UnderscoreNamingStrategy(CASE_LOWER);\n    }\n\n    private static function underscoreNamingUpper(): UnderscoreNamingStrategy\n    {\n        return new UnderscoreNamingStrategy(CASE_UPPER);\n    }\n\n    private static function customNaming(): CustomPascalNamingStrategy\n    {\n        return new CustomPascalNamingStrategy();\n    }\n\n    /**\n     * Data Provider for NamingStrategy#classToTableName\n     *\n     * @return array<array{NamingStrategy, string, string}>\n     */\n    public static function dataClassToTableName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'SomeClassName', 'SomeClassName'],\n            [self::defaultNaming(), 'SomeClassName', '\\SomeClassName'],\n            [self::defaultNaming(), 'Name', '\\Some\\Class\\Name'],\n            [self::defaultNaming(), 'Name2Test', '\\Some\\Class\\Name2Test'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'some_class_name', '\\Name\\Space\\SomeClassName'],\n            [self::underscoreNamingLower(), 'name', '\\Some\\Class\\Name'],\n            [self::underscoreNamingLower(), 'name2_test', '\\Some\\Class\\Name2Test'],\n            [self::underscoreNamingLower(), 'name2test', '\\Some\\Class\\Name2test'],\n            [self::underscoreNamingUpper(), 'SOME_CLASS_NAME', '\\Name\\Space\\SomeClassName'],\n            [self::underscoreNamingUpper(), 'NAME', '\\Some\\Class\\Name'],\n            [self::underscoreNamingUpper(), 'NAME2_TEST', '\\Some\\Class\\Name2Test'],\n            [self::underscoreNamingUpper(), 'NAME2TEST', '\\Some\\Class\\Name2test'],\n\n            // CustomPascalNamingStrategy\n            [self::customNaming(), 'SomeClassName', 'SomeClassName'],\n            [self::customNaming(), 'Name2Test', '\\Some\\Class\\Name2Test'],\n        ];\n    }\n\n    #[DataProvider('dataClassToTableName')]\n    public function testClassToTableName(NamingStrategy $strategy, string $expected, string $className): void\n    {\n        self::assertSame($expected, $strategy->classToTableName($className));\n    }\n\n    /**\n     * Data Provider for NamingStrategy#propertyToColumnName\n     *\n     * @return array<array{NamingStrategy, string, string, string}>\n     */\n    public static function dataPropertyToColumnName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'someProperty', 'someProperty', 'Some\\Class'],\n            [self::defaultNaming(), 'SOME_PROPERTY', 'SOME_PROPERTY', 'Some\\Class'],\n            [self::defaultNaming(), 'some_property', 'some_property', 'Some\\Class'],\n            [self::defaultNaming(), 'base64Encoded', 'base64Encoded', 'Some\\Class'],\n            [self::defaultNaming(), 'base64_encoded', 'base64_encoded', 'Some\\Class'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'some_property', 'someProperty', 'Some\\Class'],\n            [self::underscoreNamingLower(), 'base64_encoded', 'base64Encoded', 'Some\\Class'],\n            [self::underscoreNamingLower(), 'base64encoded', 'base64encoded', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'someProperty', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'some_property', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'SOME_PROPERTY', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'BASE64_ENCODED', 'base64Encoded', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'BASE64ENCODED', 'base64encoded', 'Some\\Class'],\n\n            // CustomPascalNamingStrategy\n            [self::customNaming(), 'SomeProperty', 'someProperty', 'Some\\Class'],\n            [self::customNaming(), 'Base64Encoded', 'base64Encoded', 'Some\\Class'],\n        ];\n    }\n\n    #[DataProvider('dataPropertyToColumnName')]\n    public function testPropertyToColumnName(\n        NamingStrategy $strategy,\n        string $expected,\n        string $propertyName,\n        string $className,\n    ): void {\n        self::assertSame($expected, $strategy->propertyToColumnName($propertyName, $className));\n    }\n\n    /**\n     * Data Provider for NamingStrategy#referenceColumnName\n     *\n     * @return array<array{NamingStrategy, string}>\n     */\n    public static function dataReferenceColumnName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'id'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'id'],\n            [self::underscoreNamingUpper(), 'ID'],\n\n            // CustomPascalNamingStrategy\n            [self::customNaming(), 'Id'],\n        ];\n    }\n\n    #[DataProvider('dataReferenceColumnName')]\n    public function testReferenceColumnName(NamingStrategy $strategy, string $expected): void\n    {\n        self::assertSame($expected, $strategy->referenceColumnName());\n    }\n\n    /**\n     * Data Provider for NamingStrategy#joinColumnName\n     *\n     * @return array<array{NamingStrategy, string, string}>\n     */\n    public static function dataJoinColumnName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'someColumn_id', 'someColumn', 'Some\\Class'],\n            [self::defaultNaming(), 'some_column_id', 'some_column', 'Some\\Class'],\n            [self::defaultNaming(), 'base64Encoded_id', 'base64Encoded', 'Some\\Class'],\n            [self::defaultNaming(), 'base64_encoded_id', 'base64_encoded', 'Some\\Class'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'some_column_id', 'someColumn', 'Some\\Class'],\n            [self::underscoreNamingLower(), 'base64_encoded_id', 'base64Encoded', 'Some\\Class'],\n            [self::underscoreNamingLower(), 'base64encoded_id', 'base64encoded', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'SOME_COLUMN_ID', 'someColumn', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'BASE64_ENCODED_ID', 'base64Encoded', 'Some\\Class'],\n            [self::underscoreNamingUpper(), 'BASE64ENCODED_ID', 'base64encoded', 'Some\\Class'],\n\n            // JoinColumnClassNamingStrategy\n            [new JoinColumnClassNamingStrategy(), 'classname_someColumn_id', 'someColumn', 'Some\\ClassName'],\n            [new JoinColumnClassNamingStrategy(), 'classname_some_column_id', 'some_column', 'ClassName'],\n        ];\n    }\n\n    #[DataProvider('dataJoinColumnName')]\n    public function testJoinColumnName(\n        UnderscoreNamingStrategy|DefaultNamingStrategy $strategy,\n        string $expected,\n        string $propertyName,\n        string|null $className = null,\n    ): void {\n        self::assertSame($expected, $strategy->joinColumnName($propertyName, $className));\n    }\n\n    /**\n     * Data Provider for NamingStrategy#joinTableName\n     *\n     * @return array<array{NamingStrategy, string, string, string}>\n     */\n    public static function dataJoinTableName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'someclassname_classname', 'SomeClassName', 'Some\\ClassName', 'some_property'],\n            [self::defaultNaming(), 'someclassname_classname', '\\SomeClassName', 'ClassName', 'some_property'],\n            [self::defaultNaming(), 'name_classname', '\\Some\\Class\\Name', 'ClassName', 'some_property'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'some_class_name_class_name', 'SomeClassName', 'Some\\ClassName', 'some_property'],\n            [self::underscoreNamingLower(), 'class1_test_class2_test', 'Class1Test', 'Some\\Class2Test', 'some_property'],\n            [self::underscoreNamingLower(), 'some_class_name_class_name', '\\SomeClassName', 'ClassName', 'some_property'],\n            [self::underscoreNamingLower(), 'name_class_name', '\\Some\\Class\\Name', 'ClassName', 'some_property'],\n            [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', 'SomeClassName', 'Some\\ClassName', 'some_property'],\n            [self::underscoreNamingUpper(), 'CLASS1_TEST_CLASS2_TEST', 'Class1Test', 'Some\\Class2Test', 'some_property'],\n            [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', '\\SomeClassName', 'ClassName', 'some_property'],\n            [self::underscoreNamingUpper(), 'NAME_CLASS_NAME', '\\Some\\Class\\Name', 'ClassName', 'some_property'],\n        ];\n    }\n\n    #[DataProvider('dataJoinTableName')]\n    public function testJoinTableName(\n        NamingStrategy $strategy,\n        string $expected,\n        string $ownerEntity,\n        string $associatedEntity,\n        string $propertyName,\n    ): void {\n        self::assertSame($expected, $strategy->joinTableName($ownerEntity, $associatedEntity, $propertyName));\n    }\n\n    /**\n     * Data Provider for NamingStrategy#joinKeyColumnName\n     *\n     * @return array<array{NamingStrategy, string, string, string|null}>\n     */\n    public static function dataJoinKeyColumnName(): array\n    {\n        return [\n            // DefaultNamingStrategy\n            [self::defaultNaming(), 'someclassname_id', 'SomeClassName', null],\n            [self::defaultNaming(), 'name_identifier', '\\Some\\Class\\Name', 'identifier'],\n\n            // UnderscoreNamingStrategy\n            [self::underscoreNamingLower(), 'some_class_name2_test_id', 'SomeClassName2Test', null],\n            [self::underscoreNamingLower(), 'some_class_name_id', 'SomeClassName', null, null],\n            [self::underscoreNamingLower(), 'class_name_identifier', '\\Some\\Class\\ClassName', 'identifier'],\n            [self::underscoreNamingLower(), 'name2_test_identifier', '\\Some\\Class\\Name2Test', 'identifier'],\n            [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_ID', 'SomeClassName', null],\n            [self::underscoreNamingUpper(), 'SOME_CLASS_NAME2_TEST_ID', 'SomeClassName2Test', null],\n            [self::underscoreNamingUpper(), 'CLASS_NAME_IDENTIFIER', '\\Some\\Class\\ClassName', 'IDENTIFIER'],\n            [self::underscoreNamingUpper(), 'NAME2_TEST_IDENTIFIER', '\\Some\\Class\\Name2Test', 'IDENTIFIER'],\n        ];\n    }\n\n    #[DataProvider('dataJoinKeyColumnName')]\n    public function testJoinKeyColumnName(\n        NamingStrategy $strategy,\n        string $expected,\n        string $propertyEntityName,\n        string|null $referencedColumnName = null,\n    ): void {\n        self::assertSame($expected, $strategy->joinKeyColumnName($propertyEntityName, $referencedColumnName));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/OneToOneOwningSideMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping\\DefaultNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\JoinColumnMapping;\nuse Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\WithoutErrorHandler;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class OneToOneOwningSideMappingTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new OneToOneOwningSideMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->joinColumns              = [new JoinColumnMapping('foo_id', 'id')];\n        $mapping->joinColumnFieldNames     = ['foo' => 'bar'];\n        $mapping->sourceToTargetKeyColumns = ['foo' => 'bar'];\n        $mapping->targetToSourceKeyColumns = ['bar' => 'foo'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof OneToOneOwningSideMapping);\n\n        self::assertCount(1, $resurrectedMapping->joinColumns);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->joinColumnFieldNames);\n        self::assertSame(['foo' => 'bar'], $resurrectedMapping->sourceToTargetKeyColumns);\n        self::assertSame(['bar' => 'foo'], $resurrectedMapping->targetToSourceKeyColumns);\n    }\n\n    /** @param array<string, mixed> $mappingArray */\n    #[DataProvider('mappingsProvider')]\n    #[WithoutErrorHandler]\n    public function testNullableDefaults(\n        bool $expectDeprecation,\n        bool $expectedValue,\n        array $mappingArray,\n    ): void {\n        $namingStrategy = new DefaultNamingStrategy();\n        if ($expectDeprecation) {\n            $this->expectDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        } else {\n            $this->expectNoDeprecationWithIdentifier(\n                'https://github.com/doctrine/orm/pull/12126',\n            );\n        }\n\n        $mapping = OneToOneOwningSideMapping::fromMappingArrayAndName(\n            $mappingArray,\n            $namingStrategy,\n            self::class,\n            null,\n            false,\n        );\n\n        foreach ($mapping->joinColumns as $joinColumn) {\n            self::assertSame($expectedValue, $joinColumn->nullable);\n        }\n    }\n\n    /** @return iterable<string, array{bool, OneToOneOwningSideMapping}> */\n    public static function mappingsProvider(): iterable\n    {\n        yield 'not part of the identifier' => [\n            false,\n            true,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                ],\n                'id' => false,\n            ],\n        ];\n\n        yield 'part of the identifier' => [\n            false,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id'],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'part of the identifier, but explicitly marked as nullable' => [\n            true,\n            false, // user's intent ignored at the ORM level\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => true],\n                ],\n                'id' => true,\n            ],\n        ];\n\n        yield 'part of the identifier, but explicitly marked as not nullable' => [\n            true,\n            false,\n            [\n                'fieldName' => 'foo',\n                'sourceEntity' => self::class,\n                'targetEntity' => self::class,\n                'isOwningSide' => true,\n                'joinColumns' => [\n                    ['name' => 'foo_id', 'referencedColumnName' => 'id', 'nullable' => false],\n                ],\n                'id' => true,\n            ],\n        ];\n    }\n\n    #[DataProvider('convertToArrayProvider')]\n    public function testConvertToArray(\n        bool $shouldHaveNullableKey,\n        bool|null $id,\n    ): void {\n        $mapping = new OneToOneOwningSideMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->joinColumns = [new JoinColumnMapping('foo_id', 'id')];\n        $mapping->id          = $id;\n\n        $mappingArray = $mapping->toArray();\n\n        foreach ($mappingArray['joinColumns'] as $joinColumn) {\n            if ($shouldHaveNullableKey) {\n                self::assertArrayHasKey('nullable', $joinColumn);\n            } else {\n                self::assertArrayNotHasKey('nullable', $joinColumn);\n            }\n        }\n    }\n\n    /** @return iterable<string, array{shouldHaveNullableKey: bool, id: bool|null}> */\n    public static function convertToArrayProvider(): iterable\n    {\n        yield 'not part of the identifier' => [\n            'shouldHaveNullableKey' => true,\n            'id' => false,\n        ];\n\n        yield 'still not part of the identifier' => [\n            'shouldHaveNullableKey' => true,\n            'id' => null,\n        ];\n\n        yield 'part of the identifier' => [\n            'shouldHaveNullableKey' => false,\n            'id' => true,\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/OwningSideMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\OwningSideMapping;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class OwningSideMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new MyOwningAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->inversedBy = 'bar';\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof OwningSideMapping);\n\n        self::assertSame('bar', $resurrectedMapping->inversedBy);\n    }\n}\n\nclass MyOwningAssociationMapping extends OwningSideMapping\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/PropertyAccessors/EnumPropertyAccessorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\EnumPropertyAccessor;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessorFactory;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass EnumPropertyAccessorTest extends OrmTestCase\n{\n    public function testEnumSetEnumGetValue(): void\n    {\n        $object   = new EnumClass();\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(EnumClass::class, 'enum');\n\n        $accessor = new EnumPropertyAccessor($accessor, EnumType::class);\n\n        $accessor->setValue($object, EnumType::A);\n\n        $this->assertEquals($object->enum, EnumType::A);\n        $this->assertEquals(EnumType::A->value, $accessor->getValue($object));\n    }\n\n    public function testEnumSetDatabaseGetValue(): void\n    {\n        $object   = new EnumClass();\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(EnumClass::class, 'enum');\n\n        $accessor = new EnumPropertyAccessor($accessor, EnumType::class);\n\n        $accessor->setValue($object, EnumType::A->value);\n\n        $this->assertEquals($object->enum, EnumType::A);\n        $this->assertEquals(EnumType::A->value, $accessor->getValue($object));\n    }\n\n    public function testEnumSetDatabaseArrayGetValue(): void\n    {\n        $object   = new EnumClass();\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(EnumClass::class, 'enumList');\n\n        $accessor = new EnumPropertyAccessor($accessor, EnumType::class);\n\n        $accessor->setValue($object, $values = [EnumType::A->value, EnumType::B->value, EnumType::C->value]);\n\n        $this->assertEquals($object->enumList, [EnumType::A, EnumType::B, EnumType::C]);\n        $this->assertEquals($values, $accessor->getValue($object));\n    }\n}\n\nclass EnumClass\n{\n    public EnumType $enum;\n    public array $enumList;\n}\n\nenum EnumType: string\n{\n    case A = 'a';\n    case B = 'b';\n    case C = 'c';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/PropertyAccessors/ObjectCastPropertyAccessorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\ObjectCastPropertyAccessor;\nuse Doctrine\\ORM\\Proxy\\InternalProxy;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass ObjectCastPropertyAccessorTest extends OrmTestCase\n{\n    public function testSetGetPublicPropertyValue(): void\n    {\n        $object   = new ObjectClass();\n        $accessor = ObjectCastPropertyAccessor::fromNames(ObjectClass::class, 'property');\n\n        $accessor->setValue($object, 'value');\n\n        $this->assertEquals($object->property, 'value');\n        $this->assertEquals('value', $accessor->getValue($object));\n    }\n\n    public function testSetGetPrivatePropertyValue(): void\n    {\n        $object   = new ObjectClass();\n        $accessor = ObjectCastPropertyAccessor::fromNames(ObjectClass::class, 'property2');\n\n        $accessor->setValue($object, 'value');\n\n        $this->assertEquals($object->getProperty2(), 'value');\n        $this->assertEquals('value', $accessor->getValue($object));\n    }\n\n    public function testSetGetInternalProxyValue(): void\n    {\n        $object   = new ObjectClassInternalProxy();\n        $accessor = ObjectCastPropertyAccessor::fromNames(ObjectClassInternalProxy::class, 'property');\n\n        $accessor->setValue($object, 'value');\n\n        $this->assertEquals($object->property, 'value');\n        $this->assertEquals('value', $accessor->getValue($object));\n        $this->assertFalse($object->isInitialized);\n        $this->assertEquals(2, $object->counter);\n    }\n}\n\nclass ObjectClass\n{\n    /** @var string */\n    public $property;\n    /** @var string */\n    private $property2;\n\n    public function getProperty2(): string\n    {\n        return $this->property2;\n    }\n}\n\nclass ObjectClassInternalProxy implements InternalProxy\n{\n    /** @var string */\n    public $property;\n    public bool $isInitialized = false;\n    public int $counter        = 0;\n\n    public function __setInitialized(bool $initialized): void\n    {\n        $this->isInitialized = $initialized;\n        $this->counter++;\n    }\n\n    public function __load(): void\n    {\n    }\n\n    /** Returns whether this proxy is initialized or not. */\n    public function __isInitialized(): bool\n    {\n        return $this->isInitialized;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/PropertyAccessors/RawValuePropertyAccessorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\RawValuePropertyAccessor;\nuse Doctrine\\Tests\\Models\\PropertyHooks\\User;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\nuse ReflectionObject;\n\nuse function trim;\n\n#[RequiresPhp(versionRequirement: '>= 8.4.0')]\nclass RawValuePropertyAccessorTest extends OrmTestCase\n{\n    public function testSetGetValue(): void\n    {\n        $object        = new User();\n        $reflection    = new ReflectionObject($object);\n        $accessorFirst = RawValuePropertyAccessor::fromReflectionProperty($reflection->getProperty('first'));\n        $accessorLast  = RawValuePropertyAccessor::fromReflectionProperty($reflection->getProperty('last'));\n\n        $accessorFirst->setValue($object, 'Benjamin');\n        $accessorLast->setValue($object, 'Eberlei');\n\n        self::assertEquals('Benjamin Eberlei', $object->fullName);\n        self::assertEquals('Benjamin', $accessorFirst->getValue($object));\n        self::assertEquals('Eberlei', $accessorLast->getValue($object));\n\n        $accessorFirst->setValue($object, '');\n        $accessorLast->setValue($object, '');\n\n        self::assertEquals('', trim($object->fullName));\n    }\n\n    public function testSetGetValueWithLanguage(): void\n    {\n        $object     = new User();\n        $reflection = new ReflectionObject($object);\n        $accessor   = RawValuePropertyAccessor::fromReflectionProperty($reflection->getProperty('language'));\n\n        $accessor->setValue($object, 'en');\n\n        self::assertEquals('EN', $object->language);\n        self::assertEquals('en', $accessor->getValue($object));\n\n        $accessor->setValue($object, 'EN');\n\n        self::assertEquals('EN', $object->language);\n        self::assertEquals('EN', $accessor->getValue($object));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/PropertyAccessors/ReadOnlyAccessorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessorFactory;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\ReadonlyAccessor;\nuse Doctrine\\Tests\\OrmTestCase;\nuse LogicException;\n\nclass ReadOnlyAccessorTest extends OrmTestCase\n{\n    public function testReadOnlyProperty(): void\n    {\n        $object   = new ReadOnlyClass();\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(ReadOnlyClass::class, 'property');\n\n        $this->assertInstanceOf(ReadonlyAccessor::class, $accessor);\n\n        $accessor->setValue($object, 1);\n\n        $this->assertEquals($object->property, 1);\n        $this->assertEquals(1, $accessor->getValue($object));\n    }\n\n    public function testReadOnlyPropertyOnlyOnce(): void\n    {\n        $object   = new ReadOnlyClass();\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(ReadOnlyClass::class, 'property');\n\n        $this->assertInstanceOf(ReadonlyAccessor::class, $accessor);\n\n        $accessor->setValue($object, 1);\n        $this->expectException(LogicException::class);\n        $accessor->setValue($object, 2);\n    }\n}\n\nclass ReadOnlyClass\n{\n    public readonly int $property;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\PropertyAccessors;\n\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\PropertyAccessorFactory;\nuse Doctrine\\ORM\\Mapping\\PropertyAccessors\\TypedNoDefaultPropertyAccessor;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass TypedNoDefaultPropertyAccessorTest extends OrmTestCase\n{\n    public function testSetValueWithoutDefault(): void\n    {\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(TypedClass::class, 'property');\n\n        $this->assertInstanceOf(TypedNoDefaultPropertyAccessor::class, $accessor);\n\n        $object = new TypedClass();\n        $accessor->setValue($object, 42);\n        $this->assertEquals(42, $accessor->getValue($object));\n    }\n\n    public function testSetNullWithoutDefault(): void\n    {\n        $accessor = PropertyAccessorFactory::createPropertyAccessor(TypedClass::class, 'property');\n\n        $object = new TypedClass();\n        $accessor->setValue($object, null);\n        $this->assertNull($accessor->getValue($object));\n\n        $accessor->setValue($object, 42);\n        $this->assertEquals(42, $accessor->getValue($object));\n\n        $accessor->setValue($object, null);\n        $this->assertNull($accessor->getValue($object));\n    }\n}\n\nclass TypedClass\n{\n    public int $property;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/QuoteStrategyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy;\nuse Doctrine\\ORM\\Mapping\\QuoteStrategy;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1845')]\nclass QuoteStrategyTest extends OrmTestCase\n{\n    private DefaultQuoteStrategy $strategy;\n\n    private AbstractPlatform $platform;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $em             = $this->getTestEntityManager();\n        $this->platform = $em->getConnection()->getDatabasePlatform();\n        $this->strategy = new DefaultQuoteStrategy();\n    }\n\n    private function createClassMetadata(string $className): ClassMetadata\n    {\n        $cm = new ClassMetadata($className);\n        $cm->initializeReflection(new RuntimeReflectionService());\n\n        return $cm;\n    }\n\n    public function testConfiguration(): void\n    {\n        $em     = $this->getTestEntityManager();\n        $config = $em->getConfiguration();\n\n        self::assertInstanceOf(QuoteStrategy::class, $config->getQuoteStrategy());\n        self::assertInstanceOf(DefaultQuoteStrategy::class, $config->getQuoteStrategy());\n\n        $config->setQuoteStrategy(new MyQuoteStrategy());\n\n        self::assertInstanceOf(QuoteStrategy::class, $config->getQuoteStrategy());\n        self::assertInstanceOf(MyQuoteStrategy::class, $config->getQuoteStrategy());\n    }\n\n    public function testGetColumnName(): void\n    {\n        $cm = $this->createClassMetadata(CmsUser::class);\n        $cm->mapField(['fieldName' => 'name', 'columnName' => '`name`']);\n        $cm->mapField(['fieldName' => 'id', 'columnName' => 'id']);\n\n        self::assertEquals('id', $this->strategy->getColumnName('id', $cm, $this->platform));\n        self::assertEquals('\"name\"', $this->strategy->getColumnName('name', $cm, $this->platform));\n    }\n\n    public function testGetTableName(): void\n    {\n        $cm = $this->createClassMetadata(CmsUser::class);\n        $cm->setPrimaryTable(['name' => '`cms_user`']);\n        self::assertEquals('\"cms_user\"', $this->strategy->getTableName($cm, $this->platform));\n\n        $cm = new ClassMetadata(CmsUser::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        $cm->setPrimaryTable(['name' => 'cms_user']);\n        self::assertEquals('cms_user', $this->strategy->getTableName($cm, $this->platform));\n    }\n\n    public function testJoinTableName(): void\n    {\n        $cm1 = $this->createClassMetadata(CmsAddress::class);\n        $cm2 = $this->createClassMetadata(CmsAddress::class);\n\n        $cm1->mapManyToMany(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n                'inversedBy'    => 'users',\n                'joinTable'     => ['name' => '`cmsaddress_cmsuser`'],\n            ],\n        );\n\n        $cm2->mapManyToMany(\n            [\n                'fieldName'     => 'user',\n                'targetEntity'  => 'CmsUser',\n                'inversedBy'    => 'users',\n                'joinTable'     => ['name' => 'cmsaddress_cmsuser'],\n            ],\n        );\n\n        self::assertEquals('\"cmsaddress_cmsuser\"', $this->strategy->getJoinTableName($cm1->associationMappings['user'], $cm1, $this->platform));\n        self::assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName($cm2->associationMappings['user'], $cm2, $this->platform));\n    }\n\n    public function testIdentifierColumnNames(): void\n    {\n        $cm1 = $this->createClassMetadata(CmsAddress::class);\n        $cm2 = $this->createClassMetadata(CmsAddress::class);\n\n        $cm1->mapField(\n            [\n                'id'            => true,\n                'fieldName'     => 'id',\n                'columnName'    => '`id`',\n            ],\n        );\n\n        $cm2->mapField(\n            [\n                'id'            => true,\n                'fieldName'     => 'id',\n                'columnName'    => 'id',\n            ],\n        );\n\n        self::assertEquals(['\"id\"'], $this->strategy->getIdentifierColumnNames($cm1, $this->platform));\n        self::assertEquals(['id'], $this->strategy->getIdentifierColumnNames($cm2, $this->platform));\n    }\n\n    public function testColumnAlias(): void\n    {\n        $i = 0;\n        self::assertEquals('columnName_0', $this->strategy->getColumnAlias('columnName', $i++, $this->platform));\n        self::assertEquals('column_name_1', $this->strategy->getColumnAlias('column_name', $i++, $this->platform));\n        self::assertEquals('COLUMN_NAME_2', $this->strategy->getColumnAlias('COLUMN_NAME', $i++, $this->platform));\n        self::assertEquals('COLUMNNAME_3', $this->strategy->getColumnAlias('COLUMN-NAME-', $i++, $this->platform));\n    }\n\n    public function testQuoteIdentifierJoinColumns(): void\n    {\n        $cm = $this->createClassMetadata(DDC117ArticleDetails::class);\n\n        $cm->mapOneToOne(\n            [\n                'id'            => true,\n                'fieldName'     => 'article',\n                'targetEntity'  => DDC117Article::class,\n                'joinColumns'    => [\n                    ['name' => '`article`', 'referencedColumnName' => 'article'],\n                ],\n            ],\n        );\n\n        self::assertEquals(['\"article\"'], $this->strategy->getIdentifierColumnNames($cm, $this->platform));\n    }\n\n    public function testJoinColumnName(): void\n    {\n        $cm = $this->createClassMetadata(DDC117ArticleDetails::class);\n\n        $cm->mapOneToOne(\n            [\n                'id'            => true,\n                'fieldName'     => 'article',\n                'targetEntity'  => DDC117Article::class,\n                'joinColumns'    => [\n                    ['name' => '`article`', 'referencedColumnName' => 'article'],\n                ],\n            ],\n        );\n\n        $joinColumn = $cm->associationMappings['article']->joinColumns[0];\n        self::assertEquals('\"article\"', $this->strategy->getJoinColumnName($joinColumn, $cm, $this->platform));\n    }\n\n    public function testReferencedJoinColumnName(): void\n    {\n        $cm = $this->createClassMetadata(DDC117ArticleDetails::class);\n\n        $cm->mapOneToOne(\n            [\n                'id'            => true,\n                'fieldName'     => 'article',\n                'targetEntity'  => DDC117Article::class,\n                'joinColumns'    => [\n                    ['name' => '`article`', 'referencedColumnName' => 'id'],\n                ],\n            ],\n        );\n\n        $joinColumn = $cm->associationMappings['article']->joinColumns[0];\n        self::assertEquals('\"id\"', $this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform));\n    }\n}\n\nclass MyQuoteStrategy extends DefaultQuoteStrategy\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ReflectionEmbeddedPropertyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Instantiator\\Instantiator;\nuse Doctrine\\ORM\\Mapping\\ReflectionEmbeddedProperty;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\Models\\Reflection\\AbstractEmbeddable;\nuse Doctrine\\Tests\\Models\\Reflection\\ArrayObjectExtendingClass;\nuse Doctrine\\Tests\\Models\\Reflection\\ConcreteEmbeddable;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionProperty;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Mapping\\ReflectionEmbeddedProperty}\n */\n#[CoversClass(ReflectionEmbeddedProperty::class)]\nclass ReflectionEmbeddedPropertyTest extends TestCase\n{\n    /**\n     * @param ReflectionProperty $parentProperty  property of the embeddable/entity where to write the embeddable to\n     * @param ReflectionProperty $childProperty   property of the embeddable class where to write values to\n     * @param string             $embeddableClass name of the embeddable class to be instantiated\n     */\n    #[DataProvider('getTestedReflectionProperties')]\n    public function testCanSetAndGetEmbeddedProperty(\n        ReflectionProperty $parentProperty,\n        ReflectionProperty $childProperty,\n        string $embeddableClass,\n    ): void {\n        $embeddedPropertyReflection = new ReflectionEmbeddedProperty($parentProperty, $childProperty, $embeddableClass);\n\n        $instantiator = new Instantiator();\n\n        $object = $instantiator->instantiate($parentProperty->getDeclaringClass()->getName());\n\n        $embeddedPropertyReflection->setValue($object, 'newValue');\n\n        self::assertSame('newValue', $embeddedPropertyReflection->getValue($object));\n\n        $embeddedPropertyReflection->setValue($object, 'changedValue');\n\n        self::assertSame('changedValue', $embeddedPropertyReflection->getValue($object));\n    }\n\n    /**\n     * @param ReflectionProperty $parentProperty  property of the embeddable/entity where to write the embeddable to\n     * @param ReflectionProperty $childProperty   property of the embeddable class where to write values to\n     * @param string             $embeddableClass name of the embeddable class to be instantiated\n     */\n    #[DataProvider('getTestedReflectionProperties')]\n    public function testWillSkipReadingPropertiesFromNullEmbeddable(\n        ReflectionProperty $parentProperty,\n        ReflectionProperty $childProperty,\n        string $embeddableClass,\n    ): void {\n        $embeddedPropertyReflection = new ReflectionEmbeddedProperty($parentProperty, $childProperty, $embeddableClass);\n\n        $instantiator = new Instantiator();\n\n        self::assertNull($embeddedPropertyReflection->getValue(\n            $instantiator->instantiate($parentProperty->getDeclaringClass()->getName()),\n        ));\n    }\n\n    /** @return ReflectionProperty[][]|string[][] */\n    public static function getTestedReflectionProperties(): array\n    {\n        return [\n            [\n                new ReflectionProperty(BooleanModel::class, 'id'),\n                new ReflectionProperty(BooleanModel::class, 'id'),\n                BooleanModel::class,\n            ],\n            // reflection on embeddables that have properties defined in abstract ancestors:\n            [\n                new ReflectionProperty(BooleanModel::class, 'id'),\n                new ReflectionProperty(AbstractEmbeddable::class, 'propertyInAbstractClass'),\n                ConcreteEmbeddable::class,\n            ],\n            [\n                new ReflectionProperty(BooleanModel::class, 'id'),\n                new ReflectionProperty(ConcreteEmbeddable::class, 'propertyInConcreteClass'),\n                ConcreteEmbeddable::class,\n            ],\n            // reflection on classes extending internal PHP classes:\n            [\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'),\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'privateProperty'),\n                ArrayObjectExtendingClass::class,\n            ],\n            [\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'),\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'protectedProperty'),\n                ArrayObjectExtendingClass::class,\n            ],\n            [\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'),\n                new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'),\n                ArrayObjectExtendingClass::class,\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ReflectionReadonlyPropertyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\ReflectionReadonlyProperty;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\ReadonlyProperties\\Author;\nuse InvalidArgumentException;\nuse LogicException;\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionProperty;\n\nclass ReflectionReadonlyPropertyTest extends TestCase\n{\n    public function testSecondWriteWithSameValue(): void\n    {\n        $author = new Author();\n\n        $wrappedReflection = new ReflectionProperty($author, 'name');\n        $reflection        = new ReflectionReadonlyProperty($wrappedReflection);\n\n        $reflection->setValue($author, 'John Doe');\n\n        self::assertSame('John Doe', $wrappedReflection->getValue($author));\n        self::assertSame('John Doe', $reflection->getValue($author));\n\n        $reflection->setValue($author, 'John Doe');\n\n        self::assertSame('John Doe', $wrappedReflection->getValue($author));\n        self::assertSame('John Doe', $reflection->getValue($author));\n    }\n\n    public function testSecondWriteWithDifferentValue(): void\n    {\n        $author = new Author();\n\n        $wrappedReflection = new ReflectionProperty($author, 'name');\n        $reflection        = new ReflectionReadonlyProperty($wrappedReflection);\n\n        $reflection->setValue($author, 'John Doe');\n\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage('Attempting to change readonly property Doctrine\\Tests\\Models\\ReadonlyProperties\\Author::$name.');\n        $reflection->setValue($author, 'Jane Doe');\n    }\n\n    public function testNonReadonlyPropertiesAreForbidden(): void\n    {\n        $reflection = new ReflectionProperty(CmsTag::class, 'name');\n\n        $this->expectException(InvalidArgumentException::class);\n        new ReflectionReadonlyProperty($reflection);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/StaticPHPMappingDriverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889Class;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse const DIRECTORY_SEPARATOR;\n\nclass StaticPHPMappingDriverTest extends MappingDriverTestCase\n{\n    protected function loadDriver(): MappingDriver\n    {\n        return new StaticPHPDriver(__DIR__ . DIRECTORY_SEPARATOR . 'php');\n    }\n\n    /**\n     * All class with static::loadMetadata are entities for php driver\n     */\n    #[Group('DDC-889')]\n    public function testinvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void\n    {\n        self::assertInstanceOf(ClassMetadata::class, $this->createClassMetadata(DDC889Class::class));\n    }\n\n    #[Group('DDC-2825')]\n    #[Group('881')]\n    public function testSchemaDefinitionViaExplicitTableSchemaAttributeProperty(): void\n    {\n        self::markTestIncomplete();\n    }\n\n    #[Group('DDC-2825')]\n    #[Group('881')]\n    public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAttributeProperty(): void\n    {\n        self::markTestIncomplete();\n    }\n\n    public function testEntityIncorrectIndexes(): void\n    {\n        self::markTestSkipped('Static PHP driver does not ensure index correctness');\n    }\n\n    public function testEntityIncorrectUniqueContraint(): void\n    {\n        self::markTestSkipped('Static PHP driver does not ensure index correctness');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/Symfony/DriverTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\Symfony;\n\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileDriver;\nuse Doctrine\\Persistence\\Mapping\\MappingException;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\nuse RecursiveDirectoryIterator;\nuse RecursiveIteratorIterator;\n\nuse function mkdir;\nuse function rmdir;\nuse function sys_get_temp_dir;\nuse function touch;\nuse function unlink;\n\n#[Group('DDC-1418')]\nabstract class DriverTestCase extends TestCase\n{\n    private string $dir;\n\n    public function testFindMappingFile(): void\n    {\n        $driver = $this->getDriver(\n            [\n                'MyNamespace\\MySubnamespace\\EntityFoo' => 'foo',\n                'MyNamespace\\MySubnamespace\\Entity' => $this->dir,\n            ],\n        );\n\n        touch($filename = $this->dir . '/Foo' . $this->getFileExtension());\n        self::assertEquals($filename, $driver->getLocator()->findMappingFile('MyNamespace\\MySubnamespace\\Entity\\Foo'));\n    }\n\n    public function testFindMappingFileInSubnamespace(): void\n    {\n        $driver = $this->getDriver(\n            [\n                'MyNamespace\\MySubnamespace\\Entity' => $this->dir,\n            ],\n        );\n\n        touch($filename = $this->dir . '/Foo.Bar' . $this->getFileExtension());\n        self::assertEquals($filename, $driver->getLocator()->findMappingFile('MyNamespace\\MySubnamespace\\Entity\\Foo\\Bar'));\n    }\n\n    public function testFindMappingFileNamespacedFoundFileNotFound(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('No mapping file found named');\n\n        $driver = $this->getDriver(\n            [\n                'MyNamespace\\MySubnamespace\\Entity' => $this->dir,\n            ],\n        );\n\n        $driver->getLocator()->findMappingFile('MyNamespace\\MySubnamespace\\Entity\\Foo');\n    }\n\n    public function testFindMappingNamespaceNotFound(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"No mapping file found named 'Foo\" . $this->getFileExtension() . \"' for class 'MyOtherNamespace\\MySubnamespace\\Entity\\Foo'.\");\n\n        $driver = $this->getDriver(\n            [\n                'MyNamespace\\MySubnamespace\\Entity' => $this->dir,\n            ],\n        );\n\n        $driver->getLocator()->findMappingFile('MyOtherNamespace\\MySubnamespace\\Entity\\Foo');\n    }\n\n    protected function setUp(): void\n    {\n        $this->dir = sys_get_temp_dir() . '/abstract_driver_test';\n        @mkdir($this->dir, 0777, true);\n    }\n\n    protected function tearDown(): void\n    {\n        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->dir), RecursiveIteratorIterator::CHILD_FIRST);\n\n        foreach ($iterator as $path) {\n            if ($path->isDir()) {\n                @rmdir((string) $path);\n            } else {\n                @unlink((string) $path);\n            }\n        }\n\n        @rmdir($this->dir);\n    }\n\n    abstract protected function getFileExtension(): string;\n\n    abstract protected function getDriver(array $paths = []): FileDriver;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/Symfony/XmlDriverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\Symfony;\n\nuse Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver;\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileDriver;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_flip;\n\n#[Group('DDC-1418')]\nclass XmlDriverTest extends DriverTestCase\n{\n    protected function getFileExtension(): string\n    {\n        return '.orm.xml';\n    }\n\n    protected function getDriver(array $paths = []): FileDriver\n    {\n        return new SimplifiedXmlDriver(array_flip($paths));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/TableMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TableMappingTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    #[IgnoreDeprecations]\n    public function testDeprecationOnIndexesPropertyIsTriggered(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357');\n\n        new Table(indexes: []);\n    }\n\n    #[IgnoreDeprecations]\n    public function testDeprecationOnUniqueConstraintsPropertyIsTriggered(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357');\n\n        new Table(uniqueConstraints: []);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/ToManyAssociationMappingTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ToManyAssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ToManyAssociationMappingImplementation;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function assert;\nuse function serialize;\nuse function unserialize;\n\nfinal class ToManyAssociationMappingTest extends TestCase\n{\n    public function testItSurvivesSerialization(): void\n    {\n        $mapping = new MyToManyAssociationMapping(\n            fieldName: 'foo',\n            sourceEntity: self::class,\n            targetEntity: self::class,\n        );\n\n        $mapping->indexBy = 'foo';\n        $mapping->orderBy = ['foo' => 'asc'];\n\n        $resurrectedMapping = unserialize(serialize($mapping));\n        assert($resurrectedMapping instanceof ToManyAssociationMapping);\n\n        self::assertSame('foo', $resurrectedMapping->fieldName);\n        self::assertSame(['foo' => 'asc'], $resurrectedMapping->orderBy);\n    }\n}\n\nclass MyToManyAssociationMapping extends AssociationMapping implements ToManyAssociationMapping\n{\n    use ToManyAssociationMappingImplementation;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Tests\\Models\\Enums\\FaultySwitch;\nuse Doctrine\\Tests\\OrmTestCase;\nuse ReflectionClass;\n\nclass TypedEnumFieldMapperTest extends OrmTestCase\n{\n    private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper\n    {\n        return new DefaultTypedFieldMapper();\n    }\n\n    public function testNotBackedEnumThrows(): void\n    {\n        $reflectionClass = new ReflectionClass(FaultySwitch::class);\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\n            'Attempting to map a non-backed enum type Doctrine\\Tests\\Models\\Enums\\SwitchStatus in entity Doctrine\\Tests\\Models\\Enums\\FaultySwitch::$status. Please use backed enums only',\n        );\n\n        self::defaultTypedFieldMapper()->validateAndComplete(['fieldName' => 'status'], $reflectionClass->getProperty('status'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/TypedFieldMapper/CustomIntAsStringTypedFieldMapper.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping\\TypedFieldMapper;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\TypedFieldMapper;\nuse ReflectionNamedType;\nuse ReflectionProperty;\n\nfinal class CustomIntAsStringTypedFieldMapper implements TypedFieldMapper\n{\n    /**\n     * {@inheritDoc}\n     */\n    public function validateAndComplete(array $mapping, ReflectionProperty $field): array\n    {\n        $type = $field->getType();\n\n        if (\n            ! isset($mapping['type'])\n            && ($type instanceof ReflectionNamedType)\n        ) {\n            if ($type->getName() === 'int') {\n                $mapping['type'] = Types::STRING;\n            }\n        }\n\n        return $mapping;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/TypedFieldMapperTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\ChainTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\DefaultTypedFieldMapper;\nuse Doctrine\\ORM\\Mapping\\TypedFieldMapper;\nuse Doctrine\\Tests\\Models\\TypedProperties\\UserTyped;\nuse Doctrine\\Tests\\ORM\\Mapping\\TypedFieldMapper\\CustomIntAsStringTypedFieldMapper;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionClass;\n\nuse function defined;\n\n#[Group('GH10313')]\nclass TypedFieldMapperTest extends OrmTestCase\n{\n    private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper\n    {\n        return new DefaultTypedFieldMapper();\n    }\n\n    private static function customTypedFieldMapper(): CustomIntAsStringTypedFieldMapper\n    {\n        return new CustomIntAsStringTypedFieldMapper();\n    }\n\n    private static function chainTypedFieldMapper(): ChainTypedFieldMapper\n    {\n        return new ChainTypedFieldMapper(self::customTypedFieldMapper(), self::defaultTypedFieldMapper());\n    }\n\n    /**\n     * Data Provider for NamingStrategy#classToTableName\n     *\n     * @return Generator<\n     *     array{\n     *         TypedFieldMapper,\n     *         ReflectionClass,\n     *         array{fieldName: string, enumType?: string, type?: mixed},\n     *         array{fieldName: string, enumType?: string, type?: mixed}\n     *     }>\n     */\n    public static function dataFieldToMappedField(): Generator\n    {\n        $reflectionClass = new ReflectionClass(UserTyped::class);\n\n        // DefaultTypedFieldMapper\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::INTEGER]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateInterval'], ['fieldName' => 'dateInterval', 'type' => Types::DATEINTERVAL]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTime'], ['fieldName' => 'dateTime', 'type' => Types::DATETIME_MUTABLE]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'dateTimeImmutable'], ['fieldName' => 'dateTimeImmutable', 'type' => Types::DATETIME_IMMUTABLE]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'array'], ['fieldName' => 'array', 'type' => Types::JSON]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'boolean'], ['fieldName' => 'boolean', 'type' => Types::BOOLEAN]];\n        yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'float'], ['fieldName' => 'float', 'type' => Types::FLOAT]];\n\n        if (defined(Types::class . '::NUMBER')) {\n            yield [self::defaultTypedFieldMapper(), $reflectionClass, ['fieldName' => 'bodyHeight'], ['fieldName' => 'bodyHeight', 'type' => Types::NUMBER]];\n        }\n\n        // CustomIntAsStringTypedFieldMapper\n        yield [self::customTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]];\n\n        // ChainTypedFieldMapper\n        yield [self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'id'], ['fieldName' => 'id', 'type' => Types::STRING]];\n        yield [self::chainTypedFieldMapper(), $reflectionClass, ['fieldName' => 'username'], ['fieldName' => 'username', 'type' => Types::STRING]];\n    }\n\n    /**\n     * @param array{fieldName: string, enumType?: string, type?: mixed} $mapping\n     * @param array{fieldName: string, enumType?: string, type?: mixed} $finalMapping\n     */\n    #[DataProvider('dataFieldToMappedField')]\n    public function testValidateAndComplete(\n        TypedFieldMapper $typedFieldMapper,\n        ReflectionClass $reflectionClass,\n        array $mapping,\n        array $finalMapping,\n    ): void {\n        self::assertSame($finalMapping, $typedFieldMapper->validateAndComplete($mapping, $reflectionClass->getProperty($mapping['fieldName'])));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/XmlMappingDriverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Mapping;\n\nuse Doctrine\\ORM\\Cache\\Exception\\CacheException;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Driver\\XmlDriver;\nuse Doctrine\\ORM\\Mapping\\EmbeddedClassMapping;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Persistence\\Mapping\\MappingException as PersistenceMappingException;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Translation;\nuse Doctrine\\Tests\\Models\\DDC3293\\DDC3293User;\nuse Doctrine\\Tests\\Models\\DDC3293\\DDC3293UserPrefixed;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889Class;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889Entity;\nuse Doctrine\\Tests\\Models\\DDC889\\DDC889SuperClass;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\Models\\GH7141\\GH7141Article;\nuse Doctrine\\Tests\\Models\\GH7316\\GH7316Article;\nuse Doctrine\\Tests\\Models\\InvalidXml;\nuse Doctrine\\Tests\\Models\\Project\\Project;\nuse Doctrine\\Tests\\Models\\Project\\ProjectId;\nuse Doctrine\\Tests\\Models\\Project\\ProjectInvalidMapping;\nuse Doctrine\\Tests\\Models\\Project\\ProjectName;\nuse Doctrine\\Tests\\Models\\ValueObjects\\Name;\nuse Doctrine\\Tests\\Models\\ValueObjects\\Person;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function substr_count;\n\nuse const DIRECTORY_SEPARATOR;\n\nclass XmlMappingDriverTest extends MappingDriverTestCase\n{\n    protected function loadDriver(): MappingDriver\n    {\n        return new XmlDriver(\n            __DIR__ . DIRECTORY_SEPARATOR . 'xml',\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            true,\n        );\n    }\n\n    public function testClassTableInheritanceDiscriminatorMap(): void\n    {\n        $mappingDriver = $this->loadDriver();\n\n        $class = new ClassMetadata(CTI::class);\n        $class->initializeReflection(new RuntimeReflectionService());\n        $mappingDriver->loadMetadataForClass(CTI::class, $class);\n\n        $expectedMap = [\n            'foo' => CTIFoo::class,\n            'bar' => CTIBar::class,\n            'baz' => CTIBaz::class,\n        ];\n\n        self::assertCount(3, $class->discriminatorMap);\n        self::assertEquals($expectedMap, $class->discriminatorMap);\n    }\n\n    public function testFailingSecondLevelCacheAssociation(): void\n    {\n        $this->expectException(CacheException::class);\n        $this->expectExceptionMessage('Entity association field \"Doctrine\\Tests\\ORM\\Mapping\\XMLSLC#foo\" not configured as part of the second-level cache.');\n        $mappingDriver = $this->loadDriver();\n\n        $class = new ClassMetadata(XMLSLC::class);\n        $mappingDriver->loadMetadataForClass(XMLSLC::class, $class);\n    }\n\n    public function testIdentifierWithAssociationKey(): void\n    {\n        $driver  = $this->loadDriver();\n        $em      = $this->getTestEntityManager();\n        $factory = new ClassMetadataFactory();\n\n        $em->getConfiguration()->setMetadataDriverImpl($driver);\n        $factory->setEntityManager($em);\n\n        $class = $factory->getMetadataFor(DDC117Translation::class);\n\n        self::assertEquals(['language', 'article'], $class->identifier);\n        self::assertArrayHasKey('article', $class->associationMappings);\n\n        self::assertTrue($class->associationMappings['article']->id);\n    }\n\n    public function testEmbeddableMapping(): void\n    {\n        $class = $this->createClassMetadata(Name::class);\n\n        self::assertTrue($class->isEmbeddedClass);\n    }\n\n    #[Group('DDC-3293')]\n    #[Group('DDC-3477')]\n    #[Group('1238')]\n    public function testEmbeddedMappingsWithUseColumnPrefix(): void\n    {\n        $factory = new ClassMetadataFactory();\n        $em      = $this->getTestEntityManager();\n\n        $em->getConfiguration()->setMetadataDriverImpl($this->loadDriver());\n        $factory->setEntityManager($em);\n\n        self::assertEquals(\n            '__prefix__',\n            $factory->getMetadataFor(DDC3293UserPrefixed::class)\n                ->embeddedClasses['address']->columnPrefix,\n        );\n    }\n\n    #[Group('DDC-3293')]\n    #[Group('DDC-3477')]\n    #[Group('1238')]\n    public function testEmbeddedMappingsWithFalseUseColumnPrefix(): void\n    {\n        $factory = new ClassMetadataFactory();\n        $em      = $this->getTestEntityManager();\n\n        $em->getConfiguration()->setMetadataDriverImpl($this->loadDriver());\n        $factory->setEntityManager($em);\n\n        self::assertFalse(\n            $factory->getMetadataFor(DDC3293User::class)\n                ->embeddedClasses['address']->columnPrefix,\n        );\n    }\n\n    public function testEmbeddedMapping(): void\n    {\n        $class = $this->createClassMetadata(Person::class);\n\n        self::assertEquals(\n            [\n                'name' => EmbeddedClassMapping::fromMappingArray([\n                    'class' => Name::class,\n                    'columnPrefix' => 'nm_',\n                    'declaredField' => null,\n                    'originalField' => null,\n                ]),\n            ],\n            $class->embeddedClasses,\n        );\n    }\n\n    #[Group('DDC-1468')]\n    public function testItMentionsFilenameAndEntityNameOnInvalidMapping(): void\n    {\n        $this->expectException(PersistenceMappingException::class);\n        $this->expectExceptionMessage('Invalid mapping file \\'Doctrine.Tests.Models.Generic.BooleanModel.dcm.xml\\' for class \\'Doctrine\\Tests\\Models\\Generic\\BooleanModel\\'.');\n        $this->createClassMetadata(BooleanModel::class);\n    }\n\n    #[Group('DDC-2429')]\n    #[DataProvider('dataValidSchema')]\n    public function testValidateXmlSchema(\n        string $class,\n        string $tableName,\n        array $fieldNames,\n        array $associationNames,\n    ): void {\n        $metadata = $this->createClassMetadata($class);\n\n        $this->assertInstanceOf(ClassMetadata::class, $metadata);\n        $this->assertEquals($metadata->getTableName(), $tableName);\n        $this->assertEquals($metadata->getFieldNames(), $fieldNames);\n        $this->assertEquals($metadata->getAssociationNames(), $associationNames);\n    }\n\n    /** @phpstan-return []array{0: class-string, 1: string, 2: list<string>, 3: list<string>} */\n    public static function dataValidSchema(): array\n    {\n        return [\n            [\n                User::class,\n                'cms_users',\n                ['name', 'email', 'version', 'indexed', 'id'],\n                ['address', 'phonenumbers', 'groups'],\n            ],\n            [\n                DDC889Entity::class,\n                'DDC889Entity',\n                [],\n                [],\n            ],\n            [\n                DDC889SuperClass::class,\n                'DDC889SuperClass',\n                ['name'],\n                [],\n            ],\n        ];\n    }\n\n    /**\n     * @param class-string                 $class\n     * @param non-empty-array<string, int> $expectedExceptionOccurrences\n     */\n    #[DataProvider('dataInvalidSchema')]\n    public function testValidateIncorrectXmlSchema(string $class, array $expectedExceptionOccurrences): void\n    {\n        try {\n            $this->createClassMetadata($class);\n\n            $this->fail('XML schema validation should throw a MappingException');\n        } catch (MappingException $exception) {\n            foreach ($expectedExceptionOccurrences as $exceptionContent => $occurrencesCount) {\n                $this->assertEquals($occurrencesCount, substr_count($exception->getMessage(), $exceptionContent));\n            }\n        }\n    }\n\n    /** @return non-empty-list<array{0: class-string, 1: non-empty-array<string, int>}> */\n    public static function dataInvalidSchema(): array\n    {\n        return [\n            [\n                DDC889Class::class,\n                ['This element is not expected' => 1],\n            ],\n            [\n                UserIncorrectAttributes::class,\n                [\n                    'attribute \\'field\\': The attribute \\'field\\' is not allowed' => 2,\n                    'The attribute \\'name\\' is required but missing' => 2,\n                    'attribute \\'fieldName\\': The attribute \\'fieldName\\' is not allowed' => 1,\n                ],\n            ],\n            [\n                UserMissingAttributes::class,\n                ['The attribute \\'name\\' is required but missing' => 1],\n            ],\n            [\n                ProjectInvalidMapping::class,\n                ['attribute \\'type\\': [facet \\'pattern\\'] The value' => 2],\n            ],\n        ];\n    }\n\n    #[Group('GH-7141')]\n    public function testOneToManyDefaultOrderByAsc(): void\n    {\n        $driver = $this->loadDriver();\n        $class  = new ClassMetadata(GH7141Article::class);\n\n        $class->initializeReflection(new RuntimeReflectionService());\n        $driver->loadMetadataForClass(GH7141Article::class, $class);\n\n        self::assertSame(\n            'ASC',\n            $class->getMetadataValue('associationMappings')['tags']->orderBy['position'],\n        );\n    }\n\n    public function testManyToManyDefaultOrderByAsc(): void\n    {\n        $class = new ClassMetadata(GH7316Article::class);\n        $class->initializeReflection(new RuntimeReflectionService());\n\n        $driver = $this->loadDriver();\n        $driver->loadMetadataForClass(GH7316Article::class, $class);\n\n        self::assertSame(\n            'ASC',\n            $class->getMetadataValue('associationMappings')['tags']->orderBy['position'],\n        );\n    }\n\n    #[Group('DDC-889')]\n    public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('libxml error: Element \\'{http://doctrine-project.org/schemas/orm/doctrine-mapping}class\\': This element is not expected.');\n\n        $this->createClassMetadata(DDC889Class::class);\n    }\n\n    public function testClassNameInFieldOrId(): void\n    {\n        $class = new ClassMetadata(Project::class);\n        $class->initializeReflection(new RuntimeReflectionService());\n\n        $driver = $this->loadDriver();\n        $driver->loadMetadataForClass(Project::class, $class);\n\n        /** @var array{type: string} $id */\n        $id = $class->getFieldMapping('id');\n        /** @var array{type: string} $name */\n        $name = $class->getFieldMapping('name');\n\n        self::assertEquals(ProjectId::class, $id->type);\n        self::assertEquals(ProjectName::class, $name->type);\n    }\n\n    public function testDisablingXmlValidationIsPossible(): void\n    {\n        $this->expectNotToPerformAssertions();\n\n        new XmlDriver(\n            __DIR__ . DIRECTORY_SEPARATOR . 'xml',\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            false,\n        );\n    }\n\n    public function testXmlValidationEnabled(): void\n    {\n        $driver = new XmlDriver(\n            __DIR__ . DIRECTORY_SEPARATOR . 'invalid_xml',\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            true,\n        );\n\n        $class = new ClassMetadata(InvalidXml::class);\n        $class->initializeReflection(new RuntimeReflectionService());\n\n        self::expectException(MappingException::class);\n        self::expectExceptionMessage(\"libxml error: Element '{http://doctrine-project.org/schemas/orm/doctrine-mapping}field', attribute 'invalid': The attribute 'invalid' is not allowed.\");\n\n        $driver->loadMetadataForClass(InvalidXml::class, $class);\n    }\n\n    public function testXmlValidationDisabled(): void\n    {\n        $driver = new XmlDriver(\n            __DIR__ . DIRECTORY_SEPARATOR . 'invalid_xml',\n            XmlDriver::DEFAULT_FILE_EXTENSION,\n            false,\n        );\n\n        $class = new ClassMetadata(InvalidXml::class);\n        $class->initializeReflection(new RuntimeReflectionService());\n\n        $driver->loadMetadataForClass(InvalidXml::class, $class);\n\n        self::assertCount(1, $class->fieldMappings);\n    }\n}\n\nclass CTI\n{\n    /** @var int */\n    public $id;\n}\n\nclass CTIFoo extends CTI\n{\n}\nclass CTIBar extends CTI\n{\n}\nclass CTIBaz extends CTI\n{\n}\n\nclass XMLSLC\n{\n    /** @var mixed */\n    public $foo;\n}\n\nclass XMLSLCFoo\n{\n    /** @var int */\n    public $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/invalid_xml/Doctrine.Tests.Models.InvalidXml.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\InvalidXml\" table=\"invalid_xml\">\n        <field name=\"email\" type=\"string\" invalid=\"true\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Enums.Card.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\n\n$metadata->mapField(\n    [\n        'id' => true,\n        'fieldName' => 'id',\n        'type' => 'integer',\n    ],\n);\n$metadata->mapField(\n    [\n        'fieldName' => 'suit',\n        'type' => 'string',\n        'enumType' => Suit::class,\n    ],\n);\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n$metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE);\n$metadata->setPrimaryTable(\n    ['name' => 'cms_users_typed_with_custom_typed_field'],\n);\n\n$metadata->mapField(\n    [\n        'id' => true,\n        'fieldName' => 'id',\n    ],\n);\n\n$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n$metadata->mapField(\n    ['fieldName' => 'customId'],\n);\n\n$metadata->mapField(\n    ['fieldName' => 'customIntTypedField'],\n);\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Insertable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n$metadata->setPrimaryTable(\n    ['name' => 'insertable_column'],\n);\n\n$metadata->mapField(\n    [\n        'id' => true,\n        'fieldName' => 'id',\n    ],\n);\n$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n$metadata->mapField(\n    [\n        'fieldName' => 'nonInsertableContent',\n        'notInsertable' => true,\n        'options' => ['default' => '1234'],\n        'generated' => ClassMetadata::GENERATED_INSERT,\n    ],\n);\n$metadata->mapField(\n    ['fieldName' => 'insertableContent'],\n);\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Updatable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\n\n$metadata->setPrimaryTable(\n    ['name' => 'updatable_column'],\n);\n\n$metadata->mapField(\n    [\n        'id' => true,\n        'fieldName' => 'id',\n    ],\n);\n$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);\n\n$metadata->mapField(\n    [\n        'fieldName' => 'nonUpdatableContent',\n        'notUpdatable' => true,\n        'generated' => ClassMetadata::GENERATED_ALWAYS,\n    ],\n);\n$metadata->mapField(\n    ['fieldName' => 'updatableContent'],\n);\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\Tests\\Models\\GH10288\\GH10288People;\n\n$metadata->mapField(\n    [\n        'id'                 => true,\n        'fieldName'          => 'id',\n    ],\n);\n\n$metadata->setDiscriminatorColumn(\n    [\n        'name'     => 'discr',\n        'enumType' => GH10288People::class,\n    ],\n);\n\n$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/CatNoId.dcm.xml",
    "content": "<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n     xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                         https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n  <entity name=\"CatNoId\">\n    <field name=\"can_has_cheezburgers\" type=\"boolean\" />\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/DDC2429Book.orm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <mapped-superclass name=\"SocialLibrary\\ReadBundle\\Entity\\Book\">\n        <id name=\"id\" type=\"integer\">\n            <generator strategy=\"AUTO\" />\n        </id>\n        <field name=\"volume\" column=\"volume\" type=\"integer\" nullable=\"true\" />\n        <field name=\"language\" column=\"language\" type=\"string\" length=\"8\" nullable=\"true\" />\n        <field name=\"isbn10\" column=\"isbn10\" type=\"string\" length=\"11\" unique=\"true\" nullable=\"true\" />\n        <field name=\"isbn13\" column=\"isbn13\" type=\"string\" length=\"14\" unique=\"true\" nullable=\"true\" />\n        <many-to-one field=\"serie\" target-entity=\"SocialLibrary\\ReadBundle\\Entity\\Serie\" inversed-by=\"volumes\">\n            <join-column name=\"serie_id\" referenced-column-name=\"id\" />\n        </many-to-one>\n        <many-to-many field=\"owners\" target-entity=\"Application\\Sonata\\UserBundle\\Entity\\User\">\n            <join-table name=\"book__owner\">\n                <join-columns>\n                    <join-column name=\"object_id\" referenced-column-name=\"id\" />\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"owner_id\" referenced-column-name=\"id\" />\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n        <many-to-many field=\"creators\" target-entity=\"SocialLibrary\\BaseBundle\\Entity\\ObjectCreator\">\n            <join-table name=\"book__creator\">\n                <join-columns>\n                    <join-column name=\"object_id\" referenced-column-name=\"id\" />\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"creator_id\" referenced-column-name=\"id\" />\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n    </mapped-superclass>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/DDC2429Novel.orm.xml",
    "content": "<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n     xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                         https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"SocialLibrary\\ReadBundle\\Entity\\Novel\" table=\"novel__novel\" repository-class=\"SocialLibrary\\ReadBundle\\Repository\\NovelRepository\">\n        <association-overrides>\n            <association-override name=\"owners\">\n                <join-table name=\"novel__owner\">\n                    <join-columns>\n                        <join-column name=\"object_id\" />\n                    </join-columns>\n                    <inverse-join-columns>\n                        <join-column name=\"owner_id\" />\n                    </inverse-join-columns>\n                </join-table>\n            </association-override>\n            <association-override name=\"creators\">\n                <join-table name=\"novel__creator\">\n                    <join-columns>\n                        <join-column name=\"object_id\" />\n                    </join-columns>\n                    <inverse-join-columns>\n                        <join-column name=\"creator_id\" />\n                    </inverse-join-columns>\n                </join-table>\n            </association-override>\n            <association-override name=\"serie\">\n                <join-columns>\n                    <join-column name=\"novel_serie_id\" referenced-column-name=\"id\" />\n                </join-columns>\n            </association-override>\n        </association-overrides>\n\n        <attribute-overrides>\n            <attribute-override name=\"volume\">\n                <field column=\"novel__volume\" type=\"integer\" nullable=\"true\" />\n            </attribute-override>\n        </attribute-overrides>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml",
    "content": "<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\CMS\\CmsAddress\" table=\"cms_users\">\n\n        <entity-listeners>\n            <entity-listener class=\"CmsAddressListener\"/>\n        </entity-listeners>\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"country\" column=\"country\" type=\"string\" length=\"50\"/>\n        <field name=\"city\" column=\"city\" type=\"string\" length=\"50\"/>\n        <field name=\"zip\" column=\"zip\" type=\"string\" length=\"50\"/>\n\n        <one-to-one field=\"user\" target-entity=\"CmsUser\" inversed-by=\"address\">\n            <join-column referenced-column-name=\"id\" />\n        </one-to-one>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\CMS\\CmsUser\" table=\"cms_users\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"status\" column=\"status\" type=\"string\" length=\"50\" unique=\"true\"/>\n        <field name=\"username\" column=\"username\" type=\"string\" length=\"255\" unique=\"true\"/>\n        <field name=\"name\" column=\"name\" type=\"string\" length=\"255\"/>\n\n        <one-to-one field=\"address\" target-entity=\"CmsAddress\" inversed-by=\"user\" orphan-removal=\"true\">\n            <cascade>\n                <cascade-persist/>\n            </cascade>\n            <join-column name=\"address_id\" referenced-column-name=\"id\"  />\n        </one-to-one>\n\n        <one-to-one field=\"email\" target-entity=\"CmsEmail\" inversed-by=\"user\" orphan-removal=\"true\">\n            <cascade>\n                <cascade-persist/>\n            </cascade>\n            <join-column referenced-column-name=\"id\" nullable=\"true\" />\n        </one-to-one>\n\n        <one-to-many field=\"phonenumbers\" target-entity=\"CmsPhonenumber\" mapped-by=\"user\" orphan-removal=\"true\">\n            <cascade>\n                <cascade-persist/>\n            </cascade>\n        </one-to-many>\n\n        <one-to-many field=\"articles\" target-entity=\"CmsArticle\" mapped-by=\"user\">\n            <cascade>\n                <cascade-detach/>\n            </cascade>\n        </one-to-many>\n\n        <many-to-many field=\"groups\" target-entity=\"CmsGroup\">\n            <cascade>\n                <cascade-persist/>\n                <cascade-detach/>\n            </cascade>\n            <join-table name=\"cms_users_groups\">\n                <join-columns>\n                    <join-column name=\"user_id\" referenced-column-name=\"id\"/>\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"group_id\" referenced-column-name=\"id\"/>\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Cache.City.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n  <entity name=\"Doctrine\\Tests\\Models\\Cache\\City\" table=\"cache_city\">\n    <cache usage=\"READ_ONLY\" />\n    <id name=\"id\" type=\"integer\" column=\"id\">\n      <generator strategy=\"IDENTITY\"/>\n    </id>\n    <field name=\"name\" type=\"string\" column=\"name\" precision=\"0\" scale=\"0\" unique=\"1\" nullable=\"false\"/>\n    <one-to-many field=\"attractions\" target-entity=\"Doctrine\\Tests\\Models\\Cache\\Attraction\" mapped-by=\"city\">\n      <cache usage=\"READ_ONLY\" />\n      <order-by>\n        <order-by-field name=\"name\" direction=\"ASC\"/>\n      </order-by>\n    </one-to-many>\n    <many-to-one field=\"state\" target-entity=\"Doctrine\\Tests\\Models\\Cache\\State\" inversed-by=\"cities\">\n      <cache/>\n      <join-columns>\n        <join-column name=\"state_id\" referenced-column-name=\"id\" nullable=\"1\"/>\n      </join-columns>\n    </many-to-one>\n    <many-to-many field=\"travels\" target-entity=\"Doctrine\\Tests\\Models\\Cache\\Travel\" mapped-by=\"visitedCities\"/>\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyContract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Company\\CompanyContract\" table=\"company_contracts\" inheritance-type=\"SINGLE_TABLE\">\n\n        <discriminator-map>\n            <discriminator-mapping value=\"fix\"          class=\"CompanyFixContract\" />\n            <discriminator-mapping value=\"flexible\"     class=\"CompanyFlexContract\" />\n            <discriminator-mapping value=\"flexultra\"    class=\"CompanyFlexUltraContract\" />\n        </discriminator-map>\n\n        <entity-listeners>\n            <entity-listener class=\"CompanyContractListener\">\n                <lifecycle-callback type=\"preFlush\"      method=\"preFlushHandler\"/>\n                <lifecycle-callback type=\"postLoad\"      method=\"postLoadHandler\"/>\n                \n                <lifecycle-callback type=\"postPersist\"   method=\"postPersistHandler\"/>\n                <lifecycle-callback type=\"prePersist\"    method=\"prePersistHandler\"/>\n\n                <lifecycle-callback type=\"postUpdate\"    method=\"postUpdateHandler\"/>\n                <lifecycle-callback type=\"preUpdate\"     method=\"preUpdateHandler\"/>\n\n                <lifecycle-callback type=\"postRemove\"    method=\"postRemoveHandler\"/>\n                <lifecycle-callback type=\"preRemove\"     method=\"preRemoveHandler\"/>\n            </entity-listener>\n        </entity-listeners>\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n        \n        <field name=\"completed\" column=\"completed\" type=\"boolean\"/>\n\n        <!-- Other mappings -->\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Company\\CompanyFixContract\">\n\n        \n        <field name=\"fixPrice\" column=\"fixPrice\" type=\"integer\"/>\n\n        <!-- Other mappings -->\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Company\\CompanyFlexContract\">\n\n        <field name=\"hoursWorked\" column=\"hoursWorked\" type=\"integer\"/>\n        <field name=\"pricePerHour\" column=\"pricePerHour\" type=\"integer\"/>\n\n        <!-- Other mappings -->\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract\">\n\n        <entity-listeners>\n            <entity-listener class=\"CompanyContractListener\">\n                <lifecycle-callback type=\"preFlush\"      method=\"preFlushHandler\"/>\n                <lifecycle-callback type=\"postLoad\"      method=\"postLoadHandler\"/>\n\n                <lifecycle-callback type=\"postPersist\"   method=\"postPersistHandler\"/>\n                <lifecycle-callback type=\"prePersist\"    method=\"prePersistHandler\"/>\n\n                <lifecycle-callback type=\"postUpdate\"    method=\"postUpdateHandler\"/>\n                <lifecycle-callback type=\"preUpdate\"     method=\"preUpdateHandler\"/>\n\n                <lifecycle-callback type=\"postRemove\"    method=\"postRemoveHandler\"/>\n                <lifecycle-callback type=\"preRemove\"     method=\"preRemoveHandler\"/>\n            </entity-listener>\n\n            <entity-listener class=\"CompanyFlexUltraContractListener\">\n                <lifecycle-callback type=\"prePersist\" method=\"prePersistHandler1\"/>\n                <lifecycle-callback type=\"prePersist\" method=\"prePersistHandler2\"/>\n            </entity-listener>\n        </entity-listeners>\n\n        <field name=\"maxPrice\" column=\"maxPrice\" type=\"integer\"/>\n\n        <!-- Other mappings -->\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Company\\CompanyPerson\" table=\"company_persons\" inheritance-type=\"JOINED\">\n\n        <discriminator-map >\n            <discriminator-mapping value=\"person\"   class=\"CompanyPerson\" />\n            <discriminator-mapping value=\"manager\"  class=\"CompanyManager\" />\n            <discriminator-mapping value=\"employee\" class=\"CompanyEmployee\" />\n        </discriminator-map>\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"name\" column=\"name\"/>\n\n        <one-to-one field=\"spouse\" target-entity=\"CompanyPerson\">\n            <join-column name=\"spouse_id\" referenced-column-name=\"id\"/>\n        </one-to-one>\n\n        <many-to-many field=\"friends\" target-entity=\"CompanyPerson\">\n            <join-table name=\"company_persons_friends\">\n                <join-columns>\n                    <join-column name=\"person_id\" referenced-column-name=\"id\"/>\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"friend_id\" referenced-column-name=\"id\"/>\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Customer.CustomerType.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\Models\\Customer\\CustomerType\" table=\"customers\" inheritance-type=\"SINGLE_TABLE\">\n        <field name=\"name\" column=\"name\"/>\n        <discriminator-column name=\"type\" type=\"string\"/>\n        <discriminator-map>\n            <discriminator-mapping value=\"Doctrine\\Tests\\Models\\Customer\\InternalCustomer\" class=\"Doctrine\\Tests\\Models\\Customer\\InternalCustomer\" />\n            <discriminator-mapping value=\"Doctrine\\Tests\\Models\\Customer\\ExternalCustomer\" class=\"Doctrine\\Tests\\Models\\Customer\\ExternalCustomer\" />\n        </discriminator-map>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC117.DDC117Translation.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC117\\DDC117Translation\">\n\n        <id name=\"article\" association-key=\"true\" />\n        <id name=\"language\" type=\"string\" />\n\n        <field name=\"title\" type=\"string\" />\n\n        <one-to-many field=\"lastTranslatedBy\" target-entity=\"DDC117Editor\" mapped-by=\"lastTranslation\" />\n\n        <many-to-one field=\"article\" target-entity=\"DDC117Article\">\n            <join-column name=\"article_id\" referenced-column-name=\"article_id\" />\n        </many-to-one>\n\n        <many-to-many field=\"reviewedByEditors\" target-entity=\"DDC117Editor\" mapped-by=\"reviewingTranslations\" />\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC1476\\DDC1476EntityWithDefaultFieldType\">\n        <id name=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n        \n        <field name=\"name\"/>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping\n        xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                            https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\"\n>\n    <entity name=\"Doctrine\\Tests\\Models\\DDC2825\\ExplicitSchemaAndTable\" table=\"explicit_table\" schema=\"explicit_schema\">\n        <id name=\"id\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping\n        xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                            https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\"\n>\n    <entity name=\"Doctrine\\Tests\\Models\\DDC2825\\SchemaAndTableInTableName\" table=\"implicit_schema.implicit_table\">\n        <id name=\"id\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC3293.DDC3293Address.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping\n        xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                            https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <embeddable name=\"Doctrine\\Tests\\Models\\DDC3293\\DDC3293Address\">\n        <field name=\"street\" type=\"string\" />\n        <field name=\"city\" type=\"string\" />\n        <field name=\"country\" type=\"string\" />\n    </embeddable>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC3293.DDC3293User.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping\n        xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                            https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\Models\\DDC3293\\DDC3293User\" table=\"user\">\n        <id name=\"id\" column=\"id\">\n            <generator strategy=\"AUTO\" />\n        </id>\n        <embedded\n            name=\"address\"\n            class=\"Doctrine\\Tests\\Models\\DDC3293\\DDC3293Address\"\n            column-prefix=\"__prefix__\"\n            use-column-prefix=\"false\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC3293.DDC3293UserPrefixed.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping\n        xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                            https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\Models\\DDC3293\\DDC3293UserPrefixed\" table=\"user\">\n        <id name=\"id\" column=\"id\">\n            <generator strategy=\"AUTO\" />\n        </id>\n        <embedded\n            name=\"address\"\n            class=\"Doctrine\\Tests\\Models\\DDC3293\\DDC3293Address\"\n            column-prefix=\"__prefix__\"\n            use-column-prefix=\"true\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC3579.DDC3579Admin.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC3579\\DDC3579Admin\">\n        <association-overrides>\n            <association-override name=\"groups\">\n                <inversed-by name=\"admins\" />\n            </association-override>\n        </association-overrides>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC3579.DDC3579User.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <mapped-superclass name=\"Doctrine\\Tests\\Models\\DDC3579\\DDC3579User\">\n        <id name=\"id\" type=\"integer\" column=\"user_id\" length=\"150\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"name\" column=\"user_name\" type=\"string\" length=\"250\" nullable=\"true\" unique=\"false\" />\n\n        <many-to-many field=\"groups\" target-entity=\"DDC3579Group\" />\n    </mapped-superclass>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <mapped-superclass name=\"Doctrine\\Tests\\Models\\DDC5934\\DDC5934BaseContract\">\n        <id name=\"id\" type=\"integer\">\n            <generator strategy=\"AUTO\" />\n        </id>\n\n        <many-to-many target-entity=\"DDC5934Member\" inversed-by=\"contract\" fetch=\"LAZY\" field=\"members\" />\n    </mapped-superclass>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC5934.DDC5934Contract.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                        https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC5934\\DDC5934Contract\">\n        <association-overrides>\n            <association-override name=\"members\" fetch=\"EXTRA_LAZY\" />\n        </association-overrides>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n         \n    <entity name=\"Doctrine\\Tests\\Models\\DDC869\\DDC869ChequePayment\">\n        <field name=\"serialNumber\" column=\"serialNumber\" type=\"string\"/>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC869\\DDC869CreditCardPayment\">\n        <field name=\"creditCardNumber\" column=\"creditCardNumber\" type=\"string\"/>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC869.DDC869Payment.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n                              \n    <mapped-superclass name=\"Doctrine\\Tests\\Models\\DDC869\\DDC869Payment\" repository-class=\"Doctrine\\Tests\\Models\\DDC869\\DDC869PaymentRepository\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n        \n        <field name=\"value\" column=\"value\" type=\"float\"/>\n    </mapped-superclass>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC889.DDC889Class.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <class name=\"Doctrine\\Tests\\Models\\DDC889\\DDC889Class\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n    </class>\n\n</doctrine-mapping>"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC889.DDC889Entity.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC889\\DDC889Entity\">\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC889.DDC889SuperClass.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <mapped-superclass name=\"Doctrine\\Tests\\Models\\DDC889\\DDC889SuperClass\">\n        <field name=\"name\" column=\"name\" type=\"string\"/>\n    </mapped-superclass>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC964\\DDC964Admin\">\n        <association-overrides>\n            <association-override name=\"groups\">\n                <join-table name=\"ddc964_users_admingroups\">\n                    <join-columns>\n                        <join-column name=\"adminuser_id\"/>\n                    </join-columns>\n                    <inverse-join-columns>\n                        <join-column name=\"admingroup_id\"/>\n                    </inverse-join-columns>\n                </join-table>\n            </association-override>\n            <association-override name=\"address\">\n                <join-columns>\n                    <join-column name=\"adminaddress_id\" referenced-column-name=\"id\"/>\n                </join-columns>\n            </association-override>\n        </association-overrides>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\DDC964\\DDC964Guest\">\n        <attribute-overrides>\n            <attribute-override name=\"id\">\n                <field column=\"guest_id\" length=\"140\"/>\n            </attribute-override>\n            <attribute-override name=\"name\">\n                <field column=\"guest_name\" type=\"string\" length=\"240\" nullable=\"false\" unique=\"true\" />\n            </attribute-override>\n        </attribute-overrides>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <mapped-superclass name=\"Doctrine\\Tests\\Models\\DDC964\\DDC964User\">\n        <id name=\"id\" type=\"integer\" column=\"user_id\" length=\"150\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n        \n        <field name=\"name\" column=\"user_name\" type=\"string\" length=\"250\" nullable=\"true\" unique=\"false\" />\n\n        <many-to-one field=\"address\" target-entity=\"DDC964Address\">\n            <cascade>\n                <cascade-persist/>\n            </cascade>\n            <join-column name=\"address_id\" referenced-column-name=\"id\"/>\n        </many-to-one>\n\n        <many-to-many field=\"groups\" target-entity=\"DDC964Group\" inversed-by=\"users\">\n            <cascade>\n                <cascade-persist/>\n                <cascade-detach/>\n            </cascade>\n            <join-table name=\"ddc964_users_groups\">\n                <join-columns>\n                    <join-column name=\"user_id\" referenced-column-name=\"id\" />\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"group_id\" referenced-column-name=\"id\" />\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n    </mapped-superclass>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Enums.Card.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Enums\\Card\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"suit\" type=\"string\" enum-type=\"Doctrine\\Tests\\Models\\Enums\\Suit\" />\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.GH7141.GH7141Article.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\GH7141\\GH7141Article\">\n        <one-to-many field=\"tags\" target-entity=\"NoTargetEntity\" mapped-by=\"noMappedByField\">\n            <order-by>\n                <order-by-field name=\"position\"/>\n            </order-by>\n        </one-to-many>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.GH7316.GH7316Article.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                         http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\GH7316\\GH7316Article\">\n        <many-to-many field=\"tags\" target-entity=\"NoTargetEntity\" mapped-by=\"noMappedByField\">\n            <order-by>\n                <order-by-field name=\"position\"/>\n            </order-by>\n        </many-to-many>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Generic.BooleanModel.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"\\stdClass\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"booleanField\" column=\"boolean_field\" type=\"boolean\"/>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.Project.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\Models\\Project\\Project\" table=\"project\">\n        <id name=\"id\" type=\"Doctrine\\Tests\\Models\\Project\\ProjectId\" column=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n        <field name=\"name\" type=\"Doctrine\\Tests\\Models\\Project\\ProjectName\" column=\"name\"/>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.ProjectInvalidMapping.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\Models\\Project\\ProjectInvalidMapping\" table=\"project\">\n        <id name=\"id\" type=\"Doctrine/Tests/Models/Project/Project/ProjectId\" column=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n        <field name=\"name\" type=\"Doctrine/Tests/Models/Project/Project/ProjectName\" column=\"name\"/>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.TypedProperties.UserTyped.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n  <entity name=\"Doctrine\\Tests\\Models\\TypedProperties\\UserTyped\" table=\"cms_users_typed\">\n    <id name=\"id\">\n      <generator strategy=\"AUTO\"/>\n    </id>\n\n    <field name=\"status\" length=\"50\"/>\n    <field name=\"username\" length=\"255\" unique=\"true\"/>\n    <field name=\"dateInterval\"/>\n    <field name=\"dateTime\"/>\n    <field name=\"dateTimeImmutable\"/>\n    <field name=\"array\"/>\n    <field name=\"boolean\"/>\n    <field name=\"float\"/>\n\n    <one-to-one field=\"email\" orphan-removal=\"true\">\n      <cascade><cascade-persist /></cascade>\n      <join-column/>\n    </one-to-one>\n\n    <many-to-one field=\"mainEmail\"/>\n\n    <embedded name=\"contact\" />\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n  <entity name=\"Doctrine\\Tests\\Models\\TypedProperties\\UserTypedWithCustomTypedField\" table=\"cms_users_typed_with_custom_typed_field\">\n    <id name=\"id\">\n      <generator strategy=\"AUTO\"/>\n    </id>\n\n    <field name=\"customId\"/>\n    <field name=\"customIntTypedField\"/>\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Upsertable.Insertable.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Upsertable\\Insertable\" table=\"insertable_column\">\n        <id name=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"nonInsertableContent\" insertable=\"false\" type=\"string\" generated=\"INSERT\" />\n        <field name=\"insertableContent\" insertable=\"true\" type=\"string\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Upsertable.Updatable.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\Models\\Upsertable\\Updatable\" table=\"updatable_column\">\n        <id name=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"nonUpdatableContent\" updatable=\"false\" type=\"string\" generated=\"ALWAYS\" />\n        <field name=\"updatableContent\" updatable=\"true\" type=\"string\" />\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.ValueObjects.Name.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n  <embeddable name=\"Doctrine\\Tests\\Models\\ValueObjects\\Name\">\n    <field name=\"firstName\"/>\n    <field name=\"lastName\"/>\n  </embeddable>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.ValueObjects.Person.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                                      https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n  <entity name=\"Doctrine\\Tests\\Models\\ValueObjects\\Person\">\n    <id name=\"id\" type=\"integer\" column=\"id\">\n      <generator strategy=\"AUTO\"/>\n    </id>\n    <embedded name=\"name\" class=\"Doctrine\\Tests\\Models\\ValueObjects\\Name\" column-prefix=\"nm_\"/>\n  </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Functional.XmlLegacyTimeEntity.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Functional\\XmlLegacyTimeEntity\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"createdAt\" type=\"datetime\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">CURRENT_TIMESTAMP</option>\n            </options>\n        </field>\n\n        <field name=\"createdAtImmutable\" type=\"datetime_immutable\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">CURRENT_TIMESTAMP</option>\n            </options>\n        </field>\n\n        <field name=\"createdTime\" type=\"time\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">CURRENT_TIME</option>\n            </options>\n        </field>\n        <field name=\"createdDate\" type=\"date\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">CURRENT_DATE</option>\n            </options>\n        </field>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Functional.XmlTimeEntity.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Functional\\XmlTimeEntity\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n\n        <field name=\"createdAt\" type=\"datetime\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">\n                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n                </option>\n            </options>\n        </field>\n\n        <field name=\"createdAtImmutable\" type=\"datetime_immutable\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">\n                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTimestamp\"/>\n                </option>\n            </options>\n        </field>\n\n        <field name=\"createdTime\" type=\"time\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">\n                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentTime\"/>\n                </option>\n            </options>\n        </field>\n        <field name=\"createdDate\" type=\"date\" insertable=\"false\" updatable=\"false\">\n            <options>\n                <option name=\"default\">\n                    <object class=\"Doctrine\\DBAL\\Schema\\DefaultExpression\\CurrentDate\"/>\n                </option>\n            </options>\n        </field>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\Animal\" inheritance-type=\"SINGLE_TABLE\">\n        <discriminator-column name=\"discr\" type=\"string\" length=\"32\" />\n        <discriminator-map>\n            <discriminator-mapping value=\"cat\" class=\"Cat\" />\n            <discriminator-mapping value=\"dog\" class=\"Dog\" />\n        </discriminator-map>\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"CUSTOM\" />\n            <custom-id-generator class=\"stdClass\" />\n        </id>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.BlogPost.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\BlogPost\">\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.BlogPostComment.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\BlogPostComment\">\n\n        <id name=\"id\" type=\"integer\">\n            <generator strategy=\"NONE\"/>\n        </id>\n\n        <many-to-one field=\"blogPost\" target-entity=\"Doctrine\\Tests\\ORM\\Mapping\\BlogPost\">\n            <join-columns>\n                <join-column nullable=\"false\"/>\n            </join-columns>\n        </many-to-one>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\CTI\" inheritance-type=\"JOINED\">\n        <discriminator-column name=\"discr\" type=\"string\" length=\"60\"/>\n        <discriminator-map>\n            <discriminator-mapping value=\"foo\" class=\"CTIFoo\"/>\n            <discriminator-mapping value=\"bar\" class=\"CTIBar\"/>\n            <discriminator-mapping value=\"baz\" class=\"CTIBaz\"/>\n        </discriminator-map>\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n        </id>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Comment.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\Comment\">\n\n        <indexes>\n            <index columns=\"content\" flags=\"fulltext\">\n                <options>\n                    <option name=\"where\">content IS NOT NULL</option>\n                </options>\n            </index>\n        </indexes>\n\n        <field name=\"content\" type=\"text\"/>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC1170Entity.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\DDC1170Entity\">\n        <id name=\"id\" column-definition=\"INT unsigned NOT NULL\">\n            <generator strategy=\"NONE\"/>\n        </id>\n        \n        <field name=\"value\" column-definition=\"VARCHAR(255) NOT NULL\"/>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\DDC807Entity\" inheritance-type=\"SINGLE_TABLE\">\n        <discriminator-column name=\"dtype\" column-definition=\"ENUM('ONE','TWO')\"/>\n        \n        <discriminator-map>\n            <discriminator-mapping value=\"ONE\" class=\"DDC807SubClasse1\" />\n            <discriminator-mapping value=\"TWO\" class=\"DDC807SubClasse1\" />\n        </discriminator-map>\n\n        <id name=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\GH10288EnumTypePerson\" inheritance-type=\"SINGLE_TABLE\">\n        <discriminator-column name=\"discr\" enum-type=\"Doctrine\\Tests\\Models\\GH10288\\GH10288People\"/>\n        \n        <discriminator-map>\n            <discriminator-mapping value=\"boss\" class=\"Doctrine\\Tests\\ORM\\Mapping\\GH10288EnumTypeBoss\" />\n        </discriminator-map>\n\n        <id name=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n  <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\ReservedWordInTableColumn\">\n\n    <id name=\"id\" type=\"integer\" column=\"id\">\n      <generator strategy=\"NONE\"/>\n    </id>\n\n    <field name=\"count\" column=\"`count`\" type=\"integer\"/>\n\n  </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.SingleTableEntityIncompleteDiscriminatorColumnMapping.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\SingleTableEntityIncompleteDiscriminatorColumnMapping\" inheritance-type=\"SINGLE_TABLE\">\n        <discriminator-column name=\"dtype\" />\n\n        <discriminator-map>\n            <discriminator-mapping value=\"ONE\" class=\"SingleTableEntityIncompleteDiscriminatorColumnMappingSub1\" />\n            <discriminator-mapping value=\"TWO\" class=\"SingleTableEntityIncompleteDiscriminatorColumnMappingSub2\" />\n        </discriminator-map>\n\n        <id name=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.SingleTableEntityNoDiscriminatorColumnMapping.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\SingleTableEntityNoDiscriminatorColumnMapping\" inheritance-type=\"SINGLE_TABLE\">\n        <discriminator-map>\n            <discriminator-mapping value=\"ONE\" class=\"SingleTableEntityNoDiscriminatorColumnMappingSub1\" />\n            <discriminator-mapping value=\"TWO\" class=\"SingleTableEntityNoDiscriminatorColumnMappingSub2\" />\n        </discriminator-map>\n\n        <id name=\"id\">\n            <generator strategy=\"NONE\"/>\n        </id>\n    </entity>\n        \n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\User\" table=\"cms_users\">\n        <options>\n            <option name=\"foo\">bar</option>\n            <option name=\"baz\">\n                <option name=\"key\">val</option>\n            </option>\n        </options>\n\n        <indexes>\n            <index name=\"name_idx\" columns=\"name\"/>\n            <index columns=\"user_email\"/>\n            <index name=\"fields\" columns=\"\" fields=\"name,email\"/>\n        </indexes>\n\n        <unique-constraints>\n            <unique-constraint columns=\"name,user_email\" name=\"search_idx\">\n                <options>\n                    <option name=\"where\">name IS NOT NULL</option>\n                </options>\n            </unique-constraint>\n            <unique-constraint columns=\"\" fields=\"name,phone\" name=\"phone_idx\"/>\n        </unique-constraints>\n\n        <lifecycle-callbacks>\n            <lifecycle-callback type=\"prePersist\" method=\"doStuffOnPrePersist\"/>\n            <lifecycle-callback type=\"prePersist\" method=\"doOtherStuffOnPrePersistToo\"/>\n            <lifecycle-callback type=\"postPersist\" method=\"doStuffOnPostPersist\"/>\n        </lifecycle-callbacks>\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n            <sequence-generator sequence-name=\"tablename_seq\" allocation-size=\"100\" initial-value=\"1\" />\n            <options>\n                <option name=\"foo\">bar</option>\n                <option name=\"unsigned\">false</option>\n            </options>\n        </id>\n\n        <field name=\"name\" column=\"name\" type=\"string\" length=\"50\" nullable=\"true\" unique=\"true\">\n            <options>\n                <option name=\"foo\">bar</option>\n                <option name=\"baz\">\n                    <option name=\"key\">val</option>\n                </option>\n                <option name=\"fixed\">false</option>\n            </options>\n        </field>\n        <field name=\"email\" column=\"user_email\" type=\"string\" column-definition=\"CHAR(32) NOT NULL\" />\n\n        <field name=\"version\" type=\"integer\" version=\"true\" />\n\n        <field name=\"indexed\" type=\"string\" index=\"true\" />\n\n        <one-to-one field=\"address\" target-entity=\"Address\" inversed-by=\"user\">\n            <cascade><cascade-remove /></cascade>\n            <join-column name=\"address_id\" referenced-column-name=\"id\" on-delete=\"CASCADE\" />\n        </one-to-one>\n\n        <one-to-many field=\"phonenumbers\" target-entity=\"Phonenumber\" mapped-by=\"user\" index-by=\"number\" orphan-removal=\"true\">\n            <cascade>\n                <cascade-persist/>\n            </cascade>\n            <order-by>\n                <order-by-field name=\"number\" direction=\"ASC\" />\n            </order-by>\n        </one-to-many>\n\n        <many-to-many field=\"groups\" target-entity=\"Group\">\n            <cascade>\n                <cascade-all/>\n            </cascade>\n            <join-table name=\"cms_users_groups\">\n                <join-columns>\n                    <join-column name=\"user_id\" referenced-column-name=\"id\" unique=\"false\" />\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"group_id\" referenced-column-name=\"id\" column-definition=\"INT NULL\" />\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectAttributes.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\UserIncorrectAttributes\" table=\"cms_users\">\n        <unique-constraints>\n            <unique-constraint columns=\"name,user_email\" name=\"search_idx\">\n                <options>\n                    <option name=\"where\">name IS NOT NULL</option>\n                </options>\n            </unique-constraint>\n            <unique-constraint columns=\"\" fields=\"name,phone\" name=\"phone_idx\"/>\n        </unique-constraints>\n\n        <lifecycle-callbacks>\n            <lifecycle-callback type=\"prePersist\" method=\"doStuffOnPrePersist\"/>\n            <lifecycle-callback type=\"prePersist\" method=\"doOtherStuffOnPrePersistToo\"/>\n            <lifecycle-callback type=\"postPersist\" method=\"doStuffOnPostPersist\"/>\n        </lifecycle-callbacks>\n\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n            <sequence-generator sequence-name=\"tablename_seq\" allocation-size=\"100\" initial-value=\"1\" />\n            <options>\n                <option name=\"foo\">bar</option>\n                <option name=\"unsigned\">false</option>\n            </options>\n        </id>\n\n        <field field=\"name\" column=\"name\" type=\"string\" length=\"50\" nullable=\"true\" unique=\"true\">\n            <options>\n                <option name=\"foo\">bar</option>\n                <option name=\"baz\">\n                    <option name=\"key\">val</option>\n                </option>\n                <option name=\"fixed\">false</option>\n            </options>\n        </field>\n        <field field=\"email\" column=\"user_email\" type=\"string\" column-definition=\"CHAR(32) NOT NULL\" />\n\n        <field name=\"version\" type=\"integer\" version=\"true\" />\n\n        <many-to-many fieldName=\"groups\" target-entity=\"Group\">\n            <cascade>\n                <cascade-all/>\n            </cascade>\n            <join-table name=\"cms_users_groups\">\n                <join-columns>\n                    <join-column name=\"user_id\" referenced-column-name=\"id\" nullable=\"false\" unique=\"false\" />\n                </join-columns>\n                <inverse-join-columns>\n                    <join-column name=\"group_id\" referenced-column-name=\"id\" column-definition=\"INT NULL\" />\n                </inverse-join-columns>\n            </join-table>\n        </many-to-many>\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectIndex.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n  <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\UserIncorrectIndex\" table=\"cms_users\">\n    <indexes>\n      <index name=\"name_idx\" columns=\"name\" fields=\"email\"/>\n    </indexes>\n\n    <id name=\"id\" type=\"integer\" column=\"id\">\n      <generator strategy=\"AUTO\"/>\n    </id>\n\n    <field name=\"name\" column=\"name\" type=\"string\">\n    </field>\n    <field name=\"email\" column=\"user_email\" type=\"string\"/>\n\n  </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectUniqueConstraint.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n  <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\UserIncorrectUniqueConstraint\" table=\"cms_users\">\n    <unique-constraints>\n      <unique-constraint name=\"name_idx\" columns=\"name\" fields=\"email\"/>\n    </unique-constraints>\n\n    <id name=\"id\" type=\"integer\" column=\"id\">\n      <generator strategy=\"AUTO\"/>\n    </id>\n\n    <field name=\"name\" column=\"name\" type=\"string\">\n    </field>\n    <field name=\"email\" column=\"user_email\" type=\"string\"/>\n\n  </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserMissingAttributes.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n                  xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\UserMissingAttributes\" table=\"cms_users\">\n        <id name=\"id\" type=\"integer\" column=\"id\">\n            <generator strategy=\"AUTO\"/>\n            <sequence-generator sequence-name=\"tablename_seq\" allocation-size=\"100\" initial-value=\"1\" />\n            <options>\n                <option name=\"foo\">bar</option>\n                <option name=\"unsigned\">false</option>\n            </options>\n        </id>\n\n        <field name=\"email\" column=\"user_email\" type=\"string\" column-definition=\"CHAR(32) NOT NULL\" />\n        <field />\n    </entity>\n\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.XMLSLC.dcm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doctrine-mapping xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\n                          https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd\">\n    <entity name=\"Doctrine\\Tests\\ORM\\Mapping\\XMLSLC\">\n        <cache usage=\"NONSTRICT_READ_WRITE\" />\n        <id name=\"foo\" association-key=\"true\"/>\n        <many-to-one field=\"foo\" target-entity=\"Doctrine\\Tests\\ORM\\Mapping\\XMLSLCFoo\">\n            <join-column name=\"foo_id\" referenced-column-name=\"id\" />\n        </many-to-one>\n    </entity>\n</doctrine-mapping>\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.dcm.yml",
    "content": "Doctrine\\Tests\\Models\\TypedProperties\\UserTypedWithCustomTypedField:\n    type: entity\n    table: cms_users_typed_with_custom_typed_field\n    id:\n        id:\n            generator:\n                strategy: AUTO\n    fields:\n        customId: ~\n        customIntTypedField: ~\n"
  },
  {
    "path": "tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.dcm.yml",
    "content": "Doctrine\\Tests\\ORM\\Mapping\\GH10288EnumTypePerson:\n  type: entity\n  inheritanceType: SINGLE_TABLE\n  discriminatorMap:\n    boss: Doctrine\\Tests\\ORM\\Mapping\\GH10288EnumTypeBoss\n  discriminatorColumn:\n    name: discr\n    enumType: Doctrine\\Tests\\Models\\GH10288\\GH10288People\n  id:\n    id: \n      generator:\n        strategy: NONE\n"
  },
  {
    "path": "tests/Tests/ORM/ORMInvalidArgumentExceptionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse stdClass;\n\nuse function spl_object_id;\n\n#[CoversClass(ORMInvalidArgumentException::class)]\nclass ORMInvalidArgumentExceptionTest extends TestCase\n{\n    /** @phpstan-return list<array{mixed, string}> */\n    public static function invalidEntityNames(): array\n    {\n        return [\n            [null, 'Entity name must be a string, null given'],\n            [true, 'Entity name must be a string, bool given'],\n            [123, 'Entity name must be a string, int given'],\n            [123.45, 'Entity name must be a string, float given'],\n            [new stdClass(), 'Entity name must be a string, stdClass given'],\n        ];\n    }\n\n    #[DataProvider('newEntitiesFoundThroughRelationshipsErrorMessages')]\n    public function testNewEntitiesFoundThroughRelationships(array $newEntities, string $expectedMessage): void\n    {\n        $exception = ORMInvalidArgumentException::newEntitiesFoundThroughRelationships($newEntities);\n\n        self::assertInstanceOf(ORMInvalidArgumentException::class, $exception);\n        self::assertSame($expectedMessage, $exception->getMessage());\n    }\n\n    public static function newEntitiesFoundThroughRelationshipsErrorMessages(): array\n    {\n        $entity1      = new stdClass();\n        $entity2      = new stdClass();\n        $entity3      = new class {\n            public function __toString(): string\n            {\n                return 'ThisIsAStringRepresentationOfEntity3';\n            }\n        };\n        $association1 = OneToManyAssociationMapping::fromMappingArray([\n            'sourceEntity' => 'foo1',\n            'fieldName'    => 'bar1',\n            'targetEntity' => 'baz1',\n        ]);\n        $association2 = OneToManyAssociationMapping::fromMappingArray([\n            'sourceEntity' => 'foo2',\n            'fieldName'    => 'bar2',\n            'targetEntity' => 'baz2',\n        ]);\n        $association3 = OneToManyAssociationMapping::fromMappingArray([\n            'sourceEntity' => 'foo3',\n            'fieldName'    => 'bar3',\n            'targetEntity' => 'baz3',\n        ]);\n\n        return [\n            'one entity found' => [\n                [\n                    [\n                        $association1,\n                        $entity1,\n                    ],\n                ],\n                'A new entity was found through the relationship \\'foo1#bar1\\' that was not configured to cascade '\n                . 'persist operations for entity: stdClass@' . spl_object_id($entity1)\n                . '. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity '\n                . 'or configure cascade persist this association in the mapping for example '\n                . '#[ORM\\ManyToOne(..., cascade: [\\'persist\\'])]. If you cannot find out which entity causes the problem '\n                . 'implement \\'baz1#__toString()\\' to get a clue.',\n            ],\n            'two entities found' => [\n                [\n                    [\n                        $association1,\n                        $entity1,\n                    ],\n                    [\n                        $association2,\n                        $entity2,\n                    ],\n                ],\n                'Multiple non-persisted new entities were found through the given association graph:' . \"\\n\\n\"\n                . ' * A new entity was found through the relationship \\'foo1#bar1\\' that was not configured to '\n                . 'cascade persist operations for entity: stdClass@' . spl_object_id($entity1) . '. '\n                . 'To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity '\n                . 'or configure cascade persist this association in the mapping for example '\n                . '#[ORM\\ManyToOne(..., cascade: [\\'persist\\'])]. If you cannot find out which entity causes the problem '\n                . 'implement \\'baz1#__toString()\\' to get a clue.' . \"\\n\"\n                . ' * A new entity was found through the relationship \\'foo2#bar2\\' that was not configured to '\n                . 'cascade persist operations for entity: stdClass@' . spl_object_id($entity2) . '. To solve '\n                . 'this issue: Either explicitly call EntityManager#persist() on this unknown entity or '\n                . 'configure cascade persist this association in the mapping for example '\n                . '#[ORM\\ManyToOne(..., cascade: [\\'persist\\'])]. If you cannot find out which entity causes the problem '\n                . 'implement \\'baz2#__toString()\\' to get a clue.',\n            ],\n            'two entities found, one is stringable' => [\n                [\n                    [\n                        $association3,\n                        $entity3,\n                    ],\n                ],\n                'A new entity was found through the relationship \\'foo3#bar3\\' that was not configured to cascade '\n                . 'persist operations for entity: ThisIsAStringRepresentationOfEntity3'\n                . '. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity '\n                . 'or configure cascade persist this association in the mapping for example '\n                . '#[ORM\\ManyToOne(..., cascade: [\\'persist\\'])].',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/ORMSetupTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\Mapping as MappingNamespace;\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\nuse Doctrine\\ORM\\Mapping\\Driver\\XmlDriver;\nuse Doctrine\\ORM\\ORMSetup;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhpExtension;\nuse PHPUnit\\Framework\\Attributes\\RequiresSetting;\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionProperty;\nuse Symfony\\Component\\Cache\\Adapter\\AbstractAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\ApcuAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\nuse Symfony\\Component\\Cache\\Adapter\\NullAdapter;\n\nuse function sys_get_temp_dir;\n\nuse const PHP_VERSION_ID;\n\nclass ORMSetupTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    #[IgnoreDeprecations]\n    public function testAttributeConfiguration(): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n        }\n\n        $config = ORMSetup::createAttributeMetadataConfiguration([], true);\n\n        self::assertInstanceOf(Configuration::class, $config);\n        self::assertEquals(sys_get_temp_dir(), $config->getProxyDir());\n        self::assertEquals('DoctrineProxies', $config->getProxyNamespace());\n        self::assertInstanceOf(AttributeDriver::class, $config->getMetadataDriverImpl());\n    }\n\n    public function testAttributeConfig(): void\n    {\n        $config = ORMSetup::createAttributeMetadataConfig([], true);\n\n        self::assertInstanceOf(Configuration::class, $config);\n        self::assertInstanceOf(AttributeDriver::class, $config->getMetadataDriverImpl());\n    }\n\n    #[IgnoreDeprecations]\n    public function testXMLConfiguration(): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n        }\n\n        $config = ORMSetup::createXMLMetadataConfiguration([], true);\n\n        self::assertInstanceOf(Configuration::class, $config);\n        self::assertInstanceOf(XmlDriver::class, $config->getMetadataDriverImpl());\n    }\n\n    public function testDisablingXmlValidationIsPossible(): void\n    {\n        $this->expectNotToPerformAssertions();\n\n        ORMSetup::createXMLMetadataConfig(paths: [], cache: new NullAdapter(), isXsdValidationEnabled: false);\n    }\n\n    #[RequiresPhpExtension('apcu')]\n    #[RequiresSetting('apc.enable_cli', '1')]\n    #[RequiresSetting('apc.enabled', '1')]\n    public function testCacheNamespaceShouldBeGeneratedForApcuWhenUsingLegacyConstructor(): void\n    {\n        if (PHP_VERSION_ID >= 80400) {\n            $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n        }\n\n        $config = ORMSetup::createConfiguration(false, '/foo');\n        $cache  = $config->getMetadataCache();\n\n        $namespaceProperty = new ReflectionProperty(AbstractAdapter::class, 'namespace');\n\n        self::assertInstanceOf(ApcuAdapter::class, $cache);\n        self::assertSame('dc2_1effb2475fcfba4f9e8b8a1dbc8f3caf:', $namespaceProperty->getValue($cache));\n    }\n\n    #[RequiresPhpExtension('apcu')]\n    #[RequiresSetting('apc.enable_cli', '1')]\n    #[RequiresSetting('apc.enabled', '1')]\n    public function testCacheNamespaceShouldBeGeneratedForApcu(): void\n    {\n        $config = ORMSetup::createConfig(false, '/foo');\n        $cache  = $config->getMetadataCache();\n\n        $namespaceProperty = new ReflectionProperty(AbstractAdapter::class, 'namespace');\n\n        self::assertInstanceOf(ApcuAdapter::class, $cache);\n        self::assertSame('dc2_1effb2475fcfba4f9e8b8a1dbc8f3caf:', $namespaceProperty->getValue($cache));\n    }\n\n    #[Group('DDC-1350')]\n    #[IgnoreDeprecations]\n    public function testConfigureProxyDir(): void\n    {\n        $config = ORMSetup::createAttributeMetadataConfiguration([], true, '/foo');\n        self::assertEquals('/foo', $config->getProxyDir());\n    }\n\n    #[Group('DDC-1350')]\n    public function testConfigureCache(): void\n    {\n        $cache  = new ArrayAdapter();\n        $config = ORMSetup::createAttributeMetadataConfig([], true, null, $cache);\n\n        self::assertSame($cache, $config->getResultCache());\n        self::assertSame($cache, $config->getQueryCache());\n        self::assertSame($cache, $config->getMetadataCache());\n    }\n\n    #[Group('DDC-3190')]\n    public function testConfigureCacheCustomInstance(): void\n    {\n        $cache  = new ArrayAdapter();\n        $config = ORMSetup::createConfig(true, null, $cache);\n\n        self::assertSame($cache, $config->getResultCache());\n        self::assertSame($cache, $config->getQueryCache());\n        self::assertSame($cache, $config->getMetadataCache());\n    }\n}\n\nclass AnnotatedDummy\n{\n    #[MappingNamespace\\PrePersist]\n    public function namespacedAttributeMethod(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Performance/SecondLevelCacheTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Performance;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function count;\nuse function microtime;\nuse function number_format;\nuse function printf;\nuse function sprintf;\nuse function str_repeat;\n\nuse const PHP_EOL;\n\n#[Group('DDC-2183')]\n#[Group('performance')]\nclass SecondLevelCacheTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::useModelSet('cache');\n\n        parent::setUp();\n    }\n\n    public function testFindEntityWithoutCache(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n        $this->findEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(6002);\n    }\n\n    public function testFindEntityWithCache(): void\n    {\n        parent::enableSecondLevelCache(false);\n\n        $this->getQueryLog()->reset()->enable();\n        $this->findEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(502);\n    }\n\n    public function testFindAllEntityWithoutCache(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n        $this->findAllEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(153);\n    }\n\n    public function testFindAllEntityWithCache(): void\n    {\n        parent::enableSecondLevelCache(false);\n\n        $this->getQueryLog()->reset()->enable();\n        $this->findAllEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(53);\n    }\n\n    public function testFindEntityOneToManyWithoutCache(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n        $this->findEntityOneToMany($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(502);\n    }\n\n    public function testFindEntityOneToManyWithCache(): void\n    {\n        parent::enableSecondLevelCache(false);\n\n        $this->getQueryLog()->reset()->enable();\n        $this->findEntityOneToMany($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(472);\n    }\n\n    public function testQueryEntityWithoutCache(): void\n    {\n        $this->getQueryLog()->reset()->enable();\n        $this->queryEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(602);\n    }\n\n    public function testQueryEntityWithCache(): void\n    {\n        parent::enableSecondLevelCache(false);\n\n        $this->getQueryLog()->reset()->enable();\n        $this->queryEntity($this->_em, __FUNCTION__);\n\n        $this->assertQueryCount(503);\n    }\n\n    private function queryEntity(EntityManagerInterface $em, string $label): void\n    {\n        $times        = 100;\n        $size         = 500;\n        $startPersist = microtime(true);\n\n        echo PHP_EOL . $label;\n\n        for ($i = 0; $i < $size; $i++) {\n            $em->persist(new Country(sprintf('Country %d', $i)));\n        }\n\n        $em->flush();\n        $em->clear();\n\n        printf(\"\\n[%s] persist %s countries\", number_format(microtime(true) - $startPersist, 6), $size);\n\n        $dql       = 'SELECT c FROM Doctrine\\Tests\\Models\\Cache\\Country c WHERE c.name LIKE :name';\n        $startFind = microtime(true);\n\n        for ($i = 0; $i < $times; $i++) {\n            $em->createQuery($dql)\n                ->setParameter('name', '%Country%')\n                ->setCacheable(true)\n                ->getResult();\n        }\n\n        printf(\"\\n[%s] select %s countries (%s times)\", number_format(microtime(true) - $startFind, 6), $size, $times);\n        printf(\"\\n%s\\n\", str_repeat('-', 50));\n    }\n\n    public function findEntityOneToMany(EntityManagerInterface $em, string $label): void\n    {\n        $times        = 50;\n        $size         = 30;\n        $states       = [];\n        $cities       = [];\n        $startPersist = microtime(true);\n        $country      = new Country('Country');\n\n        echo PHP_EOL . $label;\n\n        $em->persist($country);\n        $em->flush();\n\n        for ($i = 0; $i < $size / 2; $i++) {\n            $state = new State(sprintf('State %d', $i), $country);\n\n            $em->persist($state);\n\n            $states[] = $state;\n        }\n\n        $em->flush();\n\n        foreach ($states as $key => $state) {\n            for ($i = 0; $i < $size; $i++) {\n                $city = new City(sprintf('City %s - %d', $key, $i), $state);\n\n                $em->persist($city);\n\n                $state->addCity($city);\n\n                $cities[] = $city;\n            }\n        }\n\n        $em->flush();\n        $em->clear();\n\n        printf(\"\\n[%s] persist %s states and %s cities\", number_format(microtime(true) - $startPersist, 6), count($states), count($cities));\n\n        $startFind = microtime(true);\n\n        for ($i = 0; $i < $times; $i++) {\n            foreach ($states as $state) {\n                $state = $em->find(State::class, $state->getId());\n\n                foreach ($state->getCities() as $city) {\n                    $city->getName();\n                }\n            }\n        }\n\n        printf(\"\\n[%s] find %s states and %s cities (%s times)\", number_format(microtime(true) - $startFind, 6), count($states), count($cities), $times);\n        printf(\"\\n%s\\n\", str_repeat('-', 50));\n    }\n\n    private function findEntity(EntityManagerInterface $em, $label): void\n    {\n        $times        = 10;\n        $size         = 500;\n        $countries    = [];\n        $startPersist = microtime(true);\n\n        echo PHP_EOL . $label;\n\n        for ($i = 0; $i < $size; $i++) {\n            $country = new Country(sprintf('Country %d', $i));\n\n            $em->persist($country);\n\n            $countries[] = $country;\n        }\n\n        $em->flush();\n        $em->clear();\n\n        printf(\"\\n[%s] persist %s countries\", number_format(microtime(true) - $startPersist, 6), $size);\n\n        $startFind = microtime(true);\n\n        for ($i = 0; $i <= $times; $i++) {\n            foreach ($countries as $country) {\n                $em->find(Country::class, $country->getId());\n                $em->clear();\n            }\n        }\n\n        printf(\"\\n[%s] find %s countries (%s times)\", number_format(microtime(true) - $startFind, 6), $size, $times);\n        printf(\"\\n%s\\n\", str_repeat('-', 50));\n    }\n\n    private function findAllEntity(EntityManagerInterface $em, string $label): void\n    {\n        $times        = 100;\n        $size         = 50;\n        $startPersist = microtime(true);\n        $rep          = $em->getRepository(Country::class);\n\n        echo PHP_EOL . $label;\n\n        for ($i = 0; $i < $size; $i++) {\n            $em->persist(new Country(sprintf('Country %d', $i)));\n        }\n\n        $em->flush();\n        $em->clear();\n\n        printf(\"\\n[%s] persist %s countries\", number_format(microtime(true) - $startPersist, 6), $size);\n\n        $startFind = microtime(true);\n\n        for ($i = 0; $i <= $times; $i++) {\n            $list = $rep->findAll();\n            $em->clear();\n\n            self::assertCount($size, $list);\n        }\n\n        printf(\"\\n[%s] find %s countries (%s times)\", number_format(microtime(true) - $startFind, 6), $size, $times);\n        printf(\"\\n%s\\n\", str_repeat('-', 50));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/PersistentCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\ORM\\PersistentCollection;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse stdClass;\n\nuse function array_keys;\nuse function assert;\nuse function method_exists;\nuse function serialize;\nuse function unserialize;\n\n/**\n * Tests the lazy-loading capabilities of the PersistentCollection and the initialization of collections.\n */\nclass PersistentCollectionTest extends OrmTestCase\n{\n    /** @var PersistentCollection */\n    protected $collection;\n\n    private EntityManagerMock $_emMock;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $connection = $this->createMock(Connection::class);\n        $connection->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        if (method_exists($connection, 'getEventManager')) {\n            $connection->method('getEventManager')\n                ->willReturn(new EventManager());\n        }\n\n        $connection->method('executeQuery')\n            ->willReturn($this->createMock(Result::class));\n\n        $this->_emMock = new EntityManagerMock($connection);\n\n        $this->setUpPersistentCollection();\n    }\n\n    /**\n     * Set up the PersistentCollection used for collection initialization tests.\n     */\n    public function setUpPersistentCollection(): void\n    {\n        $classMetaData    = $this->_emMock->getClassMetadata(ECommerceCart::class);\n        $this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection());\n        $this->collection->setInitialized(false);\n        $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products'));\n    }\n\n    public function testCanBePutInLazyLoadingMode(): void\n    {\n        $class      = $this->_emMock->getClassMetadata(ECommerceProduct::class);\n        $collection = new PersistentCollection($this->_emMock, $class, new ArrayCollection());\n        $collection->setInitialized(false);\n        self::assertFalse($collection->isInitialized());\n    }\n\n    /**\n     * Test that PersistentCollection::current() initializes the collection.\n     */\n    public function testCurrentInitializesCollection(): void\n    {\n        $this->collection->current();\n        self::assertTrue($this->collection->isInitialized());\n    }\n\n    /**\n     * Test that PersistentCollection::key() initializes the collection.\n     */\n    public function testKeyInitializesCollection(): void\n    {\n        $this->collection->key();\n        self::assertTrue($this->collection->isInitialized());\n    }\n\n    /**\n     * Test that PersistentCollection::next() initializes the collection.\n     */\n    public function testNextInitializesCollection(): void\n    {\n        $this->collection->next();\n        self::assertTrue($this->collection->isInitialized());\n    }\n\n    #[Group('DDC-3382')]\n    public function testNonObjects(): void\n    {\n        self::assertEmpty($this->collection);\n\n        $this->collection->add('dummy');\n\n        self::assertNotEmpty($this->collection);\n\n        $product = new ECommerceProduct();\n\n        $this->collection->set(1, $product);\n        $this->collection->set(2, 'dummy');\n        $this->collection->set(3, null);\n\n        self::assertSame($product, $this->collection->get(1));\n        self::assertSame('dummy', $this->collection->get(2));\n        self::assertNull($this->collection->get(3));\n    }\n\n    #[Group('6110')]\n    public function testRemovingElementsAlsoRemovesKeys(): void\n    {\n        $dummy = new stdClass();\n\n        $this->collection->add($dummy);\n        self::assertEquals([0], array_keys($this->collection->toArray()));\n\n        $this->collection->removeElement($dummy);\n        self::assertEquals([], array_keys($this->collection->toArray()));\n    }\n\n    #[Group('6110')]\n    public function testClearWillAlsoClearKeys(): void\n    {\n        $this->collection->add(new stdClass());\n        $this->collection->clear();\n        self::assertEquals([], array_keys($this->collection->toArray()));\n    }\n\n    #[Group('6110')]\n    public function testClearWillAlsoResetKeyPositions(): void\n    {\n        $dummy = new stdClass();\n\n        $this->collection->add($dummy);\n        $this->collection->removeElement($dummy);\n        $this->collection->clear();\n        $this->collection->add($dummy);\n        self::assertEquals([0], array_keys($this->collection->toArray()));\n    }\n\n    #[Group('6613')]\n    #[Group('6614')]\n    #[Group('6616')]\n    public function testWillKeepNewItemsInDirtyCollectionAfterInitialization(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n        assert($unitOfWork instanceof UnitOfWork || $unitOfWork instanceof MockObject);\n\n        $this->_emMock->setUnitOfWork($unitOfWork);\n\n        $newElement       = new stdClass();\n        $persistedElement = new stdClass();\n\n        $this->collection->add($newElement);\n\n        self::assertFalse($this->collection->isInitialized());\n        self::assertTrue($this->collection->isDirty());\n\n        $unitOfWork\n            ->expects(self::once())\n            ->method('loadCollection')\n            ->with($this->collection)\n            ->willReturnCallback(static function (PersistentCollection $persistentCollection) use ($persistedElement): void {\n                $persistentCollection->unwrap()->add($persistedElement);\n            });\n\n        $this->collection->initialize();\n\n        self::assertSame([$persistedElement, $newElement], $this->collection->toArray());\n        self::assertTrue($this->collection->isInitialized());\n        self::assertTrue($this->collection->isDirty());\n    }\n\n    #[Group('6613')]\n    #[Group('6614')]\n    #[Group('6616')]\n    public function testWillDeDuplicateNewItemsThatWerePreviouslyPersistedInDirtyCollectionAfterInitialization(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n        assert($unitOfWork instanceof UnitOfWork || $unitOfWork instanceof MockObject);\n\n        $this->_emMock->setUnitOfWork($unitOfWork);\n\n        $newElement                    = new stdClass();\n        $newElementThatIsAlsoPersisted = new stdClass();\n        $persistedElement              = new stdClass();\n\n        $this->collection->add($newElementThatIsAlsoPersisted);\n        $this->collection->add($newElement);\n\n        self::assertFalse($this->collection->isInitialized());\n        self::assertTrue($this->collection->isDirty());\n\n        $unitOfWork\n            ->expects(self::once())\n            ->method('loadCollection')\n            ->with($this->collection)\n            ->willReturnCallback(static function (PersistentCollection $persistentCollection) use (\n                $persistedElement,\n                $newElementThatIsAlsoPersisted,\n            ): void {\n                $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted);\n                $persistentCollection->unwrap()->add($persistedElement);\n            });\n\n        $this->collection->initialize();\n\n        self::assertSame(\n            [$newElementThatIsAlsoPersisted, $persistedElement, $newElement],\n            $this->collection->toArray(),\n        );\n        self::assertTrue($this->collection->isInitialized());\n        self::assertTrue($this->collection->isDirty());\n    }\n\n    #[Group('6613')]\n    #[Group('6614')]\n    #[Group('6616')]\n    public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsWereAdded(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n        assert($unitOfWork instanceof UnitOfWork || $unitOfWork instanceof MockObject);\n\n        $this->_emMock->setUnitOfWork($unitOfWork);\n\n        $newElementThatIsAlsoPersisted = new stdClass();\n        $persistedElement              = new stdClass();\n\n        $this->collection->add($newElementThatIsAlsoPersisted);\n\n        self::assertFalse($this->collection->isInitialized());\n        self::assertTrue($this->collection->isDirty());\n\n        $unitOfWork\n            ->expects(self::once())\n            ->method('loadCollection')\n            ->with($this->collection)\n            ->willReturnCallback(static function (PersistentCollection $persistentCollection) use (\n                $persistedElement,\n                $newElementThatIsAlsoPersisted,\n            ): void {\n                $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted);\n                $persistentCollection->unwrap()->add($persistedElement);\n            });\n\n        $this->collection->initialize();\n\n        self::assertSame(\n            [$newElementThatIsAlsoPersisted, $persistedElement],\n            $this->collection->toArray(),\n        );\n        self::assertTrue($this->collection->isInitialized());\n        self::assertFalse($this->collection->isDirty());\n    }\n\n    public function testModifyUOWForDeferredImplicitOwnerOnClear(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n        $unitOfWork->expects(self::once())->method('scheduleCollectionDeletion');\n        $this->_emMock->setUnitOfWork($unitOfWork);\n\n        $this->collection->clear();\n    }\n\n    public function testItCanBeSerializedAndUnserializedBack(): void\n    {\n        $this->collection->add(new stdClass());\n        $collection = unserialize(serialize($this->collection));\n        $collection->add(new stdClass());\n        $collection[3] = new stdClass();\n        self::assertCount(3, $collection);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeParametersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1AlternateName;\nuse Doctrine\\Tests\\Models\\GeoNames\\Country;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass BasicEntityPersisterCompositeTypeParametersTest extends OrmTestCase\n{\n    protected BasicEntityPersister $persister;\n    protected EntityManagerMock $entityManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->entityManager = $this->getTestEntityManager();\n\n        $this->entityManager->getClassMetadata(Country::class);\n        $this->entityManager->getClassMetadata(Admin1::class);\n        $this->entityManager->getClassMetadata(Admin1AlternateName::class);\n\n        $this->persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(Admin1AlternateName::class));\n    }\n\n    public function testExpandParametersWillExpandCompositeEntityKeys(): void\n    {\n        $country = new Country('IT', 'Italy');\n        $admin1  = new Admin1(10, 'Rome', $country);\n\n        [$values, $types] = $this->persister->expandParameters(['admin1' => $admin1]);\n\n        self::assertEquals(['integer', 'string'], $types);\n        self::assertEquals([10, 'IT'], $values);\n    }\n\n    public function testExpandCriteriaParametersWillExpandCompositeEntityKeys(): void\n    {\n        $country = new Country('IT', 'Italy');\n        $admin1  = new Admin1(10, 'Rome', $country);\n\n        $criteria = Criteria::create(true);\n        $criteria->andWhere(Criteria::expr()->eq('admin1', $admin1));\n\n        [$values, $types] = $this->persister->expandCriteriaParameters($criteria);\n\n        self::assertEquals(['integer', 'string'], $types);\n        self::assertEquals([10, 'IT'], $values);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeSqlTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters;\n\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\ORM\\Mapping\\AssociationMapping;\nuse Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\ORM\\Persisters\\Exception\\CantUseInOperatorOnCompositeKeys;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1AlternateName;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass BasicEntityPersisterCompositeTypeSqlTest extends OrmTestCase\n{\n    protected BasicEntityPersister $persister;\n    protected EntityManagerMock $entityManager;\n    private AssociationMapping $associationMapping;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->entityManager      = $this->getTestEntityManager();\n        $this->persister          = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(Admin1AlternateName::class));\n        $this->associationMapping = new ManyToOneAssociationMapping(\n            fieldName: 'admin1',\n            sourceEntity: WhoCares::class,\n            targetEntity: Admin1AlternateName::class,\n        );\n    }\n\n    public function testSelectConditionStatementEq(): void\n    {\n        $statement = $this->persister->getSelectConditionStatementSQL('admin1', 1, $this->associationMapping, Comparison::EQ);\n        self::assertEquals('t0.admin1 = ? AND t0.country = ?', $statement);\n    }\n\n    public function testSelectConditionStatementEqNull(): void\n    {\n        $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, $this->associationMapping, Comparison::IS);\n        self::assertEquals('t0.admin1 IS NULL AND t0.country IS NULL', $statement);\n    }\n\n    public function testSelectConditionStatementNeqNull(): void\n    {\n        $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, $this->associationMapping, Comparison::NEQ);\n        self::assertEquals('t0.admin1 IS NOT NULL AND t0.country IS NOT NULL', $statement);\n    }\n\n    public function testSelectConditionStatementIn(): void\n    {\n        $this->expectException(CantUseInOperatorOnCompositeKeys::class);\n        $this->persister->getSelectConditionStatementSQL('admin1', [], $this->associationMapping, Comparison::IN);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters;\n\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Schema\\Name\\UnquotedIdentifierFolding;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\Tests\\DbalTypes\\NegativeToPositiveType;\nuse Doctrine\\Tests\\DbalTypes\\UpperCaseStringType;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeChild;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent;\nuse Doctrine\\Tests\\Models\\Generic\\NonAlphaColumnsEntity;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse ReflectionMethod;\n\nuse function array_slice;\nuse function enum_exists;\n\nclass BasicEntityPersisterTypeValueSqlTest extends OrmTestCase\n{\n    protected BasicEntityPersister $persister;\n    protected EntityManagerMock $entityManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        if (DBALType::hasType('upper_case_string')) {\n            DBALType::overrideType('upper_case_string', UpperCaseStringType::class);\n        } else {\n            DBALType::addType('upper_case_string', UpperCaseStringType::class);\n        }\n\n        $this->entityManager = $this->getTestEntityManager();\n\n        $this->persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(CustomTypeParent::class));\n    }\n\n    public function testGetInsertSQLUsesTypeValuesSQL(): void\n    {\n        $method = new ReflectionMethod($this->persister, 'getInsertSQL');\n        $sql    = $method->invoke($this->persister);\n\n        self::assertEquals('INSERT INTO customtype_parents (customInteger, child_id) VALUES (ABS(?), ?)', $sql);\n    }\n\n    public function testUpdateUsesTypeValuesSQL(): void\n    {\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n\n        $platform = $this->getMockBuilder(AbstractPlatform::class)\n            ->setConstructorArgs(enum_exists(UnquotedIdentifierFolding::class) ? [UnquotedIdentifierFolding::UPPER] : [])\n            ->getMock();\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $connection = $this->getMockBuilder(Connection::class)\n            ->setConstructorArgs([[], $driver])\n            ->onlyMethods(['executeStatement', 'getDatabasePlatform'])\n            ->getMock();\n        $connection->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        $child = new CustomTypeChild();\n\n        $parent                = new CustomTypeParent();\n        $parent->customInteger = 1;\n        $parent->child         = $child;\n\n        $entityManager = $this->createTestEntityManagerWithConnection($connection);\n\n        $entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], ['customInteger' => 0, 'child' => null]);\n        $entityManager->getUnitOfWork()->registerManaged($child, ['id' => 1], []);\n\n        $entityManager->getUnitOfWork()->propertyChanged($parent, 'customInteger', 0, 1);\n        $entityManager->getUnitOfWork()->propertyChanged($parent, 'child', null, $child);\n\n        $persister = new BasicEntityPersister($entityManager, $entityManager->getClassMetadata(CustomTypeParent::class));\n\n        $connection->expects($this->once())\n            ->method('executeStatement')\n            ->with('UPDATE customtype_parents SET customInteger = ABS(?), child_id = ? WHERE id = ?');\n\n        $persister->update($parent);\n    }\n\n    public function testGetSelectConditionSQLUsesTypeValuesSQL(): void\n    {\n        $method = new ReflectionMethod($this->persister, 'getSelectConditionSQL');\n        $sql    = $method->invoke($this->persister, ['customInteger' => 1, 'child' => 1]);\n\n        self::assertEquals('t0.customInteger = ABS(?) AND t0.child_id = ?', $sql);\n    }\n\n    #[Group('DDC-1719')]\n    public function testStripNonAlphanumericCharactersFromSelectColumnListSQL(): void\n    {\n        $persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(NonAlphaColumnsEntity::class));\n        $method    = new ReflectionMethod($persister, 'getSelectColumnsSQL');\n\n        self::assertEquals('t0.\"simple-entity-id\" AS simpleentityid_1, t0.\"simple-entity-value\" AS simpleentityvalue_2', $method->invoke($persister));\n    }\n\n    #[Group('DDC-2073')]\n    public function testSelectConditionStatementIsNull(): void\n    {\n        $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz');\n        $statement          = $this->persister->getSelectConditionStatementSQL('test', null, $associationMapping, Comparison::IS);\n        self::assertEquals('test IS NULL', $statement);\n    }\n\n    public function testSelectConditionStatementEqNull(): void\n    {\n        $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz');\n        $statement          = $this->persister->getSelectConditionStatementSQL('test', null, $associationMapping, Comparison::EQ);\n        self::assertEquals('test IS NULL', $statement);\n    }\n\n    public function testSelectConditionStatementNeqNull(): void\n    {\n        $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz');\n        $statement          = $this->persister->getSelectConditionStatementSQL(\n            'test',\n            null,\n            $associationMapping,\n            Comparison::NEQ,\n        );\n        self::assertEquals('test IS NOT NULL', $statement);\n    }\n\n    #[Group('DDC-3056')]\n    #[Group('GH12254')]\n    public function testSelectConditionStatementWithMultipleValuesContainingNull(): void\n    {\n        self::assertEquals(\n            't0.id IS NULL',\n            $this->persister->getSelectConditionStatementSQL('id', [null]),\n        );\n\n        self::assertEquals(\n            '(t0.id IN (?) OR t0.id IS NULL)',\n            $this->persister->getSelectConditionStatementSQL('id', [null, 123]),\n        );\n\n        self::assertEquals(\n            '(t0.id IN (?) OR t0.id IS NULL)',\n            $this->persister->getSelectConditionStatementSQL('id', [123, null]),\n        );\n\n        self::assertEquals(\n            '(t0.id IN (?, ?) OR t0.id IS NULL)',\n            $this->persister->getSelectConditionStatementSQL('id', [123, null, 234]),\n        );\n\n        self::assertEquals(\n            '1=0',\n            $this->persister->getSelectConditionStatementSQL('id', []),\n        );\n    }\n\n    public function testCountCondition(): void\n    {\n        $persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(NonAlphaColumnsEntity::class));\n\n        // Using a criteria as array\n        $statement = $persister->getCountSQL(['value' => 'bar']);\n        self::assertEquals('SELECT COUNT(*) FROM \"not-a-simple-entity\" t0 WHERE t0.\"simple-entity-value\" = ?', $statement);\n\n        // Using a criteria object\n        $criteria  = Criteria::create(true)->where(Criteria::expr()->eq('value', 'bar'));\n        $statement = $persister->getCountSQL($criteria);\n        self::assertEquals('SELECT COUNT(*) FROM \"not-a-simple-entity\" t0 WHERE t0.\"simple-entity-value\" = ?', $statement);\n    }\n\n    public function testCountEntities(): void\n    {\n        self::assertEquals(0, $this->persister->count());\n    }\n\n    public function testDeleteManyToManyUsesTypeValuesSQL(): void\n    {\n        $connection = $this->getMockBuilder(Connection::class)\n            ->setConstructorArgs([[], $this->createMock(Driver::class)])\n            ->onlyMethods(['delete', 'getDatabasePlatform'])\n            ->getMock();\n        $connection->method('getDatabasePlatform')\n            ->willReturn($this->createMock(AbstractPlatform::class));\n\n        $entityManager = $this->createTestEntityManagerWithConnection($connection);\n\n        $persister = new BasicEntityPersister($entityManager, $this->entityManager->getClassMetadata(CustomTypeParent::class));\n\n        $friend = new CustomTypeParent();\n        $parent = new CustomTypeParent();\n        $parent->addMyFriend($friend);\n\n        $entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], []);\n        $entityManager->getUnitOfWork()->registerManaged($friend, ['id' => 2], []);\n\n        $deleteCalls = [];\n\n        $connection->method('delete')\n            ->willReturnCallback(static function (...$args) use (&$deleteCalls): int {\n                $deleteCalls[] = $args;\n\n                return 1;\n            });\n\n        $persister->delete($parent);\n\n        self::assertSame([\n            [\n                'customtype_parent_friends',\n                ['friend_customtypeparent_id' => 1],\n                ['integer'],\n            ],\n            [\n                'customtype_parent_friends',\n                ['customtypeparent_id' => 1],\n                ['integer'],\n            ],\n        ], array_slice($deleteCalls, 0, 2));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/BinaryIdPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters;\n\nuse Doctrine\\DBAL\\DriverManager;\nuse Doctrine\\DBAL\\Types\\Type as DbalType;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\ORMSetup;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Persistence\\Mapping\\Driver\\ClassLocator;\nuse Doctrine\\Persistence\\Mapping\\Driver\\FileClassLocator;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\BinaryPrimaryKey\\BinaryIdType;\nuse Doctrine\\Tests\\Models\\BinaryPrimaryKey\\Category;\nuse Doctrine\\Tests\\OrmTestCase;\n\nuse const PHP_VERSION_ID;\n\nfinal class BinaryIdPersisterTest extends OrmTestCase\n{\n    private EntityManager|null $entityManager = null;\n\n    public function testOneToManyWithEagerFetchMode(): void\n    {\n        $entityManager = $this->createEntityManager();\n\n        $this->createDummyBlogData($entityManager, 3);\n\n        $categories = $entityManager->createQueryBuilder()\n            ->select('category')\n            ->from(Category::class, 'category')\n            ->getQuery()\n            ->setFetchMode(Category::class, 'children', ClassMetadata::FETCH_EAGER)\n            ->getResult();\n\n        self::assertCount(3, $categories);\n    }\n\n    private function createDummyBlogData(\n        EntityManager $entityManager,\n        int $categoryCount = 1,\n        int $categoryParentsCount = 0,\n    ): void {\n        for ($h = 0; $h < $categoryCount; $h++) {\n            $categoryParent = null;\n\n            for ($i = 0; $i < $categoryParentsCount; $i++) {\n                $categoryParent = new Category('CategoryParent#' . $i, $categoryParent);\n                $entityManager->persist($categoryParent);\n            }\n\n            $category = new Category('Category#' . $h, $categoryParent);\n            $entityManager->persist($category);\n        }\n\n        $entityManager->flush();\n        $entityManager->clear();\n    }\n\n    private function createEntityManager(): EntityManager\n    {\n        if ($this->entityManager !== null) {\n            return $this->entityManager;\n        }\n\n        $config = ORMSetup::createAttributeMetadataConfiguration(\n            $this->getClassLocator(),\n            isDevMode: true,\n        );\n        $config->enableNativeLazyObjects(PHP_VERSION_ID >= 80400);\n\n        if (! DbalType::hasType(BinaryIdType::NAME)) {\n            DbalType::addType(BinaryIdType::NAME, BinaryIdType::class);\n        }\n\n        $connection    = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config);\n        $entityManager = new EntityManagerMock($connection, $config);\n\n        $schemaTool = new SchemaTool($entityManager);\n        $schemaTool->createSchema($entityManager->getMetadataFactory()->getAllMetadata());\n\n        $schemaValidator = new SchemaValidator($entityManager);\n        $schemaValidator->validateMapping();\n\n        $this->entityManager = $entityManager;\n\n        return $entityManager;\n    }\n\n    /** @return list<string>|ClassLocator */\n    private function getClassLocator(): array|ClassLocator\n    {\n        $paths = [__DIR__ . '/../../Models/BinaryPrimaryKey'];\n\n        if (! AttributeDriverFactory::isClassLocatorSupported()) {\n            return $paths;\n        }\n\n        return FileClassLocator::createFromDirectories($paths);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/Exception/UnrecognizedFieldTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters\\Exception;\n\nuse Doctrine\\ORM\\Persisters\\Exception\\UnrecognizedField;\nuse Doctrine\\Tests\\Models\\Taxi\\Car;\nuse PHPUnit\\Framework\\TestCase;\n\nclass UnrecognizedFieldTest extends TestCase\n{\n    public function testByFullyQualifiedName(): void\n    {\n        static::expectException(UnrecognizedField::class);\n        static::expectExceptionMessage('Unrecognized field: Doctrine\\Tests\\Models\\Taxi\\Car::$color');\n\n        throw UnrecognizedField::byFullyQualifiedName(Car::class, 'color');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Persisters/ManyToManyPersisterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Persisters;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister;\nuse Doctrine\\Tests\\Models\\ManyToManyPersister\\ChildClass;\nuse Doctrine\\Tests\\Models\\ManyToManyPersister\\OtherParentClass;\nuse Doctrine\\Tests\\Models\\ManyToManyPersister\\ParentClass;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[CoversClass(ManyToManyPersister::class)]\nfinal class ManyToManyPersisterTest extends OrmTestCase\n{\n    #[Group('GH-6991')]\n    #[Group('ManyToManyPersister')]\n    public function testDeleteManyToManyCollection(): void\n    {\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n\n        $connection = $this->getMockBuilder(Connection::class)\n            ->setConstructorArgs([[], $driver])\n            ->onlyMethods(['executeStatement', 'getDatabasePlatform'])\n            ->getMock();\n        $connection->method('getDatabasePlatform')\n            ->willReturn(new SQLitePlatform());\n\n        $parent      = new ParentClass(1);\n        $otherParent = new OtherParentClass(42);\n        $child       = new ChildClass(1, $otherParent);\n\n        $parent->children->add($child);\n        $child->parents->add($parent);\n\n        $em = $this->createTestEntityManagerWithConnection($connection);\n        $em->persist($parent);\n        $em->flush();\n\n        $childReloaded = $em->find(ChildClass::class, ['id1' => 1, 'otherParent' => $otherParent]);\n        self::assertInstanceOf(ChildClass::class, $childReloaded);\n\n        $connection->expects($this->once())\n            ->method('executeStatement')\n            ->with('DELETE FROM parent_child WHERE child_id1 = ? AND child_id2 = ?', [1, 42]);\n\n        $persister = new ManyToManyPersister($em);\n        $persister->delete($childReloaded->parents);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Proxy/ProxyFactoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Proxy;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\EntityNotFoundException;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\ORM\\Proxy\\ProxyFactory;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Persistence\\Proxy;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Mocks\\UnitOfWorkMock;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\Attributes\\RequiresMethod;\nuse PHPUnit\\Framework\\Attributes\\RequiresPhp;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse stdClass;\nuse Symfony\\Component\\VarExporter\\ProxyHelper;\n\nuse function assert;\nuse function method_exists;\nuse function sys_get_temp_dir;\n\n/**\n * Test the proxy generator. Its work is generating on-the-fly subclasses of a given model, which implement the Proxy pattern.\n */\nclass ProxyFactoryTest extends OrmTestCase\n{\n    use VerifyDeprecations;\n\n    private UnitOfWorkMock $uowMock;\n    private EntityManagerMock $emMock;\n    private ProxyFactory $proxyFactory;\n\n    protected function setUp(): void\n    {\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $connection = $this->createMock(Connection::class);\n        $connection->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        if (method_exists($connection, 'getEventManager')) {\n            $connection->method('getEventManager')\n                ->willReturn(new EventManager());\n        }\n\n        $this->emMock  = new EntityManagerMock($connection);\n        $this->uowMock = new UnitOfWorkMock($this->emMock);\n        $this->emMock->setUnitOfWork($this->uowMock);\n        $this->proxyFactory = new ProxyFactory($this->emMock, sys_get_temp_dir(), 'Proxies', ProxyFactory::AUTOGENERATE_ALWAYS);\n    }\n\n    public function testReferenceProxyDelegatesLoadingToThePersister(): void\n    {\n        $identifier = ['id' => 42];\n        $persister  = $this->getMockBuilder(BasicEntityPersister::class)\n            ->onlyMethods(['loadById'])\n            ->disableOriginalConstructor()\n            ->getMock();\n\n        $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister);\n\n        $proxy = $this->proxyFactory->getProxy(ECommerceFeature::class, $identifier);\n\n        $persister\n            ->expects(self::atLeastOnce())\n            ->method('loadById')\n            ->with(self::equalTo($identifier))\n            ->willReturn($proxy);\n\n        $proxy->getDescription();\n    }\n\n    public function testSkipMappedSuperClassesOnGeneration(): void\n    {\n        $cm                     = new ClassMetadata(stdClass::class);\n        $cm->isMappedSuperclass = true;\n\n        self::assertSame(\n            0,\n            $this->proxyFactory->generateProxyClasses([$cm]),\n            'No proxies generated.',\n        );\n    }\n\n    #[Group('6625')]\n    public function testSkipEmbeddableClassesOnGeneration(): void\n    {\n        $cm                  = new ClassMetadata(stdClass::class);\n        $cm->isEmbeddedClass = true;\n\n        self::assertSame(\n            0,\n            $this->proxyFactory->generateProxyClasses([$cm]),\n            'No proxies generated.',\n        );\n    }\n\n    #[Group('DDC-1771')]\n    public function testSkipAbstractClassesOnGeneration(): void\n    {\n        $cm = new ClassMetadata(AbstractClass::class);\n        $cm->initializeReflection(new RuntimeReflectionService());\n        self::assertNotNull($cm->reflClass);\n\n        $num = $this->proxyFactory->generateProxyClasses([$cm]);\n\n        self::assertEquals(0, $num, 'No proxies generated.');\n    }\n\n    #[Group('DDC-2432')]\n    public function testFailedProxyLoadingDoesNotMarkTheProxyAsInitialized(): void\n    {\n        $persister = $this->getMockBuilder(BasicEntityPersister::class)\n            ->onlyMethods(['load'])\n            ->disableOriginalConstructor()\n            ->getMock();\n        $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister);\n\n        $proxy = $this->proxyFactory->getProxy(ECommerceFeature::class, ['id' => 42]);\n\n        $persister\n            ->expects(self::atLeastOnce())\n            ->method('load')\n            ->willReturn(null);\n\n        try {\n            $proxy->getDescription();\n            self::fail('An exception was expected to be raised');\n        } catch (EntityNotFoundException) {\n        }\n\n        self::assertUninitializedLazyObject($proxy);\n    }\n\n    private static function assertUninitializedLazyObject(object $proxy): void\n    {\n        if ($proxy instanceof Proxy) {\n            self::assertFalse($proxy->__isInitialized());\n\n            return;\n        }\n\n        $reflectionClass = new ReflectionClass($proxy);\n        self::assertTrue($reflectionClass->isUninitializedLazyObject($proxy));\n    }\n\n    #[Group('DDC-2432')]\n    public function testFailedProxyCloningDoesNotMarkTheProxyAsInitialized(): void\n    {\n        $persister = $this->getMockBuilder(BasicEntityPersister::class)\n            ->onlyMethods(['load', 'getClassMetadata'])\n            ->disableOriginalConstructor()\n            ->getMock();\n        $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister);\n\n        $proxy = $this->proxyFactory->getProxy(ECommerceFeature::class, ['id' => 42]);\n\n        $persister\n            ->expects(self::atLeastOnce())\n            ->method('load')\n            ->willReturn(null);\n\n        try {\n            $cloned = clone $proxy;\n            $cloned->__load();\n            self::fail('An exception was expected to be raised');\n        } catch (EntityNotFoundException) {\n        }\n\n        self::assertUninitializedLazyObject($proxy);\n    }\n\n    public function testProxyClonesParentFields(): void\n    {\n        if ($this->emMock->getConfiguration()->isNativeLazyObjectsEnabled()) {\n            self::markTestSkipped('This test is not relevant when native lazy objects are enabled');\n        }\n\n        $companyEmployee = new CompanyEmployee();\n        $companyEmployee->setSalary(1000); // A property on the CompanyEmployee\n        $companyEmployee->setName('Bob'); // A property on the parent class, CompanyPerson\n\n        // Set the id of the CompanyEmployee (which is in the parent CompanyPerson)\n        $property = new ReflectionProperty(CompanyPerson::class, 'id');\n        $property->setValue($companyEmployee, 42);\n\n        $classMetaData = $this->emMock->getClassMetadata(CompanyEmployee::class);\n\n        $persister = $this->getMockBuilder(BasicEntityPersister::class)\n            ->onlyMethods(['loadById', 'getClassMetadata'])\n            ->disableOriginalConstructor()\n            ->getMock();\n        $this->uowMock->setEntityPersister(CompanyEmployee::class, $persister);\n\n        $proxy = $this->proxyFactory->getProxy(CompanyEmployee::class, ['id' => 42]);\n        assert($proxy instanceof Proxy);\n\n        $loadByIdMock = $persister\n            ->expects(self::atLeastOnce())\n            ->method('loadById');\n\n        $loadByIdMock->willReturn($companyEmployee);\n\n        $persister\n            ->expects(self::atLeastOnce())\n            ->method('getClassMetadata')\n            ->willReturn($classMetaData);\n\n        $cloned = clone $proxy;\n        assert($cloned instanceof CompanyEmployee);\n\n        self::assertSame(42, $cloned->getId(), 'Expected the Id to be cloned');\n        self::assertSame(1000, $cloned->getSalary(), 'Expect properties on the CompanyEmployee class to be cloned');\n        self::assertSame('Bob', $cloned->getName(), 'Expect properties on the CompanyPerson class to be cloned');\n    }\n\n    #[RequiresPhp('8.4')]\n    public function testProxyFactoryAcceptsNullProxyArgsWhenNativeLazyObjectsAreEnabled(): void\n    {\n        $this->emMock->getConfiguration()->enableNativeLazyObjects(true);\n        $this->proxyFactory = new ProxyFactory(\n            $this->emMock,\n            null,\n            null,\n        );\n        $proxy              = $this->proxyFactory->getProxy(\n            ECommerceFeature::class,\n            ['id' => 42],\n        );\n        $reflection         = new ReflectionClass($proxy);\n\n        self::assertTrue($reflection->isUninitializedLazyObject($proxy));\n    }\n\n    #[RequiresPhp('8.4')]\n    #[RequiresMethod(ProxyHelper::class, 'generateLazyGhost')]\n    #[IgnoreDeprecations]\n    public function testProxyFactoryTriggersDeprecationWhenNativeLazyObjectsAreDisabled(): void\n    {\n        $this->emMock->getConfiguration()->enableNativeLazyObjects(false);\n\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n\n        $this->proxyFactory = new ProxyFactory(\n            $this->emMock,\n            sys_get_temp_dir(),\n            'Proxies',\n            ProxyFactory::AUTOGENERATE_ALWAYS,\n        );\n    }\n\n    #[RequiresPhp('< 8.4')]\n    public function testProxyFactoryDoesNotTriggerDeprecationWhenNativeLazyObjectsAreDisabled(): void\n    {\n        $this->emMock->getConfiguration()->enableNativeLazyObjects(false);\n\n        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12005');\n\n        $this->proxyFactory = new ProxyFactory(\n            $this->emMock,\n            sys_get_temp_dir(),\n            'Proxies',\n            ProxyFactory::AUTOGENERATE_ALWAYS,\n        );\n    }\n\n    public function testProxyFactoryThrowsIfLazyGhostsAreUnavailable(): void\n    {\n        if (method_exists(ProxyHelper::class, 'generateLazyGhost')) {\n            self::markTestSkipped('This test is not relevant when lazy ghosts are available');\n        }\n\n        $this->emMock->getConfiguration()->enableNativeLazyObjects(false);\n\n        $this->expectException(ORMInvalidArgumentException::class);\n        $this->expectExceptionMessage('Symfony LazyGhost is not available. Please install the \"symfony/var-exporter\" package version 6.4 or 7 to use this feature or enable PHP 8.4 native lazy objects.');\n\n        new ProxyFactory(\n            $this->emMock,\n            sys_get_temp_dir(),\n            'Proxies',\n            ProxyFactory::AUTOGENERATE_ALWAYS,\n        );\n    }\n}\n\nabstract class AbstractClass\n{\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/CustomTreeWalkersJoinTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\Tests\\Mocks\\CustomTreeWalkerJoin;\nuse Doctrine\\Tests\\OrmTestCase;\n\n/**\n * Test case for custom AST walking and adding new joins.\n *\n * @link        http://www.doctrine-project.org\n */\nclass CustomTreeWalkersJoinTest extends OrmTestCase\n{\n    private EntityManagerInterface $em;\n\n    protected function setUp(): void\n    {\n        $this->em = $this->getTestEntityManager();\n    }\n\n    public function assertSqlGeneration(string $dqlToBeTested, string $sqlToBeConfirmed): void\n    {\n        $query = $this->em->createQuery($dqlToBeTested);\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CustomTreeWalkerJoin::class])\n              ->useQueryCache(false);\n\n        self::assertEquals($sqlToBeConfirmed, $query->getSql());\n        $query->free();\n    }\n\n    public function testAddsJoin(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id',\n        );\n    }\n\n    public function testDoesNotAddJoin(): void\n    {\n        $this->assertSqlGeneration(\n            'select a from Doctrine\\Tests\\Models\\CMS\\CmsAddress a',\n            'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_',\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/CustomTreeWalkersTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\ComparisonExpression;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalExpression;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalFactor;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalPrimary;\nuse Doctrine\\ORM\\Query\\AST\\ConditionalTerm;\nuse Doctrine\\ORM\\Query\\AST\\PathExpression;\nuse Doctrine\\ORM\\Query\\AST\\SelectStatement;\nuse Doctrine\\ORM\\Query\\AST\\WhereClause;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlOutputWalker;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TreeWalker;\nuse Doctrine\\ORM\\Query\\TreeWalkerAdapter;\nuse Doctrine\\Tests\\Mocks\\CustomTreeWalkerJoin;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\n\nuse function array_merge;\nuse function count;\n\n/**\n * Test case for custom AST walking and modification.\n *\n * @link        http://www.doctrine-project.org\n */\nclass CustomTreeWalkersTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    /**\n     * @param list<class-string<TreeWalker>> $treeWalkers\n     * @param class-string<SqlWalker>|null   $outputWalker\n     */\n    public function generateSql(string $dqlToBeTested, array $treeWalkers, string|null $outputWalker): string\n    {\n        $query = $this->entityManager->createQuery($dqlToBeTested);\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $treeWalkers)\n            ->useQueryCache(false);\n\n        if ($outputWalker) {\n            $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, $outputWalker);\n        }\n\n        return $query->getSql();\n    }\n\n    /**\n     * @param list<class-string<TreeWalker>> $treeWalkers\n     * @param class-string<SqlWalker>|null   $outputWalker\n     */\n    public function assertSqlGeneration(\n        string $dqlToBeTested,\n        string $sqlToBeConfirmed,\n        array $treeWalkers = [],\n        string|null $outputWalker = null,\n    ): void {\n        self::assertEquals($sqlToBeConfirmed, $this->generateSql($dqlToBeTested, $treeWalkers, $outputWalker));\n    }\n\n    public function testSupportsQueriesWithoutWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = 1',\n            [CustomTreeWalker::class],\n        );\n    }\n\n    public function testSupportsQueriesWithMultipleConditionalExpressions(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.name = :name or u.name = :otherName',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1',\n            [CustomTreeWalker::class],\n        );\n    }\n\n    public function testSupportsQueriesWithSimpleConditionalExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.name = :name',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1',\n            [CustomTreeWalker::class],\n        );\n    }\n\n    public function testSetUnknownQueryComponentThrowsException(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage(\"Invalid query component given for DQL alias 'x', requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys.\");\n\n        $this->generateSql(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            [],\n            AddUnknownQueryComponentWalker::class,\n        );\n    }\n\n    public function testSupportsSeveralHintsQueries(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c0_.id = 1',\n            [CustomTreeWalkerJoin::class, CustomTreeWalker::class],\n        );\n    }\n}\n\nclass AddUnknownQueryComponentWalker extends SqlOutputWalker\n{\n    protected function createSqlForFinalizer(SelectStatement $selectStatement): string\n    {\n        $this->setQueryComponent('x', []);\n\n        return parent::createSqlForFinalizer($selectStatement);\n    }\n}\n\nclass CustomTreeWalker extends TreeWalkerAdapter\n{\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        // Get the DQL aliases of all the classes we want to modify\n        $dqlAliases = [];\n\n        foreach ($this->getQueryComponents() as $dqlAlias => $comp) {\n            // Hard-coded check just for demonstration: We want to modify the query if\n            // it involves the CmsUser class.\n            if ($comp['metadata']->name === CmsUser::class) {\n                $dqlAliases[] = $dqlAlias;\n            }\n        }\n\n        // Create our conditions for all involved classes\n        $factors = [];\n        foreach ($dqlAliases as $alias) {\n            $pathExpr       = new PathExpression(PathExpression::TYPE_STATE_FIELD, $alias, 'id');\n            $pathExpr->type = PathExpression::TYPE_STATE_FIELD;\n            $comparisonExpr = new ComparisonExpression($pathExpr, '=', '1');\n\n            $condPrimary                              = new ConditionalPrimary();\n            $condPrimary->simpleConditionalExpression = $comparisonExpr;\n\n            $factor    = new ConditionalFactor($condPrimary);\n            $factors[] = $factor;\n        }\n\n        $whereClause = $selectStatement->whereClause;\n        if ($whereClause !== null) {\n            // There is already a WHERE clause, so append the conditions\n            $condExpr = $whereClause->conditionalExpression;\n\n            // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression\n            if (! ($condExpr instanceof ConditionalExpression)) {\n                $condExpr = new ConditionalExpression([$condExpr]);\n\n                $whereClause->conditionalExpression = $condExpr;\n            }\n\n            $existingTerms = $whereClause->conditionalExpression->conditionalTerms;\n\n            if (count($existingTerms) > 1) {\n                // More than one term, so we need to wrap all these terms in a single root term\n                // i.e: \"WHERE u.name = :foo or u.other = :bar\" => \"WHERE (u.name = :foo or u.other = :bar) AND <our condition>\"\n\n                $primary                        = new ConditionalPrimary();\n                $primary->conditionalExpression = new ConditionalExpression($existingTerms);\n                $existingFactor                 = new ConditionalFactor($primary);\n                $term                           = new ConditionalTerm([...[$existingFactor], ...$factors]);\n\n                $selectStatement->whereClause->conditionalExpression->conditionalTerms = [$term];\n            } else {\n                // Just one term so we can simply append our factors to that term\n                $singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0];\n\n                // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression\n                if (! ($singleTerm instanceof ConditionalTerm)) {\n                    $singleTerm = new ConditionalTerm([$singleTerm]);\n\n                    $selectStatement->whereClause->conditionalExpression->conditionalTerms[0] = $singleTerm;\n                }\n\n                $singleTerm->conditionalFactors                                        = array_merge($singleTerm->conditionalFactors, $factors);\n                $selectStatement->whereClause->conditionalExpression->conditionalTerms = [$singleTerm];\n            }\n        } else {\n            // Create a new WHERE clause with our factors\n            $term                         = new ConditionalTerm($factors);\n            $condExpr                     = new ConditionalExpression([$term]);\n            $whereClause                  = new WhereClause($condExpr);\n            $selectStatement->whereClause = $whereClause;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/DeleteSqlGenerationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Test case for testing the saving and referencing of query identifiers.\n *\n * @link        http://www.phpdoctrine.org\n *\n * @todo        1) [romanb] We  might want to split the SQL generation tests into multiple\n *              testcases later since we'll have a lot of them and we might want to have special SQL\n *              generation tests for some dbms specific SQL syntaxes.\n */\nclass DeleteSqlGenerationTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    public function assertSqlGeneration(string $dqlToBeTested, string $sqlToBeConfirmed): void\n    {\n        $query = $this->entityManager->createQuery($dqlToBeTested);\n\n        parent::assertEquals($sqlToBeConfirmed, $query->getSql());\n\n        $query->free();\n    }\n\n    #[Group('6939')]\n    public function testSupportsDeleteWithoutWhereAndAlias(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser',\n            'DELETE FROM cms_users',\n        );\n    }\n\n    public function testSupportsDeleteWithoutWhereAndFrom(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'DELETE FROM cms_users',\n        );\n    }\n\n    public function testSupportsDeleteWithoutWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'DELETE FROM cms_users',\n        );\n    }\n\n    public function testSupportsWhereClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1',\n            'DELETE FROM cms_users WHERE id = ?',\n        );\n    }\n\n    public function testSupportsWhereOrExpressions(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = ?1 OR u.name = ?2',\n            'DELETE FROM cms_users WHERE username = ? OR name = ?',\n        );\n    }\n\n    public function testSupportsWhereNestedConditionalExpressions(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1 OR ( u.username = ?2 OR u.name = ?3)',\n            'DELETE FROM cms_users WHERE id = ? OR (username = ? OR name = ?)',\n        );\n\n        //$this->assertSqlGeneration(\n        //    'DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser WHERE id = ?1',\n        //    'DELETE FROM cms_users WHERE id = ?'\n        //);\n    }\n\n    public function testIsCaseAgnostic(): void\n    {\n        $this->assertSqlGeneration(\n            'delete from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1',\n            'DELETE FROM cms_users WHERE username = ?',\n        );\n    }\n\n    public function testSupportsAndCondition(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = ?1 AND u.name = ?2',\n            'DELETE FROM cms_users WHERE username = ? AND name = ?',\n        );\n    }\n\n    public function testSupportsWhereNot(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NOT u.id != ?1',\n            'DELETE FROM cms_users WHERE NOT id <> ?',\n        );\n    }\n\n    public function testSupportsWhereNotWithParentheses(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NOT ( u.id != ?1 )',\n            'DELETE FROM cms_users WHERE NOT (id <> ?)',\n        );\n    }\n\n    public function testSupportsWhereNotWithAndExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NOT ( u.id != ?1 AND u.username = ?2 )',\n            'DELETE FROM cms_users WHERE NOT (id <> ? AND username = ?)',\n        );\n    }\n\n    // ConditionalPrimary was already tested (see testSupportsWhereClause() and testSupportsWhereNot())\n\n    public function testSupportsGreaterThanComparisonClause(): void\n    {\n        // id = ? was already tested (see testDeleteWithWhere())\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id > ?1',\n            'DELETE FROM cms_users WHERE id > ?',\n        );\n    }\n\n    public function testSupportsGreaterThanOrEqualToComparisonClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id >= ?1',\n            'DELETE FROM cms_users WHERE id >= ?',\n        );\n    }\n\n    public function testSupportsLessThanComparisonClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id < ?1',\n            'DELETE FROM cms_users WHERE id < ?',\n        );\n    }\n\n    public function testSupportsLessThanOrEqualToComparisonClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id <= ?1',\n            'DELETE FROM cms_users WHERE id <= ?',\n        );\n    }\n\n    public function testSupportsNotEqualToComparisonClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id <> ?1',\n            'DELETE FROM cms_users WHERE id <> ?',\n        );\n    }\n\n    public function testSupportsNotEqualToComparisonClauseExpressedWithExclamationMark(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id != ?1',\n            'DELETE FROM cms_users WHERE id <> ?',\n        );\n    }\n\n    public function testSupportsNotBetweenClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id NOT BETWEEN ?1 AND ?2',\n            'DELETE FROM cms_users WHERE id NOT BETWEEN ? AND ?',\n        );\n    }\n\n    public function testSupportsBetweenClauseUsedWithAndClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id BETWEEN ?1 AND ?2 AND u.username != ?3',\n            'DELETE FROM cms_users WHERE id BETWEEN ? AND ? AND username <> ?',\n        );\n    }\n\n    public function testSupportsNotLikeClause(): void\n    {\n        // \"WHERE\" Expression LikeExpression\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username NOT LIKE ?1',\n            'DELETE FROM cms_users WHERE username NOT LIKE ?',\n        );\n    }\n\n    public function testSupportsLikeClauseWithEscapeExpression(): void\n    {\n        $this->assertSqlGeneration(\n            \"DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username LIKE ?1 ESCAPE '\\\\'\",\n            \"DELETE FROM cms_users WHERE username LIKE ? ESCAPE '\\\\'\",\n        );\n    }\n\n    public function testSupportsIsNullClause(): void\n    {\n        // \"WHERE\" Expression NullComparisonExpression\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IS NULL',\n            'DELETE FROM cms_users WHERE name IS NULL',\n        );\n    }\n\n    public function testSupportsIsNotNullClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IS NOT NULL',\n            'DELETE FROM cms_users WHERE name IS NOT NULL',\n        );\n    }\n\n    public function testSupportsAtomExpressionAsClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE 1 = 1',\n            'DELETE FROM cms_users WHERE 1 = 1',\n        );\n    }\n\n    public function testSupportsParameterizedAtomExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE ?1 = 1',\n            'DELETE FROM cms_users WHERE ? = 1',\n        );\n    }\n\n    public function testSupportsInClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id IN ( ?1, ?2, ?3, ?4 )',\n            'DELETE FROM cms_users WHERE id IN (?, ?, ?, ?)',\n        );\n    }\n\n    public function testSupportsNotInClause(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id NOT IN ( ?1, ?2 )',\n            'DELETE FROM cms_users WHERE id NOT IN (?, ?)',\n        );\n    }\n\n    #[Group('DDC-980')]\n    public function testSubselectTableAliasReferencing(): void\n    {\n        $this->assertSqlGeneration(\n            'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE SIZE(u.groups) = 10',\n            'DELETE FROM cms_users WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10',\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/ExprTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query\\Expr;\nuse Doctrine\\ORM\\Query\\Expr\\Andx;\nuse Doctrine\\ORM\\Query\\Expr\\Comparison;\nuse Doctrine\\ORM\\Query\\Expr\\From;\nuse Doctrine\\ORM\\Query\\Expr\\Func;\nuse Doctrine\\ORM\\Query\\Expr\\GroupBy;\nuse Doctrine\\ORM\\Query\\Expr\\Join;\nuse Doctrine\\ORM\\Query\\Expr\\Literal;\nuse Doctrine\\ORM\\Query\\Expr\\Math;\nuse Doctrine\\ORM\\Query\\Expr\\OrderBy;\nuse Doctrine\\ORM\\Query\\Expr\\Orx;\nuse Doctrine\\ORM\\Query\\Expr\\Select;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Test case for the DQL Expr class used for generating DQL snippets through\n * a programmatic interface\n *\n * @link        http://www.phpdoctrine.org\n */\nclass ExprTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n\n    private Expr $expr;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n        $this->expr          = new Expr();\n    }\n\n    public function testAvgExpr(): void\n    {\n        self::assertEquals('AVG(u.id)', (string) $this->expr->avg('u.id'));\n    }\n\n    public function testMaxExpr(): void\n    {\n        self::assertEquals('MAX(u.id)', (string) $this->expr->max('u.id'));\n    }\n\n    public function testMinExpr(): void\n    {\n        self::assertEquals('MIN(u.id)', (string) $this->expr->min('u.id'));\n    }\n\n    public function testCountExpr(): void\n    {\n        self::assertEquals('MAX(u.id)', (string) $this->expr->max('u.id'));\n    }\n\n    public function testCountDistinctExpr(): void\n    {\n        self::assertEquals('COUNT(DISTINCT u.id)', (string) $this->expr->countDistinct('u.id'));\n    }\n\n    public function testCountDistinctExprMulti(): void\n    {\n        self::assertEquals('COUNT(DISTINCT u.id, u.name)', (string) $this->expr->countDistinct('u.id', 'u.name'));\n    }\n\n    public function testExistsExpr(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from('User', 'u')->where('u.name = ?1');\n\n        self::assertEquals('EXISTS(SELECT u FROM User u WHERE u.name = ?1)', (string) $this->expr->exists($qb));\n    }\n\n    public function testAllExpr(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from('User', 'u')->where('u.name = ?1');\n\n        self::assertEquals('ALL(SELECT u FROM User u WHERE u.name = ?1)', (string) $this->expr->all($qb));\n    }\n\n    public function testSomeExpr(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from('User', 'u')->where('u.name = ?1');\n\n        self::assertEquals('SOME(SELECT u FROM User u WHERE u.name = ?1)', (string) $this->expr->some($qb));\n    }\n\n    public function testAnyExpr(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from('User', 'u')->where('u.name = ?1');\n\n        self::assertEquals('ANY(SELECT u FROM User u WHERE u.name = ?1)', (string) $this->expr->any($qb));\n    }\n\n    public function testNotExpr(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from('User', 'u')->where('u.name = ?1');\n\n        self::assertEquals('NOT(SELECT u FROM User u WHERE u.name = ?1)', (string) $this->expr->not($qb));\n    }\n\n    public function testAndExpr(): void\n    {\n        self::assertEquals('1 = 1 AND 2 = 2', (string) $this->expr->andX((string) $this->expr->eq(1, 1), (string) $this->expr->eq(2, 2)));\n    }\n\n    public function testIntelligentParenthesisPreventionAndExpr(): void\n    {\n        self::assertEquals(\n            '1 = 1 AND 2 = 2',\n            (string) $this->expr->andX($this->expr->orX($this->expr->andX($this->expr->eq(1, 1))), (string) $this->expr->eq(2, 2)),\n        );\n    }\n\n    public function testOrExpr(): void\n    {\n        self::assertEquals('1 = 1 OR 2 = 2', (string) $this->expr->orX((string) $this->expr->eq(1, 1), (string) $this->expr->eq(2, 2)));\n    }\n\n    public function testAbsExpr(): void\n    {\n        self::assertEquals('ABS(1)', (string) $this->expr->abs(1));\n    }\n\n    public function testProdExpr(): void\n    {\n        self::assertEquals('1 * 2', (string) $this->expr->prod(1, 2));\n    }\n\n    public function testDiffExpr(): void\n    {\n        self::assertEquals('1 - 2', (string) $this->expr->diff(1, 2));\n    }\n\n    public function testSumExpr(): void\n    {\n        self::assertEquals('1 + 2', (string) $this->expr->sum(1, 2));\n    }\n\n    public function testQuotientExpr(): void\n    {\n        self::assertEquals('10 / 2', (string) $this->expr->quot(10, 2));\n    }\n\n    public function testScopeInArithmeticExpr(): void\n    {\n        self::assertEquals('(100 - 20) / 2', (string) $this->expr->quot($this->expr->diff(100, 20), 2));\n        self::assertEquals('100 - (20 / 2)', (string) $this->expr->diff(100, $this->expr->quot(20, 2)));\n    }\n\n    public function testSquareRootExpr(): void\n    {\n        self::assertEquals('SQRT(1)', (string) $this->expr->sqrt(1));\n    }\n\n    public function testEqualExpr(): void\n    {\n        self::assertEquals('1 = 1', (string) $this->expr->eq(1, 1));\n    }\n\n    public function testLikeExpr(): void\n    {\n        self::assertEquals('a.description LIKE :description', (string) $this->expr->like('a.description', ':description'));\n    }\n\n    public function testNotLikeExpr(): void\n    {\n        self::assertEquals('a.description NOT LIKE :description', (string) $this->expr->notLike('a.description', ':description'));\n    }\n\n    public function testConcatExpr(): void\n    {\n        self::assertEquals('CONCAT(u.first_name, u.last_name)', (string) $this->expr->concat('u.first_name', 'u.last_name'));\n        self::assertEquals('CONCAT(u.first_name, u.middle_name, u.last_name)', (string) $this->expr->concat('u.first_name', 'u.middle_name', 'u.last_name'));\n    }\n\n    public function testSubstringExpr(): void\n    {\n        self::assertEquals('SUBSTRING(a.title, 0, 25)', (string) $this->expr->substring('a.title', 0, 25));\n    }\n\n    public function testModExpr(): void\n    {\n        self::assertEquals('MOD(10, 1)', (string) $this->expr->mod(10, 1));\n    }\n\n    #[Group('regression')]\n    #[Group('DDC-612')]\n    public function testSubstringExprAcceptsTwoArguments(): void\n    {\n        self::assertEquals('SUBSTRING(a.title, 5)', (string) $this->expr->substring('a.title', 5));\n    }\n\n    public function testLowerExpr(): void\n    {\n        self::assertEquals('LOWER(u.first_name)', (string) $this->expr->lower('u.first_name'));\n    }\n\n    public function testUpperExpr(): void\n    {\n        self::assertEquals('UPPER(u.first_name)', (string) $this->expr->upper('u.first_name'));\n    }\n\n    public function testLengthExpr(): void\n    {\n        self::assertEquals('LENGTH(u.first_name)', (string) $this->expr->length('u.first_name'));\n    }\n\n    public function testGreaterThanExpr(): void\n    {\n        self::assertEquals('5 > 2', (string) $this->expr->gt(5, 2));\n    }\n\n    public function testLessThanExpr(): void\n    {\n        self::assertEquals('2 < 5', (string) $this->expr->lt(2, 5));\n    }\n\n    public function testStringLiteralExpr(): void\n    {\n        self::assertEquals(\"'word'\", (string) $this->expr->literal('word'));\n    }\n\n    public function testNumericLiteralExpr(): void\n    {\n        self::assertEquals(5, (string) $this->expr->literal(5));\n    }\n\n    #[Group('regression')]\n    #[Group('DDC-610')]\n    public function testLiteralExprProperlyQuotesStrings(): void\n    {\n        self::assertEquals(\"'00010001'\", (string) $this->expr->literal('00010001'));\n    }\n\n    public function testGreaterThanOrEqualToExpr(): void\n    {\n        self::assertEquals('5 >= 2', (string) $this->expr->gte(5, 2));\n    }\n\n    public function testLessThanOrEqualTo(): void\n    {\n        self::assertEquals('2 <= 5', (string) $this->expr->lte(2, 5));\n    }\n\n    public function testBetweenExpr(): void\n    {\n        self::assertEquals('u.id BETWEEN 3 AND 6', (string) $this->expr->between('u.id', 3, 6));\n    }\n\n    public function testTrimExpr(): void\n    {\n        self::assertEquals('TRIM(u.id)', (string) $this->expr->trim('u.id'));\n    }\n\n    public function testIsNullExpr(): void\n    {\n        self::assertEquals('u.id IS NULL', (string) $this->expr->isNull('u.id'));\n    }\n\n    public function testIsNotNullExpr(): void\n    {\n        self::assertEquals('u.id IS NOT NULL', (string) $this->expr->isNotNull('u.id'));\n    }\n\n    public function testIsInstanceOfExpr(): void\n    {\n        self::assertEquals('u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyEmployee', (string) $this->expr->isInstanceOf('u', CompanyEmployee::class));\n    }\n\n    public function testIsMemberOfExpr(): void\n    {\n        self::assertEquals(':groupId MEMBER OF u.groups', (string) $this->expr->isMemberOf(':groupId', 'u.groups'));\n    }\n\n    public static function provideIterableValue(): Generator\n    {\n        $gen = static function () {\n            yield from [1, 2, 3];\n        };\n\n        yield 'simple_array' => [[1, 2, 3]];\n        yield 'generator' => [$gen()];\n    }\n\n    public static function provideLiteralIterableValue(): Generator\n    {\n        $gen = static function () {\n            yield from ['foo', 'bar'];\n        };\n\n        yield 'simple_array' => [['foo', 'bar']];\n        yield 'generator' => [$gen()];\n    }\n\n    #[DataProvider('provideIterableValue')]\n    public function testInExpr(iterable $value): void\n    {\n        self::assertEquals('u.id IN(1, 2, 3)', (string) $this->expr->in('u.id', $value));\n    }\n\n    #[DataProvider('provideLiteralIterableValue')]\n    public function testInLiteralExpr(iterable $value): void\n    {\n        self::assertEquals(\"u.type IN('foo', 'bar')\", (string) $this->expr->in('u.type', $value));\n    }\n\n    #[DataProvider('provideIterableValue')]\n    public function testNotInExpr(iterable $value): void\n    {\n        self::assertEquals('u.id NOT IN(1, 2, 3)', (string) $this->expr->notIn('u.id', $value));\n    }\n\n    #[DataProvider('provideLiteralIterableValue')]\n    public function testNotInLiteralExpr(iterable $value): void\n    {\n        self::assertEquals(\"u.type NOT IN('foo', 'bar')\", (string) $this->expr->notIn('u.type', $value));\n    }\n\n    public function testAndxOrxExpr(): void\n    {\n        $andExpr = $this->expr->andX();\n        $andExpr->add($this->expr->eq(1, 1));\n        $andExpr->add($this->expr->lt(1, 5));\n\n        $orExpr = $this->expr->orX();\n        $orExpr->add($andExpr);\n        $orExpr->add($this->expr->eq(1, 1));\n\n        self::assertEquals('(1 = 1 AND 1 < 5) OR 1 = 1', (string) $orExpr);\n    }\n\n    public function testOrxExpr(): void\n    {\n        $orExpr = $this->expr->orX();\n        $orExpr->add($this->expr->eq(1, 1));\n        $orExpr->add($this->expr->lt(1, 5));\n\n        self::assertEquals('1 = 1 OR 1 < 5', (string) $orExpr);\n    }\n\n    public function testOrderByCountExpr(): void\n    {\n        $orderExpr = $this->expr->desc('u.username');\n\n        self::assertEquals($orderExpr->count(), 1);\n        self::assertEquals('u.username DESC', (string) $orderExpr);\n    }\n\n    public function testOrderByOrder(): void\n    {\n        $orderExpr = $this->expr->desc('u.username');\n        self::assertEquals('u.username DESC', (string) $orderExpr);\n    }\n\n    public function testOrderByAsc(): void\n    {\n        $orderExpr = $this->expr->asc('u.username');\n        self::assertEquals('u.username ASC', (string) $orderExpr);\n    }\n\n    public function testAddThrowsException(): void\n    {\n        $orExpr = $this->expr->orX();\n        $this->expectException(InvalidArgumentException::class);\n        $orExpr->add($this->expr->quot(5, 2));\n    }\n\n    #[DataProvider('provideInvalidTypesForAdd')]\n    public function testAddThrowsExceptionOnInvalidType(mixed $arg): void\n    {\n        $orExpr = $this->expr->orX();\n        $this->expectException(InvalidArgumentException::class);\n        $orExpr->add($arg);\n    }\n\n    /** @return Generator<string, array{mixed}> */\n    public static function provideInvalidTypesForAdd(): Generator\n    {\n        yield 'integer 1' => [1];\n        yield 'object' => [(object) ['foo' => 'bar']];\n        yield 'array' => [['foo' => 'bar']];\n    }\n\n    #[Group('DDC-1683')]\n    public function testBooleanLiteral(): void\n    {\n        self::assertEquals('true', $this->expr->literal(true));\n        self::assertEquals('false', $this->expr->literal(false));\n    }\n\n    #[Group('DDC-1686')]\n    public function testExpressionGetter(): void\n    {\n        // Andx\n        $andx = new Andx(['1 = 1', '2 = 2']);\n        self::assertEquals(['1 = 1', '2 = 2'], $andx->getParts());\n\n        // Comparison\n        $comparison = new Comparison('foo', Comparison::EQ, 'bar');\n        self::assertEquals('foo', $comparison->getLeftExpr());\n        self::assertEquals('bar', $comparison->getRightExpr());\n        self::assertEquals(Comparison::EQ, $comparison->getOperator());\n\n        // From\n        $from = new From('Foo', 'f', 'f.id');\n        self::assertEquals('f', $from->getAlias());\n        self::assertEquals('Foo', $from->getFrom());\n        self::assertEquals('f.id', $from->getIndexBy());\n\n        // Func\n        $func = new Func('MAX', ['f.id']);\n        self::assertEquals('MAX', $func->getName());\n        self::assertEquals(['f.id'], $func->getArguments());\n\n        // GroupBy\n        $group = new GroupBy(['foo DESC', 'bar ASC']);\n        self::assertEquals(['foo DESC', 'bar ASC'], $group->getParts());\n\n        // Join\n        $join = new Join(Join::INNER_JOIN, 'f.bar', 'b', Join::WITH, 'b.bar_id = 1', 'b.bar_id');\n        self::assertEquals(Join::INNER_JOIN, $join->getJoinType());\n        self::assertEquals(Join::WITH, $join->getConditionType());\n        self::assertEquals('b.bar_id = 1', $join->getCondition());\n        self::assertEquals('b.bar_id', $join->getIndexBy());\n        self::assertEquals('f.bar', $join->getJoin());\n        self::assertEquals('b', $join->getAlias());\n\n        // Literal\n        $literal = new Literal(['foo']);\n        self::assertEquals(['foo'], $literal->getParts());\n\n        // Math\n        $math = new Math(10, '+', 20);\n        self::assertEquals(10, $math->getLeftExpr());\n        self::assertEquals(20, $math->getRightExpr());\n        self::assertEquals('+', $math->getOperator());\n\n        // OrderBy\n        $order = new OrderBy('foo', 'DESC');\n        self::assertEquals(['foo DESC'], $order->getParts());\n\n        // Andx\n        $orx = new Orx(['foo = 1', 'bar = 2']);\n        self::assertEquals(['foo = 1', 'bar = 2'], $orx->getParts());\n\n        // Select\n        $select = new Select(['foo', 'bar']);\n        self::assertEquals(['foo', 'bar'], $select->getParts());\n    }\n\n    public function testAddEmpty(): void\n    {\n        $andExpr = $this->expr->andX();\n        $andExpr->add($this->expr->andX());\n\n        self::assertEquals(0, $andExpr->count());\n    }\n\n    public function testAddNull(): void\n    {\n        $andExpr = $this->expr->andX();\n        $andExpr->add(null);\n\n        self::assertEquals(0, $andExpr->count());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/FilterCollectionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Query\\Filter\\SQLFilter;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\OrmTestCase;\nuse InvalidArgumentException;\n\n/**\n * Test case for FilterCollection\n */\nclass FilterCollectionTest extends OrmTestCase\n{\n    private EntityManagerMock $em;\n\n    protected function setUp(): void\n    {\n        $this->em = $this->getTestEntityManager();\n        $this->em->getConfiguration()->addFilter('testFilter', MyFilter::class);\n    }\n\n    public function testEnable(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertCount(0, $filterCollection->getEnabledFilters());\n\n        $filter1 = $filterCollection->enable('testFilter');\n\n        $enabledFilters = $filterCollection->getEnabledFilters();\n\n        self::assertCount(1, $enabledFilters);\n        self::assertContainsOnly(MyFilter::class, $enabledFilters);\n\n        $filter2 = $filterCollection->disable('testFilter');\n        self::assertCount(0, $filterCollection->getEnabledFilters());\n        self::assertSame($filter1, $filter2);\n\n        $filter3 = $filterCollection->enable('testFilter');\n        self::assertNotSame($filter1, $filter3);\n\n        $filter4 = $filterCollection->suspend('testFilter');\n        self::assertSame($filter3, $filter4);\n\n        $filter5 = $filterCollection->enable('testFilter');\n        self::assertNotSame($filter4, $filter5);\n\n        self::assertCount(1, $enabledFilters);\n        self::assertContainsOnly(MyFilter::class, $enabledFilters);\n    }\n\n    public function testSuspend(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertCount(0, $filterCollection->getEnabledFilters());\n\n        $filter1 = $filterCollection->enable('testFilter');\n        self::assertCount(1, $filterCollection->getEnabledFilters());\n\n        $filter2 = $filterCollection->suspend('testFilter');\n        self::assertSame($filter1, $filter2);\n        self::assertCount(0, $filterCollection->getEnabledFilters());\n\n        $filter3 = $filterCollection->restore('testFilter');\n        self::assertSame($filter1, $filter3);\n        self::assertCount(1, $filterCollection->getEnabledFilters());\n    }\n\n    public function testRestoreFailure(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        $this->expectException(InvalidArgumentException::class);\n        $filterCollection->suspend('testFilter');\n    }\n\n    public function testHasFilter(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertTrue($filterCollection->has('testFilter'));\n        self::assertFalse($filterCollection->has('fakeFilter'));\n    }\n\n    public function testIsEnabled(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertFalse($filterCollection->isEnabled('testFilter'));\n\n        $filterCollection->enable('testFilter');\n\n        self::assertTrue($filterCollection->isEnabled('testFilter'));\n\n        $filterCollection->suspend('testFilter');\n\n        self::assertFalse($filterCollection->isEnabled('testFilter'));\n\n        $filterCollection->restore('testFilter');\n\n        self::assertTrue($filterCollection->isEnabled('testFilter'));\n\n        self::assertFalse($filterCollection->isEnabled('wrongFilter'));\n    }\n\n    public function testIsSuspended(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertFalse($filterCollection->isSuspended('testFilter'));\n\n        $filterCollection->enable('testFilter');\n\n        self::assertFalse($filterCollection->isSuspended('testFilter'));\n\n        $filterCollection->suspend('testFilter');\n\n        self::assertTrue($filterCollection->isSuspended('testFilter'));\n\n        $filterCollection->restore('testFilter');\n\n        self::assertFalse($filterCollection->isSuspended('testFilter'));\n\n        $filterCollection->disable('testFilter');\n\n        self::assertFalse($filterCollection->isSuspended('testFilter'));\n\n        self::assertFalse($filterCollection->isSuspended('wrongFilter'));\n    }\n\n    public function testGetFilterInvalidArgument(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $filterCollection = $this->em->getFilters();\n        $filterCollection->getFilter('testFilter');\n    }\n\n    public function testGetFilter(): void\n    {\n        $filterCollection = $this->em->getFilters();\n        $filterCollection->enable('testFilter');\n\n        self::assertInstanceOf(MyFilter::class, $filterCollection->getFilter('testFilter'));\n\n        $filterCollection->suspend('testFilter');\n\n        $this->expectException(InvalidArgumentException::class);\n        $filterCollection->getFilter('testFilter');\n    }\n\n    public function testHashing(): void\n    {\n        $filterCollection = $this->em->getFilters();\n\n        self::assertTrue($filterCollection->isClean());\n\n        $oldHash = $filterCollection->getHash();\n        $filterCollection->setFiltersStateDirty();\n\n        self::assertFalse($filterCollection->isClean());\n        self::assertSame($oldHash, $filterCollection->getHash());\n        self::assertTrue($filterCollection->isClean());\n\n        $filterCollection->enable('testFilter');\n\n        self::assertFalse($filterCollection->isClean());\n\n        $hash = $filterCollection->getHash();\n\n        self::assertNotSame($oldHash, $hash);\n        self::assertTrue($filterCollection->isClean());\n        self::assertSame($hash, $filterCollection->getHash());\n\n        $filterCollection->suspend('testFilter');\n\n        self::assertFalse($filterCollection->isClean());\n        self::assertSame($oldHash, $filterCollection->getHash());\n        self::assertTrue($filterCollection->isClean());\n\n        $filterCollection->restore('testFilter');\n\n        self::assertFalse($filterCollection->isClean());\n        self::assertSame($hash, $filterCollection->getHash());\n        self::assertTrue($filterCollection->isClean());\n\n        $filterCollection->disable('testFilter');\n\n        self::assertFalse($filterCollection->isClean());\n        self::assertSame($oldHash, $filterCollection->getHash());\n        self::assertTrue($filterCollection->isClean());\n    }\n}\n\nclass MyFilter extends SQLFilter\n{\n    public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string\n    {\n        // getParameter applies quoting automatically\n        return $targetTableAlias . '.id = ' . $this->getParameter('id');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/LanguageRecognitionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\AbstractQuery;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\ConcatFunction;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\Tests\\Mocks\\NullSqlWalker;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\n\nclass LanguageRecognitionTest extends OrmTestCase\n{\n    use VerifyDeprecations;\n\n    private EntityManagerInterface $entityManager;\n    private int $hydrationMode = AbstractQuery::HYDRATE_OBJECT;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    public function assertValidDQL(string $dql): void\n    {\n        $this->parseDql($dql);\n        $this->addToAssertionCount(1);\n    }\n\n    public function assertInvalidDQL(string $dql): void\n    {\n        $this->expectException(QueryException::class);\n        $this->parseDql($dql);\n    }\n\n    /** @phpstan-param array<string, mixed> $hints */\n    public function parseDql(string $dql, array $hints = []): ParserResult\n    {\n        $query = $this->entityManager->createQuery($dql);\n        $query->setDQL($dql);\n        $query->setHydrationMode($this->hydrationMode);\n\n        foreach ($hints as $key => $value) {\n            $query->setHint($key, $value);\n        }\n\n        $parser = new Parser($query);\n\n        // We do NOT test SQL output here. That only unnecessarily slows down the tests!\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, NullSqlWalker::class);\n\n        return $parser->parse();\n    }\n\n    public function testEmptyQueryString(): void\n    {\n        $this->assertInvalidDQL('');\n    }\n\n    public function testPlainFromClauseWithAlias(): void\n    {\n        $this->assertValidDQL('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSelectSingleComponentWithAsterisk(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    #[DataProvider('invalidDQL')]\n    public function testRejectsInvalidDQL(string $dql): void\n    {\n        $this->expectException(QueryException::class);\n\n        $this->entityManager->getConfiguration()->setEntityNamespaces(\n            [\n                'Unknown' => 'Unknown',\n                'CMS' => 'Doctrine\\Tests\\Models\\CMS',\n            ],\n        );\n\n        $this->parseDql($dql);\n    }\n\n    /** @phpstan-return list<array{string}> */\n    public static function invalidDQL(): array\n    {\n        return [\n\n            ['SELECT \\'foo\\' AS foo\\bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'],\n            /* Checks for invalid IdentificationVariables and AliasIdentificationVariables */\n            ['SELECT \\foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser \\foo'],\n            ['SELECT foo\\ FROM Doctrine\\Tests\\Models\\CMS\\CmsUser foo\\\\'],\n            ['SELECT foo\\bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser foo\\bar'],\n\n            /* Checks for invalid AbstractSchemaName */\n            ['SELECT u FROM UnknownClass u'],  // unknown\n            ['SELECT u FROM Unknown\\Class u'], // unknown with namespace\n            ['SELECT u FROM \\Unknown\\Class u'], // unknown, leading backslash\n            ['SELECT u FROM Unknown\\\\\\\\Class u'], // unknown, syntactically bogus (duplicate \\\\)\n            ['SELECT u FROM Unknown\\Class\\ u'], // unknown, syntactically bogus (trailing \\)\n            ['SELECT u FROM Doctrine\\Tests\\Models\\CMS\\\\\\\\CmsUser u'], // syntactically bogus (duplicate \\\\)array('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser\\ u'), // syntactically bogus (trailing \\)\n\n            /* Checks for invalid AliasResultVariable */\n            ['SELECT \\'foo\\' AS \\foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'],\n            ['SELECT \\'foo\\' AS \\foo\\bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'],\n            ['SELECT \\'foo\\' AS foo\\ FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'],\n            ['SELECT \\'foo\\' AS foo\\\\\\\\bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u'],\n\n            ['0'],\n        ];\n    }\n\n    public function testSelectSingleComponentWithMultipleColumns(): void\n    {\n        $this->assertValidDQL('SELECT u.name, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSelectMultipleComponentsUsingMultipleFrom(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE u = p.user');\n    }\n\n    public function testSelectMultipleComponentsWithAsterisk(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.phonenumbers p');\n    }\n\n    public function testSelectDistinctIsSupported(): void\n    {\n        $this->assertValidDQL('SELECT DISTINCT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testAggregateFunctionInSelect(): void\n    {\n        $this->assertValidDQL('SELECT COUNT(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testMultipleParenthesisInSelect(): void\n    {\n        $this->assertValidDQL('SELECT (((u.id))) as v FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testDuplicatedAliasInAggregateFunction(): void\n    {\n        $this->assertInvalidDQL('SELECT COUNT(u.id) AS num, SUM(u.id) AS num FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testAggregateFunctionWithDistinctInSelect(): void\n    {\n        $this->assertValidDQL('SELECT COUNT(DISTINCT u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testFunctionalExpressionsSupportedInWherePart(): void\n    {\n        $this->assertValidDQL(\"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE TRIM(u.name) = 'someone'\");\n    }\n\n    public function testTrimFalsyString(): void\n    {\n        $this->assertValidDQL(\"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE TRIM('0' FROM u.name) = 'someone'\");\n    }\n\n    public function testArithmeticExpressionsSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');\n    }\n\n    public function testInExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id IN (1, 2)');\n    }\n\n    public function testInExpressionWithoutSpacesSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id IN (1,2,3)');\n    }\n\n    public function testNotInExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id NOT IN (1)');\n    }\n\n    public function testInExpressionWithSingleValuedAssociationPathExpression(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u WHERE u.avatar IN (?1, ?2)');\n    }\n\n    public function testInvalidInExpressionWithCollectionValuedAssociationPathExpression(): void\n    {\n        $this->assertInvalidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.phonenumbers IN (?1, ?2)');\n    }\n\n    public function testInstanceOfExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyEmployee');\n    }\n\n    public function testInstanceOfExpressionWithInputParamSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF ?1');\n    }\n\n    public function testNotInstanceOfExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u NOT INSTANCE OF ?1');\n    }\n\n    public function testExistsExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = 1234)');\n    }\n\n    public function testNotExistsExpressionSupportedInWherePart(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = 1234)');\n    }\n\n    public function testAggregateFunctionInHavingClause(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.phonenumbers p HAVING COUNT(p.phonenumber) > 2');\n        $this->assertValidDQL(\"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'romanb'\");\n    }\n\n    public function testLeftJoin(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.phonenumbers p');\n    }\n\n    public function testJoin(): void\n    {\n        $this->assertValidDQL('SELECT u,p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.phonenumbers p');\n    }\n\n    public function testInnerJoin(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.phonenumbers p');\n    }\n\n    public function testMultipleLeftJoin(): void\n    {\n        $this->assertValidDQL('SELECT u, a, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a LEFT JOIN u.phonenumbers p');\n    }\n\n    public function testMultipleInnerJoin(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a INNER JOIN u.phonenumbers p');\n    }\n\n    public function testMixingOfJoins(): void\n    {\n        $this->assertValidDQL('SELECT u.name, a.topic, p.phonenumber FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a LEFT JOIN u.phonenumbers p');\n    }\n\n    public function testJoinClassPathUsingON(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a ON a.user = u.id');\n    }\n\n    #[IgnoreDeprecations]\n    public function testJoinClassPathUsingWITH(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/12192');\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a WITH a.user = u.id');\n    }\n\n    #[Group('DDC-3701')]\n    public function testJoinClassPathUsingWHERE(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE a.user = u.id');\n    }\n\n    #[Group('DDC-3701')]\n    public function testDDC3701WHEREIsNotWITH(): void\n    {\n        $this->assertInvalidDQL('SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c JOIN Doctrine\\Tests\\Models\\Company\\CompanyEmployee e WHERE e.id = c.salesPerson WHERE c.completed = true');\n    }\n\n    public function testOrderBySingleColumn(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.name');\n    }\n\n    public function testOrderBySingleColumnAscending(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.name ASC');\n    }\n\n    public function testOrderBySingleColumnDescending(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.name DESC');\n    }\n\n    public function testOrderByMultipleColumns(): void\n    {\n        $this->assertValidDQL('SELECT u.name, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username DESC, u.name DESC');\n    }\n\n    public function testSubselectInInExpression(): void\n    {\n        $this->assertValidDQL(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id NOT IN (SELECT u2.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 WHERE u2.name = 'zYne')\");\n    }\n\n    public function testSubselectInSelectPart(): void\n    {\n        $this->assertValidDQL(\"SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = 'jon'\");\n    }\n\n    public function testArithmeticExpressionInSelectPart(): void\n    {\n        $this->assertValidDQL('SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testArithmeticExpressionInSubselectPart(): void\n    {\n        $this->assertValidDQL(\"SELECT (SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) value FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = 'jon'\");\n    }\n\n    public function testArithmeticExpressionWithParenthesisInSubselectPart(): void\n    {\n        $this->assertValidDQL(\"SELECT (SELECT (SUM(u.id) / COUNT(u.id)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) value FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = 'jon'\");\n    }\n\n    #[Group('DDC-1079')]\n    public function testSelectLiteralInSubselect(): void\n    {\n        $this->assertValidDQL('SELECT (SELECT 1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) value FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n        $this->assertValidDQL('SELECT (SELECT 0 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) value FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    #[Group('DDC-1077')]\n    public function testConstantValueInSelect(): void\n    {\n        $this->assertValidDQL(\"SELECT u.name, 'foo' AS bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\");\n    }\n\n    public function testDuplicateAliasInSubselectPart(): void\n    {\n        $this->assertInvalidDQL(\"SELECT (SELECT SUM(u.id) / COUNT(u.id) AS foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2) foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = 'jon'\");\n    }\n\n    public function testPositionalInputParameter(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1');\n    }\n\n    public function testNamedInputParameter(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :id');\n    }\n\n    public function testJoinConditionOverrideNotSupported(): void\n    {\n        $this->assertInvalidDQL(\"SELECT u.name, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.phonenumbers p ON p.phonenumber = '123 123'\");\n    }\n\n    public function testIndexByClauseWithOneComponent(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INDEX BY u.id');\n    }\n\n    public function testIndexBySupportsJoins(): void\n    {\n        $this->assertValidDQL('SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a INDEX BY a.id'); // INDEX BY is now referring to articles\n    }\n\n    public function testIndexBySupportsJoins2(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INDEX BY u.id LEFT JOIN u.phonenumbers p INDEX BY p.phonenumber');\n    }\n\n    public function testBetweenExpressionSupported(): void\n    {\n        $this->assertValidDQL(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name BETWEEN 'jepso' AND 'zYne'\");\n    }\n\n    public function testNotBetweenExpressionSupported(): void\n    {\n        $this->assertValidDQL(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'\");\n    }\n\n    public function testLikeExpression(): void\n    {\n        $this->assertValidDQL(\"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name LIKE 'z%'\");\n    }\n\n    public function testNotLikeExpression(): void\n    {\n        $this->assertValidDQL(\"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name NOT LIKE 'z%'\");\n    }\n\n    public function testLikeExpressionWithCustomEscapeCharacter(): void\n    {\n        $this->assertValidDQL(\"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'\");\n    }\n\n    public function testFieldComparisonWithoutAlias(): void\n    {\n        $this->assertInvalidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE id = 1');\n    }\n\n    public function testDuplicatedAliasDeclaration(): void\n    {\n        $this->assertInvalidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles u WHERE u.id = 1');\n    }\n\n    public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression(): void\n    {\n        // This should be allowed because avatar is a single-value association.\n        // SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ?\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u JOIN u.avatar a WHERE a.id = ?1');\n    }\n\n    public function testImplicitJoinInWhereOnCollectionValuedPathExpression(): void\n    {\n        // This should be forbidden, because articles is a collection\n        $this->assertInvalidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.articles a WHERE a.title = ?');\n    }\n\n    public function testInvalidSyntaxIsRejected(): void\n    {\n        $this->assertInvalidDQL('FOOBAR CmsUser');\n        $this->assertInvalidDQL('DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser.articles');\n        $this->assertInvalidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.articles.comments');\n\n        // Currently UNDEFINED OFFSET error\n        $this->assertInvalidDQL('SELECT c FROM CmsUser.articles.comments c');\n    }\n\n    public function testUpdateWorksWithOneField(): void\n    {\n        $this->assertValidDQL(\"UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = 'someone'\");\n    }\n\n    public function testUpdateWorksWithMultipleFields(): void\n    {\n        $this->assertValidDQL(\"UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = 'someone', u.username = 'some'\");\n    }\n\n    public function testUpdateSupportsConditions(): void\n    {\n        $this->assertValidDQL(\"UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = 'someone' WHERE u.id = 5\");\n    }\n\n    public function testDeleteAll(): void\n    {\n        $this->assertValidDQL('DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testDeleteWithCondition(): void\n    {\n        $this->assertValidDQL('DELETE FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = 3');\n    }\n\n    /**\n     * The main use case for this generalized style of join is when a join condition\n     * does not involve a foreign key relationship that is mapped to an entity relationship.\n     */\n    public function testImplicitJoinWithCartesianProductAndConditionInWhere(): void\n    {\n        $this->assertValidDQL('SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE u.name = a.topic');\n    }\n\n    public function testAllExpressionWithCorrelatedSubquery(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id > ALL (SELECT u2.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 WHERE u2.name = u.name)');\n    }\n\n    public function testCustomJoinsAndWithKeywordSupported(): void\n    {\n        $this->assertValidDQL('SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.phonenumbers p WITH p.phonenumber = 123 WHERE u.id = 1');\n    }\n\n    public function testAnyExpressionWithCorrelatedSubquery(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id > ANY (SELECT u2.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 WHERE u2.name = u.name)');\n    }\n\n    public function testSomeExpressionWithCorrelatedSubquery(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id > SOME (SELECT u2.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 WHERE u2.name = u.name)');\n    }\n\n    public function testArithmeticExpressionWithoutParenthesisInWhereClause(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE SIZE(u.phonenumbers) + 1 > 10');\n    }\n\n    public function testMemberOfExpression(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE :param MEMBER OF u.phonenumbers');\n        //$this->assertValidDQL(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE 'Joe' MEMBER OF u.nicknames\");\n    }\n\n    public function testSizeFunction(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE SIZE(u.phonenumbers) > 1');\n    }\n\n    public function testEmptyCollectionComparisonExpression(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.phonenumbers IS EMPTY');\n    }\n\n    public function testSingleValuedAssociationFieldInWhere(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.address = ?1');\n        $this->assertValidDQL('SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.user = ?1');\n    }\n\n    public function testBooleanLiteralInWhere(): void\n    {\n        $this->assertValidDQL('SELECT b FROM Doctrine\\Tests\\Models\\Generic\\BooleanModel b WHERE b.booleanField = true');\n    }\n\n    public function testSubqueryInSelectExpression(): void\n    {\n        $this->assertValidDQL('select u, (select max(p.phonenumber) from Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p) maxId from Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testUsageOfQComponentOutsideSubquery(): void\n    {\n        $this->assertInvalidDQL('select u, (select max(p.phonenumber) from Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p) maxId from Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE p.user = ?1');\n    }\n\n    public function testUnknownAbstractSchemaName(): void\n    {\n        $this->assertInvalidDQL('SELECT u FROM UnknownClassName u');\n    }\n\n    public function testCorrectPartialObjectLoad(): void\n    {\n        $this->assertValidDQL('SELECT PARTIAL u.{id,name} FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testIncorrectPartialObjectLoadBecauseOfMissingIdentifier(): void\n    {\n        $this->assertInvalidDQL('SELECT PARTIAL u.{name} FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testScalarExpressionInSelect(): void\n    {\n        $this->assertValidDQL('SELECT u, 42 + u.id AS someNumber FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testInputParameterInSelect(): void\n    {\n        $this->assertValidDQL('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    #[Group('DDC-1091')]\n    public function testCustomFunctionsReturningStringInStringPrimary(): void\n    {\n        $this->entityManager->getConfiguration()->addCustomStringFunction('CC', ConcatFunction::class);\n\n        $this->assertValidDQL(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE CC('%', u.name) LIKE '%foo%'\");\n    }\n\n    #[Group('DDC-505')]\n    public function testDQLKeywordInJoinIsAllowed(): void\n    {\n        $this->assertValidDQL('SELECT u FROM ' . __NAMESPACE__ . '\\DQLKeywordsModelUser u JOIN u.group g');\n    }\n\n    #[Group('DDC-505')]\n    public function testDQLKeywordInConditionIsAllowed(): void\n    {\n        $this->assertValidDQL('SELECT g FROM ' . __NAMESPACE__ . '\\DQLKeywordsModelGroup g WHERE g.from=0');\n    }\n\n    public function testInverseSideSingleValuedAssociationPathNotAllowed(): void\n    {\n        self::markTestSkipped('The exception is currently thrown in the SQLWalker, not earlier.');\n        $this->assertInvalidDQL('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.address = ?1');\n    }\n\n    #[Group('DDC-617')]\n    public function testSelectOnlyNonRootEntityAlias(): void\n    {\n        $this->assertInvalidDQL('SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.groups g');\n    }\n\n    #[Group('DDC-1108')]\n    public function testInputParameterSingleChar(): void\n    {\n        $this->assertValidDQL('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = :q');\n    }\n\n    #[Group('DDC-1053')]\n    public function testGroupBy(): void\n    {\n        $this->assertValidDQL('SELECT g.id, count(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g JOIN g.users u GROUP BY g.id');\n    }\n\n    #[Group('DDC-1053')]\n    public function testGroupByIdentificationVariable(): void\n    {\n        $this->assertValidDQL('SELECT g, count(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g JOIN g.users u GROUP BY g');\n    }\n\n    #[Group('DDC-1053')]\n    public function testGroupByUnknownIdentificationVariable(): void\n    {\n        $this->assertInvalidDQL('SELECT g, count(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g JOIN g.users u GROUP BY m');\n    }\n\n    #[Group('DDC-117')]\n    public function testSizeOfForeignKeyOneToManyPrimaryKeyEntity(): void\n    {\n        $this->assertValidDQL('SELECT a, t FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Article a JOIN a.translations t WHERE SIZE(a.translations) > 0');\n    }\n\n    #[Group('DDC-117')]\n    public function testSizeOfForeignKeyManyToManyPrimaryKeyEntity(): void\n    {\n        $this->assertValidDQL('SELECT e, t FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Editor e JOIN e.reviewingTranslations t WHERE SIZE(e.reviewingTranslations) > 0');\n    }\n\n    public function testCaseSupportContainingNullIfExpression(): void\n    {\n        $this->assertValidDQL('SELECT u.id, NULLIF(u.name, u.name) AS shouldBeNull FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testCaseSupportContainingCoalesceExpression(): void\n    {\n        $this->assertValidDQL(\"select COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\");\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportIsNullExpression(): void\n    {\n        $this->assertValidDQL('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING u.username IS NULL');\n    }\n\n    #[Group('DDC-3085')]\n    public function testHavingSupportResultVariableInNullComparisonExpression(): void\n    {\n        $this->assertValidDQL('SELECT u AS user, SUM(a.id) AS score FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN Doctrine\\Tests\\Models\\CMS\\CmsAddress a ON a.user = u GROUP BY u HAVING score IS NOT NULL AND score >= 5');\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportLikeExpression(): void\n    {\n        $this->assertValidDQL(\"SELECT _u.id, count(_articles) as uuuu FROM Doctrine\\Tests\\Models\\CMS\\CmsUser _u LEFT JOIN _u.articles _articles GROUP BY _u HAVING uuuu LIKE '3'\");\n    }\n\n    #[Group('DDC-3018')]\n    public function testNewLiteralExpression(): void\n    {\n        $this->assertValidDQL('SELECT new ' . __NAMESPACE__ . \"\\\\DummyStruct(u.id, 'foo', 1, true) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\");\n    }\n\n    #[Group('DDC-3075')]\n    public function testNewLiteralWithSubselectExpression(): void\n    {\n        $this->assertValidDQL('SELECT new ' . __NAMESPACE__ . \"\\\\DummyStruct(u.id, 'foo', (SELECT 1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser su), true) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\");\n    }\n\n    public function testStringPrimaryAcceptsAggregateExpression(): void\n    {\n        $this->assertValidDQL(\n            'SELECT CONCAT(a.topic, MAX(a.version)) last FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a GROUP BY a',\n        );\n    }\n}\n\n#[Entity]\nclass DQLKeywordsModelUser\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[OneToOne(targetEntity: 'DQLKeywordsModelGroup')]\n    private DQLKeywordsModelGroup $group;\n}\n\n#[Entity]\nclass DQLKeywordsModelGroup\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    #[Column]\n    private string $from;\n}\n\nclass DummyStruct\n{\n    public function __construct($id, $arg1, $arg2, $arg3)\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/LexerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\Common\\Lexer\\Token;\nuse Doctrine\\ORM\\Query\\Lexer;\nuse Doctrine\\ORM\\Query\\TokenType;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass LexerTest extends OrmTestCase\n{\n    #[DataProvider('provideTokens')]\n    public function testScannerRecognizesTokens($type, $value): void\n    {\n        $lexer = new Lexer($value);\n\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n\n        self::assertEquals($type, $token->type);\n        self::assertEquals($value, $token->value);\n    }\n\n    public function testScannerRecognizesTerminalString(): void\n    {\n        /*\n         * \"all\" looks like an identifier, but in fact it's a reserved word\n         * (a terminal string). It's up to the parser to accept it as an identifier\n         * (with its literal value) when appropriate.\n         */\n\n        $lexer = new Lexer('all');\n\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n\n        self::assertEquals(TokenType::T_ALL, $token->type);\n    }\n\n    public function testScannerRecognizesDecimalInteger(): void\n    {\n        $lexer = new Lexer('1234');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_INTEGER, $token->type);\n        self::assertEquals(1234, $token->value);\n    }\n\n    public function testScannerRecognizesFloat(): void\n    {\n        $lexer = new Lexer('1.234');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertEquals(1.234, $token->value);\n    }\n\n    public function testScannerRecognizesFloatWithExponent(): void\n    {\n        $lexer = new Lexer('1.2e3');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertEquals(1.2e3, $token->value);\n    }\n\n    public function testScannerRecognizesFloatWithExponent2(): void\n    {\n        $lexer = new Lexer('0.2e3');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertEquals(.2e3, $token->value);\n    }\n\n    public function testScannerRecognizesFloatWithNegativeExponent(): void\n    {\n        $lexer = new Lexer('7E-10');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertEquals(7E-10, $token->value);\n    }\n\n    public function testScannerRecognizesFloatBig(): void\n    {\n        $lexer = new Lexer('123456789.01');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertEquals(1.2345678901e8, $token->value);\n    }\n\n    public function testScannerRecognizesFloatContainingWhitespace(): void\n    {\n        $lexer = new Lexer('-   1.234e2');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_MINUS, $token->type);\n        self::assertEquals('-', $token->value);\n\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_FLOAT, $token->type);\n        self::assertNotEquals(-1.234e2, $token->value);\n        self::assertEquals(1.234e2, $token->value);\n    }\n\n    public function testScannerRecognizesStringContainingWhitespace(): void\n    {\n        $lexer = new Lexer(\"'This is a string.'\");\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_STRING, $token->type);\n        self::assertEquals('This is a string.', $token->value);\n    }\n\n    public function testScannerRecognizesStringContainingSingleQuotes(): void\n    {\n        $lexer = new Lexer(\"'abc''defg'''\");\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_STRING, $token->type);\n        self::assertEquals(\"abc'defg'\", $token->value);\n    }\n\n    public function testScannerRecognizesInputParameter(): void\n    {\n        $lexer = new Lexer('?1');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);\n        self::assertEquals('?1', $token->value);\n    }\n\n    public function testScannerRecognizesNamedInputParameter(): void\n    {\n        $lexer = new Lexer(':name');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);\n        self::assertEquals(':name', $token->value);\n    }\n\n    public function testScannerRecognizesNamedInputParameterStartingWithUnderscore(): void\n    {\n        $lexer = new Lexer(':_name');\n        $lexer->moveNext();\n        $token = $lexer->lookahead;\n        self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);\n        self::assertEquals(':_name', $token->value);\n    }\n\n    public function testScannerTokenizesASimpleQueryCorrectly(): void\n    {\n        $dql   = \"SELECT u FROM My\\Namespace\\User u WHERE u.name = 'Jack O''Neil'\";\n        $lexer = new Lexer($dql);\n\n        $tokens = [\n            new Token('SELECT', TokenType::T_SELECT, 0),\n            new Token('u', TokenType::T_IDENTIFIER, 7),\n            new Token('FROM', TokenType::T_FROM, 9),\n            new Token('My\\Namespace\\User', TokenType::T_FULLY_QUALIFIED_NAME, 14),\n            new Token('u', TokenType::T_IDENTIFIER, 32),\n            new Token('WHERE', TokenType::T_WHERE, 34),\n            new Token('u', TokenType::T_IDENTIFIER, 40),\n            new Token('.', TokenType::T_DOT, 41),\n            new Token('name', TokenType::T_IDENTIFIER, 42),\n            new Token('=', TokenType::T_EQUALS, 47),\n            new Token(\"Jack O'Neil\", TokenType::T_STRING, 49),\n        ];\n\n        foreach ($tokens as $expected) {\n            $lexer->moveNext();\n            $actual = $lexer->lookahead;\n            self::assertEquals($expected->value, $actual->value);\n            self::assertEquals($expected->type, $actual->type);\n            self::assertEquals($expected->position, $actual->position);\n        }\n\n        self::assertFalse($lexer->moveNext());\n    }\n\n    /** @phpstan-return list<array{int, string}> */\n    public static function provideTokens(): array\n    {\n        return [\n            [TokenType::T_IDENTIFIER, 'u'], // one char\n            [TokenType::T_IDENTIFIER, 'someIdentifier'],\n            [TokenType::T_IDENTIFIER, 's0m31d3nt1f13r'], // including digits\n            [TokenType::T_IDENTIFIER, 'some_identifier'], // including underscore\n            [TokenType::T_IDENTIFIER, '_some_identifier'], // starts with underscore\n            [TokenType::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test)\n            [TokenType::T_FULLY_QUALIFIED_NAME, 'Some\\Class'], // DQL class reference\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/NativeQueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse DateTime;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\OrmTestCase;\n\nclass NativeQueryTest extends OrmTestCase\n{\n    /** @var EntityManagerMock */\n    protected $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    public function testValuesAreNotBeingResolvedForSpecifiedParameterTypes(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n\n        $this->entityManager->setUnitOfWork($unitOfWork);\n\n        $unitOfWork\n            ->expects(self::never())\n            ->method('getSingleIdentifierValue');\n\n        $rsm = new ResultSetMapping();\n\n        $query = $this->entityManager->createNativeQuery('SELECT d.* FROM date_time_model d WHERE d.datetime = :value', $rsm);\n\n        $query->setParameter('value', new DateTime(), Types::DATETIME_MUTABLE);\n\n        self::assertEmpty($query->getResult());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/ParameterTypeInfererTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse DateInterval;\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\nuse Doctrine\\Tests\\Models\\Enums\\AccessLevel;\nuse Doctrine\\Tests\\Models\\Enums\\UserStatus;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass ParameterTypeInfererTest extends OrmTestCase\n{\n    /** @phpstan-return Generator<string, array{mixed, (ParameterType::*|ArrayParameterType::*|string)}> */\n    public static function providerParameterTypeInferer(): Generator\n    {\n        yield 'integer' => [1, Types::INTEGER];\n        yield 'string' => ['bar', ParameterType::STRING];\n        yield 'numeric_string' => ['1', ParameterType::STRING];\n        yield 'datetime_object' => [new DateTime(), Types::DATETIME_MUTABLE];\n        yield 'datetime_immutable_object' => [new DateTimeImmutable(), Types::DATETIME_IMMUTABLE];\n        yield 'date_interval_object' => [new DateInterval('P1D'), Types::DATEINTERVAL];\n        yield 'array_of_int' => [[2], ArrayParameterType::INTEGER];\n        yield 'array_of_string' => [['foo'], ArrayParameterType::STRING];\n        yield 'array_of_numeric_string' => [['1', '2'], ArrayParameterType::STRING];\n        yield 'empty_array' => [[], ArrayParameterType::STRING];\n        yield 'boolean' => [true, Types::BOOLEAN];\n        yield 'int_backed_enum' => [AccessLevel::Admin, Types::INTEGER];\n        yield 'string_backed_enum' => [UserStatus::Active, Types::STRING];\n        yield 'array_of_int_backed_enum' => [[AccessLevel::Admin], ArrayParameterType::INTEGER];\n        yield 'array_of_string_backed_enum' => [[UserStatus::Active], ArrayParameterType::STRING];\n    }\n\n    #[DataProvider('providerParameterTypeInferer')]\n    public function testParameterTypeInferer(mixed $value, ParameterType|ArrayParameterType|int|string $expected): void\n    {\n        self::assertEquals($expected, ParameterTypeInferer::inferType($value));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/ParserResultTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\nuse LogicException;\nuse PHPUnit\\Framework\\TestCase;\n\nclass ParserResultTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    /** @var ParserResult */\n    public $parserResult;\n\n    protected function setUp(): void\n    {\n        $this->parserResult = new ParserResult();\n    }\n\n    public function testGetRsm(): void\n    {\n        self::assertInstanceOf(ResultSetMapping::class, $this->parserResult->getResultSetMapping());\n    }\n\n    public function testItThrowsWhenAttemptingToAccessTheExecutorBeforeItIsSet(): void\n    {\n        $this->expectException(LogicException::class);\n        $this->expectExceptionMessage(\n            'Executor not set yet. Call Doctrine\\ORM\\Query\\ParserResult::setSqlExecutor() first.',\n        );\n\n        $this->parserResult->getSqlExecutor();\n    }\n\n    public function testSetGetSqlExecutor(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11188');\n\n        $executor = $this->createMock(AbstractSqlExecutor::class);\n        $this->parserResult->setSqlExecutor($executor);\n        self::assertSame($executor, $this->parserResult->getSqlExecutor());\n    }\n\n    public function testGetSqlParameterPosition(): void\n    {\n        $this->parserResult->addParameterMapping(1, 1);\n        $this->parserResult->addParameterMapping(1, 2);\n        self::assertEquals([1, 2], $this->parserResult->getSqlParameterPositions(1));\n    }\n\n    public function testGetParameterMappings(): void\n    {\n        self::assertIsArray($this->parserResult->getParameterMappings());\n\n        $this->parserResult->addParameterMapping(1, 1);\n        $this->parserResult->addParameterMapping(1, 2);\n        self::assertEquals([1 => [1, 2]], $this->parserResult->getParameterMappings());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/ParserTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\TokenType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse stdClass;\n\nclass ParserTest extends OrmTestCase\n{\n    #[Group('DDC-3715')]\n    public function testAbstractSchemaNameSupportsFQCN(): void\n    {\n        $parser = $this->createParser(CmsUser::class);\n\n        self::assertEquals(CmsUser::class, $parser->AbstractSchemaName());\n    }\n\n    #[Group('DDC-3715')]\n    public function testAbstractSchemaNameSupportsClassnamesWithLeadingBackslash(): void\n    {\n        $parser = $this->createParser('\\\\' . CmsUser::class);\n\n        self::assertEquals('\\\\' . CmsUser::class, $parser->AbstractSchemaName());\n    }\n\n    #[Group('DDC-3715')]\n    public function testAbstractSchemaNameSupportsIdentifier(): void\n    {\n        $parser = $this->createParser(stdClass::class);\n\n        self::assertEquals(stdClass::class, $parser->AbstractSchemaName());\n    }\n\n    #[DataProvider('validMatches')]\n    #[Group('DDC-3701')]\n    #[DoesNotPerformAssertions]\n    public function testMatch(TokenType $expectedToken, string $inputString): void\n    {\n        $parser = $this->createParser($inputString);\n\n        $parser->match($expectedToken); // throws exception if not matched\n    }\n\n    #[DataProvider('invalidMatches')]\n    #[Group('DDC-3701')]\n    public function testMatchFailure(TokenType $expectedToken, string $inputString): void\n    {\n        $this->expectException(QueryException::class);\n\n        $parser = $this->createParser($inputString);\n\n        $parser->match($expectedToken);\n    }\n\n    /** @phpstan-return list<array{int, string}> */\n    public static function validMatches(): array\n    {\n        /*\n         * This only covers the special case handling in the Parser that some\n         * tokens that are *not* T_IDENTIFIER are accepted as well when matching\n         * identifiers.\n         *\n         * The basic checks that tokens are classified correctly do not belong here\n         * but in LexerTest.\n         */\n        return [\n            [TokenType::T_WHERE, 'where'], // keyword\n            [TokenType::T_DOT, '.'], // token that cannot be an identifier\n            [TokenType::T_IDENTIFIER, 'someIdentifier'],\n            [TokenType::T_IDENTIFIER, 'from'], // also a terminal string (the \"FROM\" keyword) as in DDC-505\n            [TokenType::T_IDENTIFIER, 'comma'],\n            // not even a terminal string, but the name of a constant in the Lexer (whitebox test)\n        ];\n    }\n\n    /** @phpstan-return list<array{int, string}> */\n    public static function invalidMatches(): array\n    {\n        return [\n            [TokenType::T_DOT, 'ALL'], // ALL is a terminal string (reserved keyword) and also possibly an identifier\n            [TokenType::T_DOT, ','], // \",\" is a token on its own, but cannot be used as identifier\n            [TokenType::T_WHERE, 'WITH'], // as in DDC-3697\n            [TokenType::T_WHERE, '.'],\n\n            // The following are qualified or aliased names and must not be accepted where only an Identifier is expected\n            [TokenType::T_IDENTIFIER, '\\\\Some\\\\Class'],\n            [TokenType::T_IDENTIFIER, 'Some\\\\Class'],\n        ];\n    }\n\n    /**\n     * PHP 7.4 would fail with Notice: Trying to access array offset on value of type null.\n     *\n     * @see https://github.com/doctrine/orm/pull/7934\n     */\n    #[Group('GH7934')]\n    public function testNullLookahead(): void\n    {\n        $query = new Query($this->getTestEntityManager());\n        $query->setDQL('SELECT CURRENT_TIMESTAMP()');\n\n        $parser = new Parser($query);\n\n        $this->expectException(QueryException::class);\n        $parser->match(TokenType::T_SELECT);\n    }\n\n    private function createParser(string $dql): Parser\n    {\n        $query = new Query($this->getTestEntityManager());\n        $query->setDQL($dql);\n\n        $parser = new Parser($query);\n        $parser->getLexer()->moveNext();\n\n        return $parser;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/QueryExpressionVisitorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Expr\\Comparison as CriteriaComparison;\nuse Doctrine\\Common\\Collections\\Expr\\Value;\nuse Doctrine\\Common\\Collections\\ExpressionBuilder as CriteriaBuilder;\nuse Doctrine\\ORM\\Query\\Expr as QueryBuilder;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\QueryExpressionVisitor;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Test for QueryExpressionVisitor\n */\nclass QueryExpressionVisitorTest extends TestCase\n{\n    private QueryExpressionVisitor $visitor;\n\n    protected function setUp(): void\n    {\n        $this->visitor = new QueryExpressionVisitor(['o', 'p']);\n    }\n\n    #[DataProvider('comparisonData')]\n    public function testWalkComparison(CriteriaComparison $criteriaExpr, QueryBuilder\\Comparison|QueryBuilder\\Func|string $queryExpr, Parameter|null $parameter = null): void\n    {\n        self::assertEquals($queryExpr, $this->visitor->walkComparison($criteriaExpr));\n        if ($parameter) {\n            self::assertEquals(new ArrayCollection([$parameter]), $this->visitor->getParameters());\n        }\n    }\n\n    /**\n     * @phpstan-return list<array{\n     *                   0: CriteriaComparison,\n     *                   1: QueryBuilder\\Comparison|QueryBuilder\\Func|string,\n     *                   2?: Parameter,\n     *               }>\n     */\n    public static function comparisonData(): array\n    {\n        $cb = new CriteriaBuilder();\n        $qb = new QueryBuilder();\n\n        return [\n            [$cb->eq('field', 'value'), $qb->eq('o.field', ':field'), new Parameter('field', 'value')],\n            [$cb->neq('field', 'value'), $qb->neq('o.field', ':field'), new Parameter('field', 'value')],\n            [$cb->eq('field', null), $qb->isNull('o.field')],\n            [$cb->neq('field', null), $qb->isNotNull('o.field')],\n            [$cb->isNull('field'), $qb->isNull('o.field')],\n\n            [$cb->gt('field', 'value'), $qb->gt('o.field', ':field'), new Parameter('field', 'value')],\n            [$cb->gte('field', 'value'), $qb->gte('o.field', ':field'), new Parameter('field', 'value')],\n            [$cb->lt('field', 'value'), $qb->lt('o.field', ':field'), new Parameter('field', 'value')],\n            [$cb->lte('field', 'value'), $qb->lte('o.field', ':field'), new Parameter('field', 'value')],\n\n            [$cb->in('field', ['value']), $qb->in('o.field', ':field'), new Parameter('field', ['value'])],\n            [$cb->notIn('field', ['value']), $qb->notIn('o.field', ':field'), new Parameter('field', ['value'])],\n\n            [$cb->contains('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value%')],\n            [$cb->memberOf(':field', 'o.field'), $qb->isMemberOf(':field', 'o.field')],\n\n            [$cb->startsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', 'value%')],\n            [$cb->endsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value')],\n\n            // Test parameter conversion\n            [$cb->eq('object.field', 'value'), $qb->eq('o.object.field', ':object_field'), new Parameter('object_field', 'value')],\n\n            // Test alternative rootAlias\n            [$cb->eq('p.field', 'value'), $qb->eq('p.field', ':p_field'), new Parameter('p_field', 'value')],\n            [$cb->eq('p.object.field', 'value'), $qb->eq('p.object.field', ':p_object_field'), new Parameter('p_object_field', 'value')],\n        ];\n    }\n\n    public function testWalkAndCompositeExpression(): void\n    {\n        $cb   = new CriteriaBuilder();\n        $expr = $this->visitor->walkCompositeExpression(\n            $cb->andX(\n                $cb->eq('foo', 1),\n                $cb->eq('bar', 1),\n            ),\n        );\n\n        self::assertInstanceOf(QueryBuilder\\Andx::class, $expr);\n        self::assertCount(2, $expr->getParts());\n    }\n\n    public function testWalkOrCompositeExpression(): void\n    {\n        $cb   = new CriteriaBuilder();\n        $expr = $this->visitor->walkCompositeExpression(\n            $cb->orX(\n                $cb->eq('foo', 1),\n                $cb->eq('bar', 1),\n            ),\n        );\n\n        self::assertInstanceOf(QueryBuilder\\Orx::class, $expr);\n        self::assertCount(2, $expr->getParts());\n    }\n\n    public function testWalkNotCompositeExpression(): void\n    {\n        $qb = new QueryBuilder();\n        $cb = new CriteriaBuilder();\n\n        $expr = $this->visitor->walkCompositeExpression(\n            $cb->not(\n                $cb->eq('foo', 1),\n            ),\n        );\n\n        self::assertInstanceOf(QueryBuilder\\Func::class, $expr);\n        self::assertEquals('NOT', $expr->getName());\n        self::assertCount(1, $expr->getArguments());\n        self::assertEquals($qb->eq('o.foo', ':foo'), $expr->getArguments()[0]);\n        self::assertEquals(new ArrayCollection([new Parameter('foo', 1)]), $this->visitor->getParameters());\n    }\n\n    public function testWalkValue(): void\n    {\n        self::assertEquals('value', $this->visitor->walkValue(new Value('value')));\n    }\n\n    public function testClearParameters(): void\n    {\n        $this->visitor->getParameters()->add(new Parameter('field', 'value'));\n\n        $this->visitor->clearParameters();\n\n        self::assertCount(0, $this->visitor->getParameters());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/QueryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse DateTime;\nuse DateTimeImmutable;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\DBAL\\ArrayParameterType;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Driver\\Result;\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\ParameterType;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Schema\\Name\\UnquotedIdentifierFolding;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain;\nuse Doctrine\\Tests\\Mocks\\ArrayResultFactory;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Enums\\AccessLevel;\nuse Doctrine\\Tests\\Models\\Enums\\City;\nuse Doctrine\\Tests\\Models\\Enums\\UserStatus;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Psr\\Cache\\CacheItemInterface;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function array_map;\nuse function enum_exists;\n\nclass QueryTest extends OrmTestCase\n{\n    private EntityManagerMock $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    public function testGetParameters(): void\n    {\n        $query = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1');\n\n        $parameters = new ArrayCollection();\n\n        self::assertEquals($parameters, $query->getParameters());\n    }\n\n    public function testGetParametersHasSomeAlready(): void\n    {\n        $query = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1');\n        $query->setParameter(2, 84);\n\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter(2, 84));\n\n        self::assertEquals($parameters, $query->getParameters());\n    }\n\n    public function testSetParameters(): void\n    {\n        $query = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1');\n\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter(1, 'foo'));\n        $parameters->add(new Parameter(2, 'bar'));\n\n        $query->setParameters($parameters);\n\n        self::assertEquals($parameters, $query->getParameters());\n    }\n\n    /** @phpstan-param LockMode::* $lockMode */\n    #[DataProvider('provideLockModes')]\n    public function testSetLockMode(LockMode|int $lockMode): void\n    {\n        $query = $this->entityManager->wrapInTransaction(static function (EntityManagerInterface $em) use ($lockMode): Query {\n            $query = $em->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1');\n            $query->setLockMode($lockMode);\n\n            return $query;\n        });\n\n        self::assertSame($lockMode, $query->getLockMode());\n        self::assertSame($lockMode, $query->getHint(Query::HINT_LOCK_MODE));\n    }\n\n    /** @phpstan-return list<array{LockMode::*}> */\n    public static function provideLockModes(): array\n    {\n        return [\n            [LockMode::PESSIMISTIC_READ],\n            [LockMode::PESSIMISTIC_WRITE],\n        ];\n    }\n\n    public function testFree(): void\n    {\n        $query = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1');\n        $query->setParameter(2, 84, ParameterType::INTEGER);\n\n        $query->free();\n\n        self::assertCount(0, $query->getParameters());\n    }\n\n    public function testClone(): void\n    {\n        $dql = 'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.username = ?1';\n\n        $query = $this->entityManager->createQuery($dql);\n        $query->setParameter(2, 84, ParameterType::INTEGER);\n        $query->setHint('foo', 'bar');\n\n        $cloned = clone $query;\n\n        self::assertEquals($dql, $cloned->getDQL());\n        self::assertCount(0, $cloned->getParameters());\n        self::assertFalse($cloned->getHint('foo'));\n    }\n\n    public function testFluentQueryInterface(): void\n    {\n        $q  = $this->entityManager->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a');\n        $q2 = $q->expireQueryCache(true)\n          ->setQueryCacheLifetime(3600)\n          ->setQueryCache(null)\n          ->expireResultCache(true)\n          ->setHint('foo', 'bar')\n          ->setHint('bar', 'baz')\n          ->setParameter(1, 'bar')\n          ->setParameters(new ArrayCollection([new Parameter(2, 'baz')]))\n          ->setResultCache(null)\n          ->setResultCacheId('foo')\n          ->setDQL('foo')\n          ->setFirstResult(10)\n          ->setMaxResults(10);\n\n        self::assertSame($q2, $q);\n    }\n\n    #[Group('DDC-968')]\n    public function testHints(): void\n    {\n        $q = $this->entityManager->createQuery('select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a');\n        $q->setHint('foo', 'bar')->setHint('bar', 'baz');\n\n        self::assertEquals('bar', $q->getHint('foo'));\n        self::assertEquals('baz', $q->getHint('bar'));\n        self::assertEquals(['foo' => 'bar', 'bar' => 'baz'], $q->getHints());\n        self::assertTrue($q->hasHint('foo'));\n        self::assertFalse($q->hasHint('barFooBaz'));\n    }\n\n    public function testToIterableWithNoDistinctAndWrongSelectClause(): void\n    {\n        $this->expectException(QueryException::class);\n\n        $q = $this->entityManager->createQuery('select u, a from Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a');\n        $q->toIterable();\n    }\n\n    public function testToIterableWithNoDistinctAndWithValidSelectClause(): void\n    {\n        $this->expectException(QueryException::class);\n\n        $q = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a');\n        $q->toIterable();\n    }\n\n    public function testIterateWithDistinct(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT DISTINCT u from Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a');\n\n        self::assertInstanceOf(Generator::class, $q->toIterable());\n    }\n\n    public function testIterateEmptyResult(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT u from Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach\n        foreach ($q->toIterable() as $item) {\n        }\n\n        self::assertTrue(true);\n    }\n\n    #[Group('DDC-1697')]\n    public function testCollectionParameters(): void\n    {\n        $cities = [\n            0 => 'Paris',\n            3 => 'Cannes',\n            9 => 'St Julien',\n        ];\n\n        $query = $this->entityManager\n                ->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.city IN (:cities)')\n                ->setParameter('cities', $cities);\n\n        $parameters = $query->getParameters();\n        $parameter  = $parameters->first();\n\n        self::assertEquals('cities', $parameter->getName());\n        self::assertEquals($cities, $parameter->getValue());\n    }\n\n    #[Group('DDC-1697')]\n    public function testExplicitCollectionParameters(): void\n    {\n        $cities = [\n            0 => 'Paris',\n            3 => 'Cannes',\n            9 => 'St Julien',\n        ];\n\n        $query = $this->entityManager\n                ->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.city IN (:cities)')\n                ->setParameter('cities', $cities, ArrayParameterType::STRING);\n\n        $parameters = $query->getParameters();\n        $parameter  = $parameters->first();\n\n        self::assertEquals('cities', $parameter->getName());\n        self::assertEquals($cities, $parameter->getValue());\n    }\n\n    /** @phpstan-return Generator<string, array{iterable}> */\n    public static function provideProcessParameterValueIterable(): Generator\n    {\n        $baseArray = [\n            0 => 'Paris',\n            3 => 'Cannes',\n            9 => 'St Julien',\n        ];\n\n        $gen = static function () use ($baseArray) {\n            yield from $baseArray;\n        };\n\n        yield 'simple_array' => [$baseArray];\n        yield 'doctrine_collection' => [new ArrayCollection($baseArray)];\n        yield 'generator' => [$gen()];\n        yield 'array_of_enum' => [array_map([City::class, 'from'], $baseArray)];\n    }\n\n    #[DataProvider('provideProcessParameterValueIterable')]\n    public function testProcessParameterValueIterable(iterable $cities): void\n    {\n        $query = $this->entityManager->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.city IN (:cities)');\n        self::assertEquals(\n            [\n                0 => 'Paris',\n                3 => 'Cannes',\n                9 => 'St Julien',\n            ],\n            $query->processParameterValue($cities),\n        );\n    }\n\n    public function testProcessParameterValueWithIterableEntityShouldNotBeTreatedAsIterable(): void\n    {\n        $group     = new CmsGroup();\n        $group->id = 1;\n\n        $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.group IN (:group)');\n        self::assertEquals(1, $query->processParameterValue($group));\n    }\n\n    #[Group('DDC-2224')]\n    public function testProcessParameterValueClassMetadata(): void\n    {\n        $query = $this->entityManager->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.city IN (:cities)');\n        self::assertEquals(\n            CmsAddress::class,\n            $query->processParameterValue($this->entityManager->getClassMetadata(CmsAddress::class)),\n        );\n    }\n\n    public function testProcessParameterValueObject(): void\n    {\n        $query    = $this->entityManager->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.user = :user');\n        $user     = new CmsUser();\n        $user->id = 12345;\n\n        self::assertSame(\n            12345,\n            $query->processParameterValue($user),\n        );\n    }\n\n    public function testProcessParameterValueValueObjectWithDriverChain(): void\n    {\n        $driverChain = new MappingDriverChain();\n        $driverChain->addDriver($this->createAttributeDriver(), 'Foo');\n        $this->entityManager->getConfiguration()->setMetadataDriverImpl($driverChain);\n\n        $query = $this->entityManager->createQuery();\n\n        $vo = new DateTimeImmutable('2020-09-01 00:00:00');\n\n        self::assertSame($vo, $query->processParameterValue($vo));\n    }\n\n    public function testProcessParameterValueNull(): void\n    {\n        $query = $this->entityManager->createQuery('SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.user = :user');\n\n        self::assertNull($query->processParameterValue(null));\n    }\n\n    public function testProcessParameterValueBackedEnum(): void\n    {\n        $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.status = :status');\n\n        self::assertSame(['active'], $query->processParameterValue([UserStatus::Active]));\n        self::assertSame([2], $query->processParameterValue([AccessLevel::User]));\n    }\n\n    public function testProcessParameterValueBackedEnumArray(): void\n    {\n        $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.status IN (:status)');\n\n        self::assertSame(['active'], $query->processParameterValue([UserStatus::Active]));\n        self::assertSame([2], $query->processParameterValue([AccessLevel::User]));\n    }\n\n    public function testDefaultQueryHints(): void\n    {\n        $config       = $this->entityManager->getConfiguration();\n        $defaultHints = [\n            'hint_name_1' => 'hint_value_1',\n            'hint_name_2' => 'hint_value_2',\n            'hint_name_3' => 'hint_value_3',\n        ];\n\n        $config->setDefaultQueryHints($defaultHints);\n        $query = $this->entityManager->createQuery();\n        self::assertSame($config->getDefaultQueryHints(), $query->getHints());\n        $this->entityManager->getConfiguration()->setDefaultQueryHint('hint_name_1', 'hint_another_value_1');\n        self::assertNotSame($config->getDefaultQueryHints(), $query->getHints());\n        $q2 = clone $query;\n        self::assertSame($config->getDefaultQueryHints(), $q2->getHints());\n    }\n\n    #[Group('DDC-3714')]\n    public function testResultCacheCaching(): void\n    {\n        $entityManager = $this->createTestEntityManagerWithConnection(\n            $this->createConnection(\n                ArrayResultFactory::createDriverResultFromArray([\n                    ['id_0' => 1],\n                ]),\n                ArrayResultFactory::createDriverResultFromArray([]),\n            ),\n        );\n\n        $entityManager->getConfiguration()->setResultCache(new ArrayAdapter());\n        $entityManager->getConfiguration()->setQueryCache(new ArrayAdapter());\n\n        $res = $entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->useQueryCache(true)\n            ->enableResultCache(60)\n            //let it cache\n            ->getResult();\n\n        self::assertCount(1, $res);\n\n        $res = $entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->useQueryCache(true)\n            ->disableResultCache()\n            ->getResult();\n        self::assertCount(0, $res);\n    }\n\n    #[Group('DDC-3741')]\n    public function testSetHydrationCacheProfileNull(): void\n    {\n        $query = $this->entityManager->createQuery();\n        $query->setHydrationCacheProfile(null);\n        self::assertNull($query->getHydrationCacheProfile());\n    }\n\n    #[Group('2947')]\n    public function testResultCacheEviction(): void\n    {\n        $entityManager = $this->createTestEntityManagerWithConnection(\n            $this->createConnection(\n                ArrayResultFactory::createDriverResultFromArray([\n                    ['id_0' => 1],\n                ]),\n                ArrayResultFactory::createDriverResultFromArray([\n                    ['id_0' => 1],\n                    ['id_0' => 2],\n                ]),\n                ArrayResultFactory::createDriverResultFromArray([\n                    ['id_0' => 1],\n                ]),\n            ),\n        );\n\n        $entityManager->getConfiguration()->setResultCache(new ArrayAdapter());\n\n        $query = $entityManager->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->enableResultCache();\n\n        // Performs the query and sets up the initial cache\n        self::assertCount(1, $query->getResult());\n\n        // Retrieves cached data since expire flag is false and we have a cached result set\n        self::assertCount(1, $query->getResult());\n\n        // Performs the query and caches the result set since expire flag is true\n        self::assertCount(2, $query->expireResultCache()->getResult());\n\n        // Retrieves cached data since expire flag is false and we have a cached result set\n        self::assertCount(2, $query->expireResultCache(false)->getResult());\n    }\n\n    #[Group('#6162')]\n    public function testSelectJoinSubquery(): void\n    {\n        $query = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN (SELECT )');\n\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Subquery');\n        $query->getSQL();\n    }\n\n    #[Group('#6162')]\n    public function testSelectFromSubquery(): void\n    {\n        $query = $this->entityManager->createQuery('select u from (select Doctrine\\Tests\\Models\\CMS\\CmsUser c) as u');\n\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Subquery');\n        $query->getSQL();\n    }\n\n    #[Group('6699')]\n    public function testGetParameterTypeJuggling(): void\n    {\n        $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id = ?0');\n\n        $query->setParameter(0, 0);\n\n        self::assertCount(1, $query->getParameters());\n        self::assertSame(0, $query->getParameter(0)->getValue());\n        self::assertSame(0, $query->getParameter('0')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithNameZeroIsNotOverridden(): void\n    {\n        $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name');\n\n        $query->setParameter(0, 0);\n        $query->setParameter('name', 'Doctrine');\n\n        self::assertCount(2, $query->getParameters());\n        self::assertSame(0, $query->getParameter('0')->getValue());\n        self::assertSame('Doctrine', $query->getParameter('name')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): void\n    {\n        $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name');\n\n        $query->setParameter('name', 'Doctrine');\n        $query->setParameter(0, 0);\n\n        self::assertCount(2, $query->getParameters());\n        self::assertSame(0, $query->getParameter(0)->getValue());\n        self::assertSame('Doctrine', $query->getParameter('name')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithTypeJugglingWorks(): void\n    {\n        $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name');\n\n        $query->setParameter('0', 1);\n        $query->setParameter('name', 'Doctrine');\n        $query->setParameter(0, 2);\n        $query->setParameter('0', 3);\n\n        self::assertCount(2, $query->getParameters());\n        self::assertSame(3, $query->getParameter(0)->getValue());\n        self::assertSame(3, $query->getParameter('0')->getValue());\n        self::assertSame('Doctrine', $query->getParameter('name')->getValue());\n    }\n\n    #[Group('6748')]\n    public function testResultCacheProfileCanBeRemovedViaSetter(): void\n    {\n        $this->entityManager->getConfiguration()->setResultCache(new ArrayAdapter());\n\n        $query = $this->entityManager->createQuery('SELECT u FROM ' . CmsUser::class . ' u');\n        $query->enableResultCache();\n        $query->setResultCacheProfile(null);\n\n        self::assertNull($query->getQueryCacheProfile());\n    }\n\n    #[Group('7527')]\n    public function testValuesAreNotBeingResolvedForSpecifiedParameterTypes(): void\n    {\n        $unitOfWork = $this->createMock(UnitOfWork::class);\n\n        $this->entityManager->setUnitOfWork($unitOfWork);\n\n        $unitOfWork\n            ->expects(self::never())\n            ->method('getSingleIdentifierValue');\n\n        $query = $this->entityManager->createQuery('SELECT d FROM ' . DateTimeModel::class . ' d WHERE d.datetime = :value');\n\n        $query->setParameter('value', new DateTime(), Types::DATETIME_MUTABLE);\n\n        self::assertEmpty($query->getResult());\n    }\n\n    #[Group('7982')]\n    public function testNonExistentExecutor(): void\n    {\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('[Syntax Error] line 0, col -1: Error: Expected SELECT, UPDATE or DELETE, got end of string.');\n\n        $this->entityManager->createQuery('0')->execute();\n    }\n\n    #[Group('8106')]\n    public function testGetParameterColonNormalize(): void\n    {\n        $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.name = :name');\n\n        $query->setParameter(':name', 'Benjamin');\n        $query->setParameter('name', 'Benjamin');\n\n        self::assertCount(1, $query->getParameters());\n        self::assertSame('Benjamin', $query->getParameter(':name')->getValue());\n        self::assertSame('Benjamin', $query->getParameter('name')->getValue());\n    }\n\n    public function testGetQueryCacheDriverWithDefaults(): void\n    {\n        $cache         = $this->createMock(CacheItemPoolInterface::class);\n        $cacheItemMock = $this->createMock(CacheItemInterface::class);\n        $cacheItemMock->method('set')->willReturnSelf();\n        $cacheItemMock->method('expiresAfter')->willReturnSelf();\n        $cache\n            ->expects(self::atLeastOnce())\n            ->method('getItem')\n            ->willReturn($cacheItemMock);\n\n        $this->entityManager->getConfiguration()->setQueryCache($cache);\n        $this->entityManager\n            ->createQuery('select u from ' . CmsUser::class . ' u')\n            ->getSQL();\n    }\n\n    public function testGetQueryCacheDriverWithCacheExplicitlySet(): void\n    {\n        $cache         = $this->createMock(CacheItemPoolInterface::class);\n        $cacheItemMock = $this->createMock(CacheItemInterface::class);\n        $cacheItemMock->method('set')->willReturnSelf();\n        $cacheItemMock->method('expiresAfter')->willReturnSelf();\n        $cache\n            ->expects(self::atLeastOnce())\n            ->method('getItem')\n            ->willReturn($cacheItemMock);\n\n        $this->entityManager\n            ->createQuery('select u from ' . CmsUser::class . ' u')\n            ->setQueryCache($cache)\n            ->getSQL();\n    }\n\n    private function createConnection(Result ...$results): Connection\n    {\n        $driverConnection = $this->createMock(Driver\\Connection::class);\n        $driverConnection->method('query')\n            ->willReturnOnConsecutiveCalls(...$results);\n\n        $platform = $this->getMockBuilder(AbstractPlatform::class)\n            ->setConstructorArgs(enum_exists(UnquotedIdentifierFolding::class) ? [UnquotedIdentifierFolding::UPPER] : [])\n            ->getMock();\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($driverConnection);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        return new Connection([], $driver);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/SelectSqlGenerationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\DBAL\\LockMode;\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Query as ORMQuery;\nuse Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode;\nuse Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\ORM\\Query\\TokenType;\nuse Doctrine\\Tests\\DbalTypes\\NegativeToPositiveType;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Exception;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function class_exists;\n\n// DBAL 3 compatibility\nclass_exists('Doctrine\\\\DBAL\\\\Platforms\\\\SqlitePlatform');\n\nclass SelectSqlGenerationTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n    private int $hydrationMode = ORMQuery::HYDRATE_OBJECT;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    /**\n     * Assert a valid SQL generation.\n     */\n    public function assertSqlGeneration(\n        string $dqlToBeTested,\n        string $sqlToBeConfirmed,\n        array $queryHints = [],\n        array $queryParams = [],\n    ): void {\n        $query = $this->entityManager->createQuery($dqlToBeTested);\n        $query->setHydrationMode($this->hydrationMode);\n\n        foreach ($queryParams as $name => $value) {\n            $query->setParameter($name, $value);\n        }\n\n        $query->useQueryCache(false);\n\n        foreach ($queryHints as $name => $value) {\n            $query->setHint($name, $value);\n        }\n\n        $sqlGenerated = $query->getSQL();\n\n        parent::assertEquals(\n            $sqlToBeConfirmed,\n            $sqlGenerated,\n        );\n\n        $query->free();\n    }\n\n    /**\n     * Asser an invalid SQL generation.\n     */\n    public function assertInvalidSqlGeneration(\n        string $dqlToBeTested,\n        string $expectedException,\n        array $queryHints = [],\n        array $queryParams = [],\n    ): void {\n        $this->expectException($expectedException);\n\n        $query = $this->entityManager->createQuery($dqlToBeTested);\n\n        foreach ($queryParams as $name => $value) {\n            $query->setParameter($name, $value);\n        }\n\n        $query->useQueryCache(false);\n\n        foreach ($queryHints as $name => $value) {\n            $query->setHint($name, $value);\n        }\n\n        $sql = $query->getSql();\n        $query->free();\n\n        // If we reached here, test failed\n        self::fail($sql);\n    }\n\n    #[Group('DDC-3697')]\n    public function testJoinWithRangeVariablePutsConditionIntoSqlWhereClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT c.id FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson c JOIN Doctrine\\Tests\\Models\\Company\\CompanyPerson r WHERE c.spouse = r AND r.id = 42',\n            'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42',\n        );\n    }\n\n    #[Group('DDC-3697')]\n    public function testJoinWithRangeVariableAndInheritancePutsConditionIntoSqlWhereClause(): void\n    {\n        /*\n         * Basically like the previous test, but this time load data for the inherited objects as well.\n         * The important thing is that the ON clauses in LEFT JOINs only contain the conditions necessary to join the appropriate inheritance table\n         * whereas the filtering condition must remain in the SQL WHERE clause.\n         */\n        $this->assertSqlGeneration(\n            'SELECT c.id FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson c JOIN Doctrine\\Tests\\Models\\Company\\CompanyPerson r WHERE c.spouse = r AND r.id = 42',\n            'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42',\n        );\n    }\n\n    public function testSupportsSelectForAllFields(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_',\n        );\n    }\n\n    public function testSupportsSelectForOneField(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0 FROM cms_users c0_',\n        );\n    }\n\n    public function testSupportsSelectForOneNestedField(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a JOIN a.user u',\n            'SELECT c0_.id AS id_0 FROM cms_articles c1_ INNER JOIN cms_users c0_ ON c1_.user_id = c0_.id',\n        );\n    }\n\n    public function testSupportsSelectForAllNestedField(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a JOIN a.user u ORDER BY u.name ASC',\n            'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id ORDER BY c1_.name ASC',\n        );\n    }\n\n    public function testNotExistsExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = 1234)',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NOT EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234)',\n        );\n    }\n\n    public function testSupportsSelectForMultipleColumnsOfASingleComponent(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.username, u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.username AS username_0, c0_.name AS name_1 FROM cms_users c0_',\n        );\n    }\n\n    public function testSupportsSelectUsingMultipleFromComponents(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE u = p.user',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id',\n        );\n    }\n\n    public function testSupportsJoinOnMultipleComponents(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p ON u = p.user',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)',\n        );\n    }\n\n    public function testSupportsJoinOnMultipleComponentsWithJoinedInheritanceType(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e JOIN Doctrine\\Tests\\Models\\Company\\CompanyManager m ON e.id = m.id',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e LEFT JOIN Doctrine\\Tests\\Models\\Company\\CompanyManager m ON e.id = m.id',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id LEFT JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c JOIN c.salesPerson s LEFT JOIN Doctrine\\Tests\\Models\\Company\\CompanyEvent e ON s.id = e.id',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN (company_events c4_ LEFT JOIN company_auctions c5_ ON c4_.id = c5_.id LEFT JOIN company_raffles c6_ ON c4_.id = c6_.id) ON (c2_.id = c4_.id) WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n        );\n    }\n\n    public function testSupportsSelectWithCollectionAssociationJoin(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, p FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.phonenumbers p',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON c0_.id = c1_.user_id',\n        );\n    }\n\n    public function testSupportsSelectWithSingleValuedAssociationJoin(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u JOIN u.avatar a',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f1_.id AS id_2, f0_.avatar_id AS avatar_id_3 FROM forum_users f0_ INNER JOIN forum_avatars f1_ ON f0_.avatar_id = f1_.id',\n        );\n    }\n\n    public function testSelectCorrelatedSubqueryComplexMathematicalExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT (SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p JOIN p.user ui WHERE ui.id = u.id) AS c FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT (SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_1 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = c2_.id) AS sclr_0 FROM cms_users c2_',\n        );\n    }\n\n    public function testSelectComplexMathematicalExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p JOIN p.user ui WHERE ui.id = ?1',\n            'SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_0 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = ?',\n        );\n    }\n\n    public function testSingleAssociationPathExpressionInSubselect(): void\n    {\n        // Can be supported if SimpleSelectExpression supports SingleValuedPathExpression instead of StateFieldPathExpression.\n        self::markTestSkipped('Not yet supported.');\n\n        $this->assertSqlGeneration(\n            'SELECT (SELECT p.user FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.user = u) user_id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1',\n            'SELECT (SELECT c0_.user_id FROM cms_phonenumbers c0_ WHERE c0_.user_id = c1_.id) AS sclr_0 FROM cms_users c1_ WHERE c1_.id = ?',\n        );\n    }\n\n    #[Group('DDC-1077')]\n    public function testConstantValueInSelect(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name, 'foo' AS bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\",\n            \"SELECT c0_.name AS name_0, 'foo' AS sclr_1 FROM cms_users c0_\",\n        );\n    }\n\n    public function testSupportsOrderByWithAscAsDefault(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u ORDER BY u.id',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC',\n        );\n    }\n\n    public function testSupportsOrderByAsc(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u ORDER BY u.id asc',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC',\n        );\n    }\n\n    public function testSupportsOrderByDesc(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u ORDER BY u.id desc',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id DESC',\n        );\n    }\n\n    public function testSupportsOrderByWithSimpleArithmeticExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u ORDER BY LENGTH(u.username) + LENGTH(u.username) asc',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY LENGTH(f0_.username) + LENGTH(f0_.username) ASC',\n        );\n    }\n\n    public function testSupportsSelectDistinct(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT DISTINCT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT DISTINCT c0_.name AS name_0 FROM cms_users c0_',\n        );\n    }\n\n    public function testSupportsAggregateFunctionInSelectedFields(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT COUNT(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id',\n            'SELECT COUNT(c0_.id) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id',\n        );\n    }\n\n    public function testSupportsAggregateFunctionWithSimpleArithmetic(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT MAX(u.id + 4) * 2 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT MAX(c0_.id + 4) * 2 AS sclr_0 FROM cms_users c0_',\n        );\n    }\n\n    #[Group('DDC-3276')]\n    public function testSupportsAggregateCountFunctionWithSimpleArithmetic(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            'SELECT COUNT(CONCAT(u.id, u.name)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id',\n            'SELECT COUNT(CONCAT(c0_.id, c0_.name)) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id',\n        );\n    }\n\n    public function testSupportsWhereClauseWithPositionalParameter(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\Forum\\ForumUser u where u.id = ?1',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id = ?',\n        );\n    }\n\n    public function testSupportsWhereClauseWithNamedParameter(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\Forum\\ForumUser u where u.username = :name',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ?',\n        );\n    }\n\n    public function testSupportsWhereAndClauseWithNamedParameters(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\Forum\\ForumUser u where u.username = :name and u.username = :name2',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ? AND f0_.username = ?',\n        );\n    }\n\n    public function testSupportsCombinedWhereClauseWithNamedParameter(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\Forum\\ForumUser u where (u.username = :name OR u.username = :name2) AND u.id = :id',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE (f0_.username = ? OR f0_.username = ?) AND f0_.id = ?',\n        );\n    }\n\n    public function testSupportsAggregateFunctionInASelectDistinct(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT COUNT(DISTINCT u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT COUNT(DISTINCT c0_.name) AS sclr_0 FROM cms_users c0_',\n        );\n    }\n\n    /** Ticket #668 */\n    public function testSupportsASqlKeywordInAStringLiteralParam(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name LIKE '%foo OR bar%'\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.name LIKE '%foo OR bar%'\",\n        );\n    }\n\n    public function testSupportsArithmeticExpressionsInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000',\n        );\n    }\n\n    public function testSupportsMultipleEntitiesInFromClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a JOIN a.user u2 WHERE u.id = u2.id',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id',\n        );\n    }\n\n    public function testSupportsMultipleEntitiesInFromClauseUsingPathExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsArticle a WHERE u.id = a.user',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id',\n        );\n    }\n\n    public function testSupportsPlainJoinWithoutClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id, a.id from Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a',\n            'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u.id, a.id from Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.articles a',\n            'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id',\n        );\n    }\n\n    #[Group('DDC-135')]\n    public function testSupportsJoinAndWithClauseRestriction(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')\",\n        );\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a WITH a.topic LIKE '%foo%'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')\",\n        );\n    }\n\n    #[Group('DDC-135')]\n    #[Group('DDC-177')]\n    public function testJoinOnClauseNotYetSupportedThrowsException(): void\n    {\n        $this->expectException(QueryException::class);\n\n        $sql = $this->entityManager->createQuery(\n            \"SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a ON a.topic LIKE '%foo%'\",\n        )->getSql();\n    }\n\n    public function testSupportsMultipleJoins(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id, a.id, p.phonenumber, c.id from Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c',\n            'SELECT c0_.id AS id_0, c1_.id AS id_1, c2_.phonenumber AS phonenumber_2, c3_.id AS id_3 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id INNER JOIN cms_phonenumbers c2_ ON c0_.id = c2_.user_id INNER JOIN cms_comments c3_ ON c1_.id = c3_.article_id',\n        );\n    }\n\n    public function testSupportsTrimFunction(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE TRIM(TRAILING ' ' FROM u.name) = 'someone'\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING ' ' FROM c0_.name) = 'someone'\",\n        );\n    }\n\n    #[Group('DDC-2668')]\n    public function testSupportsTrimLeadingZeroString(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE TRIM(TRAILING '0' FROM u.name) != ''\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''\",\n        );\n    }\n\n    /** Ticket 894 */\n    public function testSupportsBetweenClauseWithPositionalParameters(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id BETWEEN ?1 AND ?2',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.id BETWEEN ? AND ?',\n        );\n    }\n\n    #[Group('DDC-1802')]\n    public function testSupportsNotBetweenForSizeFunction(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT m.name FROM Doctrine\\Tests\\Models\\StockExchange\\Market m WHERE SIZE(m.stocks) NOT BETWEEN ?1 AND ?2',\n            'SELECT e0_.name AS name_0 FROM exchange_markets e0_ WHERE (SELECT COUNT(*) FROM exchange_stocks e1_ WHERE e1_.market_id = e0_.id) NOT BETWEEN ? AND ?',\n        );\n    }\n\n    public function testSupportsFunctionalExpressionsInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE TRIM(u.name) = 'someone'\",\n            // String quoting in the SQL usually depends on the database platform.\n            // This test works with a mock connection which uses ' for string quoting.\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(c0_.name) = 'someone'\",\n        );\n    }\n\n    public function testSupportsInstanceOfExpressionsInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyEmployee',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')\",\n        );\n    }\n\n    public function testSupportsInstanceOfExpressionInWherePartWithMultipleValues(): void\n    {\n        // This also uses FQCNs starting with or without a backslash in the INSTANCE OF parameter\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF (Doctrine\\Tests\\Models\\Company\\CompanyEmployee, \\Doctrine\\Tests\\Models\\Company\\CompanyManager)',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')\",\n        );\n    }\n\n    #[Group('DDC-1194')]\n    public function testSupportsInstanceOfExpressionsInWherePartPrefixedSlash(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF \\Doctrine\\Tests\\Models\\Company\\CompanyEmployee',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')\",\n        );\n    }\n\n    #[Group('DDC-1194')]\n    public function testSupportsInstanceOfExpressionsInWherePartWithUnrelatedClass(): void\n    {\n        $this->assertInvalidSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF \\Doctrine\\Tests\\Models\\CMS\\CmsUser',\n            QueryException::class,\n        );\n    }\n\n    public function testSupportsInstanceOfExpressionsInWherePartInDeeperLevel(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee u WHERE u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyManager',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id WHERE c0_.discr IN ('manager')\",\n        );\n    }\n\n    public function testSupportsInstanceOfExpressionsInWherePartInDeepestLevel(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyManager u WHERE u INSTANCE OF Doctrine\\Tests\\Models\\Company\\CompanyManager',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')\",\n        );\n    }\n\n    public function testSupportsInstanceOfExpressionsUsingInputParameterInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson u WHERE u INSTANCE OF ?1',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN (?)',\n            [],\n            [1 => $this->entityManager->getClassMetadata(CompanyEmployee::class)],\n        );\n    }\n\n    /** Ticket #973 */\n    public function testSupportsSingleValuedInExpressionWithoutSpacesInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE IDENTITY(u.email) IN(46)',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.email_id IN (46)',\n        );\n    }\n\n    public function testSupportsMultipleValuedInExpressionInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id IN (1, 2)',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id IN (1, 2)',\n        );\n    }\n\n    public function testSupportsNotInExpressionInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE :id NOT IN (1)',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ? NOT IN (1)',\n        );\n    }\n\n    #[Group('DDC-1802')]\n    public function testSupportsNotInExpressionForModFunction(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE MOD(u.id, 5) NOT IN(1,3,4)',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE MOD(c0_.id, 5) NOT IN (1, 3, 4)',\n        );\n    }\n\n    public function testInExpressionWithSingleValuedAssociationPathExpressionInWherePart(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u WHERE u.avatar IN (?1, ?2)',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)',\n        );\n    }\n\n    public function testInvalidInExpressionWithSingleValuedAssociationPathExpressionOnInverseSide(): void\n    {\n        // We do not support SingleValuedAssociationPathExpression on inverse side\n        $this->assertInvalidSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.address IN (?1, ?2)',\n            QueryException::class,\n        );\n    }\n\n    public function testInExpressionWithArithmeticExpression(): void\n    {\n        $this->entityManager->getConfiguration()->addCustomStringFunction('FOO', MyAbsFunction::class);\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u WHERE u.username IN (FOO('Lo'), 'Lo', :name)\",\n            \"SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)\",\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Forum\\ForumUser u WHERE u.id IN (1 + 1)',\n            'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)',\n        );\n    }\n\n    public function testSupportsConcatFunctionForMysqlAndPostgresql(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE CONCAT(u.name, 's') = ?1\",\n            \"SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, 's') = ?\",\n        );\n        $this->assertSqlGeneration(\n            'SELECT CONCAT(u.id, u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1',\n            'SELECT CONCAT(c0_.id, c0_.name) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?',\n        );\n\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n        $this->assertSqlGeneration(\n            \"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE CONCAT(u.name, 's') = ?1\",\n            \"SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE c0_.name || 's' = ?\",\n        );\n        $this->assertSqlGeneration(\n            'SELECT CONCAT(u.id, u.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1',\n            'SELECT c0_.id || c0_.name AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?',\n        );\n    }\n\n    public function testSupportsExistsExpressionInWherePartWithCorrelatedSubquery(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = u.id)',\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = c0_.id)',\n        );\n    }\n\n    #[Group('DDC-593')]\n    public function testSubqueriesInComparisonExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE (u.id >= (SELECT u2.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 WHERE u2.name = :name)) AND (u.id <= (SELECT u3.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u3 WHERE u3.name = :name))',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id >= (SELECT c1_.id FROM cms_users c1_ WHERE c1_.name = ?)) AND (c0_.id <= (SELECT c2_.id FROM cms_users c2_ WHERE c2_.name = ?))',\n        );\n    }\n\n    public function testSupportsMemberOfExpressionOneToMany(): void\n    {\n        // \"Get all users who have $phone as a phonenumber.\" (*cough* doesnt really make sense...)\n        $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE :param MEMBER OF u.phonenumbers');\n\n        $phone              = new CmsPhonenumber();\n        $phone->phonenumber = 101;\n        $q->setParameter('param', $phone);\n\n        self::assertEquals(\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id AND c1_.phonenumber = ?)',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsMemberOfExpressionManyToMany(): void\n    {\n        // \"Get all users who are members of $group.\"\n        $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE :param MEMBER OF u.groups');\n\n        $group     = new CmsGroup();\n        $group->id = 101;\n        $q->setParameter('param', $group);\n\n        self::assertEquals(\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (?))',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsMemberOfExpressionManyToManyParameterArray(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE :param MEMBER OF u.groups');\n\n        $group      = new CmsGroup();\n        $group->id  = 101;\n        $group2     = new CmsGroup();\n        $group2->id = 105;\n        $q->setParameter('param', [$group, $group2]);\n\n        self::assertEquals(\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (?))',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsMemberOfExpressionSelfReferencing(): void\n    {\n        // \"Get all persons who have $person as a friend.\"\n        // Tough one: Many-many self-referencing (\"friends\") with class table inheritance\n        $q      = $this->entityManager->createQuery('SELECT p FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p WHERE :param MEMBER OF p.friends');\n        $person = new CompanyPerson();\n        $this->entityManager->getClassMetadata($person::class)->setIdentifierValues($person, ['id' => 101]);\n        $q->setParameter('param', $person);\n        self::assertEquals(\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ WHERE c3_.person_id = c0_.id AND c3_.friend_id IN (?))',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsMemberOfWithSingleValuedAssociation(): void\n    {\n        // Impossible example, but it illustrates the purpose\n        $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.email MEMBER OF u.groups');\n\n        self::assertEquals(\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (c0_.email_id))',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsMemberOfWithIdentificationVariable(): void\n    {\n        // Impossible example, but it illustrates the purpose\n        $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u MEMBER OF u.groups');\n\n        self::assertEquals(\n            'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (c0_.id))',\n            $q->getSql(),\n        );\n    }\n\n    public function testSupportsCurrentDateFunction(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\\Tests\\Models\\Generic\\DateTimeModel d WHERE d.datetime > current_date()');\n        self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_datetime > CURRENT_DATE', $q->getSql());\n    }\n\n    public function testSupportsCurrentTimeFunction(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\\Tests\\Models\\Generic\\DateTimeModel d WHERE d.time > current_time()');\n        self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_time > CURRENT_TIME', $q->getSql());\n    }\n\n    public function testSupportsCurrentTimestampFunction(): void\n    {\n        $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\\Tests\\Models\\Generic\\DateTimeModel d WHERE d.datetime > current_timestamp()');\n        self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_datetime > CURRENT_TIMESTAMP', $q->getSql());\n    }\n\n    public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition(): void\n    {\n        $this->assertSqlGeneration(\n            // DQL\n            // The result of this query consists of all employees whose spouses are also employees.\n            'SELECT DISTINCT emp FROM Doctrine\\Tests\\Models\\CMS\\CmsEmployee emp\n                WHERE EXISTS (\n                    SELECT spouseEmp\n                    FROM Doctrine\\Tests\\Models\\CMS\\CmsEmployee spouseEmp\n                    WHERE spouseEmp = emp.spouse)',\n            // SQL\n            'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)',\n        );\n    }\n\n    public function testExistsExpressionWithSimpleSelectReturningScalar(): void\n    {\n        $this->assertSqlGeneration(\n            // DQL\n            // The result of this query consists of all employees whose spouses are also employees.\n            'SELECT DISTINCT emp FROM Doctrine\\Tests\\Models\\CMS\\CmsEmployee emp\n                WHERE EXISTS (\n                    SELECT 1\n                    FROM Doctrine\\Tests\\Models\\CMS\\CmsEmployee spouseEmp\n                    WHERE spouseEmp = emp.spouse)',\n            // SQL\n            'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT 1 AS sclr_3 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)',\n        );\n    }\n\n    public function testLimitFromQueryClass(): void\n    {\n        $q = $this->entityManager\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->setMaxResults(10);\n\n        self::assertEquals('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10', $q->getSql());\n    }\n\n    public function testLimitAndOffsetFromQueryClass(): void\n    {\n        $q = $this->entityManager\n            ->createQuery('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u')\n            ->setMaxResults(10)\n            ->setFirstResult(0);\n\n        // DBAL 2.8+ doesn't add OFFSET part when offset is 0\n        self::assertThat(\n            $q->getSql(),\n            self::logicalOr(\n                self::identicalTo('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10'),\n                self::identicalTo('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10 OFFSET 0'),\n            ),\n        );\n    }\n\n    public function testSizeFunction(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE SIZE(u.phonenumbers) > 1',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1',\n        );\n    }\n\n    public function testSizeFunctionSupportsManyToMany(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE SIZE(u.groups) > 1',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id) > 1',\n        );\n    }\n\n    public function testEmptyCollectionComparisonExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.phonenumbers IS EMPTY',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.phonenumbers IS NOT EMPTY',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0',\n        );\n    }\n\n    public function testNestedExpressions(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where u.id > 10 and u.id < 42 and ((u.id * 2) > 5)',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)',\n        );\n    }\n\n    public function testNestedExpressions2(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where (u.id > 10) and (u.id < 42 and ((u.id * 2) > 5)) or u.id <> 42',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42',\n        );\n    }\n\n    public function testNestedExpressions3(): void\n    {\n        $this->assertSqlGeneration(\n            'select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u where (u.id > 10) and (u.id between 1 and 10 or u.id in (1, 2, 3, 4, 5))',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id BETWEEN 1 AND 10 OR c0_.id IN (1, 2, 3, 4, 5))',\n        );\n    }\n\n    public function testOrderByCollectionAssociationSize(): void\n    {\n        $this->assertSqlGeneration(\n            'select u, size(u.articles) as numArticles from Doctrine\\Tests\\Models\\CMS\\CmsUser u order by numArticles',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT COUNT(*) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ ORDER BY sclr_4 ASC',\n        );\n    }\n\n    public function testOrderBySupportsSingleValuedPathExpressionOwningSide(): void\n    {\n        $this->assertSqlGeneration(\n            'select a from Doctrine\\Tests\\Models\\CMS\\CmsArticle a order by a.user',\n            'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ ORDER BY c0_.user_id ASC',\n        );\n    }\n\n    public function testOrderBySupportsSingleValuedPathExpressionInverseSide(): void\n    {\n        $this->expectException(QueryException::class);\n        $q = $this->entityManager->createQuery('select u from Doctrine\\Tests\\Models\\CMS\\CmsUser u order by u.address');\n        $q->getSQL();\n    }\n\n    public function testBooleanLiteralInWhereOnSqlite(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new SQLitePlatform());\n        $this->assertSqlGeneration(\n            'SELECT b FROM Doctrine\\Tests\\Models\\Generic\\BooleanModel b WHERE b.booleanField = true',\n            'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 1',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT b FROM Doctrine\\Tests\\Models\\Generic\\BooleanModel b WHERE b.booleanField = false',\n            'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 0',\n        );\n    }\n\n    public function testBooleanLiteralInWhereOnPostgres(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n        $this->assertSqlGeneration(\n            'SELECT b FROM Doctrine\\Tests\\Models\\Generic\\BooleanModel b WHERE b.booleanField = true',\n            'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = true',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT b FROM Doctrine\\Tests\\Models\\Generic\\BooleanModel b WHERE b.booleanField = false',\n            'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = false',\n        );\n    }\n\n    public function testSingleValuedAssociationFieldInWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT p FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.user = ?1',\n            'SELECT c0_.phonenumber AS phonenumber_0, c0_.user_id AS user_id_1 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?',\n        );\n    }\n\n    public function testSingleValuedAssociationNullCheckOnOwningSide(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT a FROM Doctrine\\Tests\\Models\\CMS\\CmsAddress a WHERE a.user IS NULL',\n            'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_ WHERE c0_.user_id IS NULL',\n        );\n    }\n\n    /**\n     * Null check on inverse side has to happen through explicit JOIN.\n     * \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.address IS NULL\"\n     * where the CmsUser is the inverse side is not supported.\n     */\n    public function testSingleValuedAssociationNullCheckOnInverseSide(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.address a WHERE a.id IS NULL',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL',\n        );\n    }\n\n    #[Group('DDC-339')]\n    #[Group('DDC-1572')]\n    public function testStringFunctionLikeExpression(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE LOWER(u.name) LIKE '%foo OR bar%'\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE '%foo OR bar%'\",\n        );\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE LOWER(u.name) LIKE :str',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE ?',\n        );\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE CONCAT(UPPER(u.name), '_moo') LIKE :str\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) || '_moo' LIKE ?\",\n        );\n\n        // DDC-1572\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE UPPER(u.name) LIKE UPPER(:str)',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) LIKE UPPER(?)',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE UPPER(LOWER(u.name)) LIKE UPPER(LOWER(:str))',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) LIKE UPPER(LOWER(?))',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE u.name',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE c0_.name)',\n        );\n    }\n\n    #[Group('DDC-1802')]\n    public function testStringFunctionNotLikeExpression(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE LOWER(u.name) NOT LIKE '%foo OR bar%'\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) NOT LIKE '%foo OR bar%'\",\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE UPPER(LOWER(u.name)) NOT LIKE UPPER(LOWER(:str))',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) NOT LIKE UPPER(LOWER(?))',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a WITH a.topic NOT LIKE u.name',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)',\n        );\n    }\n\n    #[Group('DDC-338')]\n    public function testOrderedCollectionFetchJoined(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT r, l FROM Doctrine\\Tests\\Models\\Routing\\RoutingRoute r JOIN r.legs l',\n            'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.departureDate AS departureDate_2, r1_.arrivalDate AS arrivalDate_3, r1_.from_id AS from_id_4, r1_.to_id AS to_id_5 FROM RoutingRoute r0_ INNER JOIN RoutingRouteLegs r2_ ON r0_.id = r2_.route_id INNER JOIN RoutingLeg r1_ ON r1_.id = r2_.leg_id ORDER BY r1_.departureDate ASC',\n        );\n    }\n\n    public function testSubselectInSelect(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = 'jon'\",\n            \"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.phonenumber) AS sclr_2 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr_1 FROM cms_users c0_ WHERE c0_.name = 'jon'\",\n        );\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticWriteLockQueryHint(): void\n    {\n        if ($this->entityManager->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('SqLite does not support Row locking at all.');\n        }\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'gblanco'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE\",\n            [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_WRITE],\n        );\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticReadLockQueryHintPostgreSql(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'gblanco'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE\",\n            [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ],\n        );\n    }\n\n    #[Group('DDC-1693')]\n    #[Group('locking')]\n    public function testLockModeNoneQueryHint(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'gblanco'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco'\",\n            [ORMQuery::HINT_LOCK_MODE => LockMode::NONE],\n        );\n    }\n\n    #[Group('DDC-430')]\n    public function testSupportSelectWithMoreThan10InputParameters(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1 OR u.id = ?2 OR u.id = ?3 OR u.id = ?4 OR u.id = ?5 OR u.id = ?6 OR u.id = ?7 OR u.id = ?8 OR u.id = ?9 OR u.id = ?10 OR u.id = ?11',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?',\n        );\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticReadLockQueryHintMySql(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'gblanco'\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' LOCK IN SHARE MODE\",\n            [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ],\n        );\n    }\n\n    #[Group('locking')]\n    #[Group('DDC-178')]\n    public function testPessimisticReadLockQueryHintOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 'gblanco'\",\n            \"SELECT c0_.id AS ID_0, c0_.status AS STATUS_1, c0_.username AS USERNAME_2, c0_.name AS NAME_3, c0_.email_id AS EMAIL_ID_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE\",\n            [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ],\n        );\n    }\n\n    #[Group('DDC-431')]\n    public function testSupportToCustomDQLFunctions(): void\n    {\n        $config = $this->entityManager->getConfiguration();\n        $config->addCustomNumericFunction('MYABS', MyAbsFunction::class);\n\n        $this->assertSqlGeneration(\n            'SELECT MYABS(p.phonenumber) FROM Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p',\n            'SELECT ABS(c0_.phonenumber) AS sclr_0 FROM cms_phonenumbers c0_',\n        );\n\n        $config->setCustomNumericFunctions([]);\n    }\n\n    #[Group('DDC-826')]\n    public function testMappedSuperclassAssociationJoin(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT f FROM Doctrine\\Tests\\Models\\DirectoryTree\\File f JOIN f.parentDirectory d WHERE f.id = ?1',\n            'SELECT f0_.id AS id_0, f0_.name AS name_1, f0_.extension AS extension_2, f0_.parentDirectory_id AS parentDirectory_id_3 FROM \"file\" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?',\n        );\n    }\n\n    #[Group('DDC-1053')]\n    public function testGroupBy(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g.id, count(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g JOIN g.users u GROUP BY g.id',\n            'SELECT c0_.id AS id_0, count(c1_.id) AS sclr_1 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id',\n        );\n    }\n\n    #[Group('DDC-1053')]\n    public function testGroupByIdentificationVariable(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g, count(u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g JOIN g.users u GROUP BY g',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, count(c1_.id) AS sclr_2 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id, c0_.name',\n        );\n    }\n\n    public function testCaseContainingNullIf(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT NULLIF(g.id, g.name) AS NullIfEqual FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g',\n            'SELECT NULLIF(c0_.id, c0_.name) AS sclr_0 FROM cms_groups c0_',\n        );\n    }\n\n    public function testCaseContainingCoalesce(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u\",\n            \"SELECT COALESCE(NULLIF(c0_.name, ''), c0_.username) AS sclr_0 FROM cms_users c0_\",\n        );\n    }\n\n    /**\n     * Test that the right discriminator data is inserted in a subquery.\n     */\n    public function testSubSelectDiscriminator(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.name, (SELECT COUNT(cfc.id) total FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract cfc) as cfc_count FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            \"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.id) AS sclr_2 FROM company_contracts c1_ WHERE c1_.discr IN ('fix')) AS sclr_1 FROM cms_users c0_\",\n        );\n    }\n\n    public function testIdVariableResultVariableReuse(): void\n    {\n        $exceptionThrown = false;\n        try {\n            $query = $this->entityManager->createQuery('SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IN (SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u)');\n\n            $query->getSql();\n            $query->free();\n        } catch (Exception) {\n            $exceptionThrown = true;\n        }\n\n        self::assertTrue($exceptionThrown);\n    }\n\n    public function testSubSelectAliasesFromOuterQuery(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT uo, (SELECT ui.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser uo',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_',\n        );\n    }\n\n    public function testSubSelectAliasesFromOuterQueryWithSubquery(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT uo, (SELECT ui.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser ui WHERE ui.id = uo.id AND ui.name IN (SELECT uii.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser uii)) AS bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser uo',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id AND c1_.name IN (SELECT c2_.name FROM cms_users c2_)) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_',\n        );\n    }\n\n    public function testSubSelectAliasesFromOuterQueryReuseInWhereClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT uo, (SELECT ui.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\\Tests\\Models\\CMS\\CmsUser uo WHERE bar = ?0',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ WHERE sclr_4 = ?',\n        );\n    }\n\n    #[Group('DDC-1298')]\n    public function testSelectForeignKeyPKWithoutFields(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT t, s, l FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Link l INNER JOIN l.target t INNER JOIN l.source s',\n            'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1, d1_.article_id AS article_id_2, d1_.title AS title_3, d2_.source_id AS source_id_4, d2_.target_id AS target_id_5 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id',\n        );\n    }\n\n    public function testGeneralCaseWithSingleWhenClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g.id, CASE WHEN ((g.id / 2) > 18) THEN 1 ELSE 0 END AS test FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g',\n            'SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_',\n        );\n    }\n\n    public function testGeneralCaseWithMultipleWhenClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g.id, CASE WHEN (g.id / 2 < 10) THEN 2 WHEN ((g.id / 2) > 20) THEN 1 ELSE 0 END AS test FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g',\n            'SELECT c0_.id AS id_0, CASE WHEN (c0_.id / 2 < 10) THEN 2 WHEN ((c0_.id / 2) > 20) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_',\n        );\n    }\n\n    public function testSimpleCaseWithSingleWhenClause(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id = CASE g.name WHEN 'admin' THEN 1 ELSE 2 END\",\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = CASE c0_.name WHEN 'admin' THEN 1 ELSE 2 END\",\n        );\n    }\n\n    public function testSimpleCaseWithMultipleWhenClause(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id = (CASE g.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END)\",\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = (CASE c0_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END)\",\n        );\n    }\n\n    public function testGeneralCaseWithSingleWhenClauseInSubselect(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id IN (SELECT CASE WHEN ((g2.id / 2) > 18) THEN 2 ELSE 1 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g2)',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN ((c1_.id / 2) > 18) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)',\n        );\n    }\n\n    public function testGeneralCaseWithMultipleWhenClauseInSubselect(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id IN (SELECT CASE WHEN (g.id / 2 < 10) THEN 3 WHEN ((g.id / 2) > 20) THEN 2 ELSE 1 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g2)',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN (c0_.id / 2 < 10) THEN 3 WHEN ((c0_.id / 2) > 20) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)',\n        );\n    }\n\n    public function testSimpleCaseWithSingleWhenClauseInSubselect(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id IN (SELECT CASE g2.name WHEN 'admin' THEN 1 ELSE 2 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g2)\",\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 ELSE 2 END AS sclr_2 FROM cms_groups c1_)\",\n        );\n    }\n\n    public function testSimpleCaseWithMultipleWhenClauseInSubselect(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT g FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g WHERE g.id IN (SELECT CASE g2.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g2)\",\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END AS sclr_2 FROM cms_groups c1_)\",\n        );\n    }\n\n    #[Group('DDC-1696')]\n    public function testSimpleCaseWithStringPrimary(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT g.id, CASE WHEN ((g.id / 2) > 18) THEN 'Foo' ELSE 'Bar' END AS test FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 'Foo' ELSE 'Bar' END AS sclr_1 FROM cms_groups c0_\",\n        );\n    }\n\n    #[Group('DDC-2205')]\n    public function testCaseNegativeValuesInThenExpression(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT CASE g.name WHEN 'admin' THEN - 1 ELSE - 2 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT CASE c0_.name WHEN 'admin' THEN -1 ELSE -2 END AS sclr_0 FROM cms_groups c0_\",\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT CASE g.name WHEN 'admin' THEN  - 2 WHEN 'guest' THEN - 1 ELSE 0 END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT CASE c0_.name WHEN 'admin' THEN -2 WHEN 'guest' THEN -1 ELSE 0 END AS sclr_0 FROM cms_groups c0_\",\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT CASE g.name WHEN 'admin' THEN (- 1) ELSE (- 2) END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT CASE c0_.name WHEN 'admin' THEN (-1) ELSE (-2) END AS sclr_0 FROM cms_groups c0_\",\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT CASE g.name WHEN 'admin' THEN ( - :value) ELSE ( + :value) END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT CASE c0_.name WHEN 'admin' THEN (-?) ELSE (+?) END AS sclr_0 FROM cms_groups c0_\",\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT CASE g.name WHEN 'admin' THEN ( - g.id) ELSE ( + g.id) END FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g\",\n            \"SELECT CASE c0_.name WHEN 'admin' THEN (-c0_.id) ELSE (+c0_.id) END AS sclr_0 FROM cms_groups c0_\",\n        );\n    }\n\n    public function testIdentityFunctionWithCompositePrimaryKey(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT IDENTITY(p.poi, 'long') AS long FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos p\",\n            'SELECT n0_.poi_long AS sclr_0 FROM navigation_photos n0_',\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos p\",\n            'SELECT n0_.poi_lat AS sclr_0 FROM navigation_photos n0_',\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT IDENTITY(p.poi, 'long') AS long, IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos p\",\n            'SELECT n0_.poi_long AS sclr_0, n0_.poi_lat AS sclr_1 FROM navigation_photos n0_',\n        );\n\n        $this->assertInvalidSqlGeneration(\n            \"SELECT IDENTITY(p.poi, 'invalid') AS invalid FROM Doctrine\\Tests\\Models\\Navigation\\NavPhotos p\",\n            QueryException::class,\n        );\n    }\n\n    #[Group('DDC-2519')]\n    public function testPartialWithAssociationIdentifier(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT PARTIAL l.{_source, _target} FROM Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference l',\n            'SELECT l0_.iUserIdSource AS iUserIdSource_0, l0_.iUserIdTarget AS iUserIdTarget_1 FROM legacy_users_reference l0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT PARTIAL l.{_description, _source, _target} FROM Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference l',\n            'SELECT l0_.description AS description_0, l0_.iUserIdSource AS iUserIdSource_1, l0_.iUserIdTarget AS iUserIdTarget_2 FROM legacy_users_reference l0_',\n        );\n    }\n\n    #[Group('DDC-1339')]\n    public function testIdentityFunctionInSelectClause(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT IDENTITY(u.email) as email_id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.email_id AS sclr_0 FROM cms_users c0_',\n        );\n    }\n\n    public function testIdentityFunctionInJoinedSubclass(): void\n    {\n        //relation is in the subclass (CompanyManager) we are querying\n        $this->assertSqlGeneration(\n            'SELECT m, IDENTITY(m.car) as car_id FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c2_.car_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',\n        );\n\n        //relation is in the base class (CompanyPerson).\n        $this->assertSqlGeneration(\n            'SELECT m, IDENTITY(m.spouse) as spouse_id FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.spouse_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',\n        );\n    }\n\n    #[Group('DDC-1339')]\n    public function testIdentityFunctionDoesNotAcceptStateField(): void\n    {\n        $this->assertInvalidSqlGeneration(\n            'SELECT IDENTITY(u.name) as name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            QueryException::class,\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInRootClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT p FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInRootClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT p FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInChildClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInChildClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInLeafClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT m FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeJoinInLeafClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT m FROM Doctrine\\Tests\\Models\\Company\\CompanyManager m',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInRootClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInRootClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInChildClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT fc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexContract fc',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInChildClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT fc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexContract fc',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInLeafClassWithDisabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT fuc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract fuc',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false],\n        );\n    }\n\n    #[Group('DDC-1389')]\n    public function testInheritanceTypeSingleTableInLeafClassWithEnabledForcePartialLoad(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT fuc FROM Doctrine\\Tests\\Models\\Company\\CompanyFlexUltraContract fuc',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')\",\n            [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true],\n        );\n    }\n\n    #[Group('DDC-1161')]\n    public function testSelfReferenceWithOneToOneDoesNotDuplicateAlias(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT p, pp FROM Doctrine\\Tests\\Models\\Company\\CompanyPerson p JOIN p.spouse pp',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c3_.id AS id_6, c3_.name AS name_7, c4_.title AS title_8, c5_.salary AS salary_9, c5_.department AS department_10, c5_.startDate AS startDate_11, c0_.discr AS discr_12, c0_.spouse_id AS spouse_id_13, c1_.car_id AS car_id_14, c3_.discr AS discr_15, c3_.spouse_id AS spouse_id_16, c4_.car_id AS car_id_17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id',\n        );\n    }\n\n    #[Group('DDC-1384')]\n    public function testAliasDoesNotExceedPlatformDefinedLength(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT m FROM ' . __NAMESPACE__ . '\\\\DDC1384Model m',\n            'SELECT d0_.aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalkerFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo AS ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo_0 FROM DDC1384Model d0_',\n        );\n    }\n\n    #[Group('DDC-331')]\n    #[Group('DDC-1384')]\n    public function testIssue331(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e.name FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id',\n        );\n    }\n\n    #[Group('DDC-1435')]\n    public function testForeignKeyAsPrimaryKeySubselect(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT s FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Article s WHERE EXISTS (SELECT r FROM Doctrine\\Tests\\Models\\DDC117\\DDC117Reference r WHERE r.source = s)',\n            'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)',\n        );\n    }\n\n    #[Group('DDC-1474')]\n    public function testSelectWithArithmeticExpressionBeforeField(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT - e.value AS value, e.id FROM ' . __NAMESPACE__ . '\\DDC1474Entity e',\n            'SELECT -d0_.value AS sclr_0, d0_.id AS id_1 FROM DDC1474Entity d0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT e.id, + e.value AS value FROM ' . __NAMESPACE__ . '\\DDC1474Entity e',\n            'SELECT d0_.id AS id_0, +d0_.value AS sclr_1 FROM DDC1474Entity d0_',\n        );\n    }\n\n     #[Group('DDC-1430')]\n    public function testGroupByAllFieldsWhenObjectHasForeignKeys(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\CMS\\CmsEmployee e GROUP BY e',\n            'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id',\n        );\n    }\n\n    #[Group('DDC-1236')]\n    public function testGroupBySupportsResultVariable(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, u.status AS st FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY st',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.status AS status_4, c0_.email_id AS email_id_5 FROM cms_users c0_ GROUP BY c0_.status',\n        );\n    }\n\n    #[Group('DDC-1236')]\n    public function testGroupBySupportsIdentificationVariable(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u AS user FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY user',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY id_0, status_1, username_2, name_3',\n        );\n    }\n\n    #[Group('DDC-1213')]\n    public function testSupportsBitComparison(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT BIT_OR(4,2), BIT_AND(4,2), u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT (4 | 2) AS sclr_0, (4 & 2) AS sclr_1, c0_.id AS id_2, c0_.status AS status_3, c0_.username AS username_4, c0_.name AS name_5, c0_.email_id AS email_id_6 FROM cms_users c0_',\n        );\n        $this->assertSqlGeneration(\n            'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE BIT_OR(u.id,2) > 0',\n            'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0',\n        );\n        $this->assertSqlGeneration(\n            'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE BIT_AND(u.id , 4) > 0',\n            'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id & 4) > 0',\n        );\n        $this->assertSqlGeneration(\n            'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE BIT_OR(u.id , 2) > 0 OR BIT_AND(u.id , 4) > 0',\n            'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0 OR (c0_.id & 4) > 0',\n        );\n    }\n\n    #[Group('DDC-1539')]\n    public function testParenthesesOnTheLeftHandOfComparison(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u where ( (u.id + u.id) * u.id ) > 100',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + c0_.id) * c0_.id) > 100',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u where (u.id + u.id) * u.id > 100',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id + c0_.id) * c0_.id > 100',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u where 100 < (u.id + u.id) * u.id ',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE 100 < (c0_.id + c0_.id) * c0_.id',\n        );\n    }\n\n    public function testSupportsParenthesisExpressionInSubSelect(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.id, (SELECT (1000*SUM(subU.id)/SUM(subU.id)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser subU where subU.id = u.id) AS subSelect FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT c0_.id AS id_0, (SELECT (1000 * SUM(c1_.id) / SUM(c1_.id)) FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_1 FROM cms_users c0_',\n        );\n    }\n\n    #[Group('DDC-1557')]\n    public function testSupportsSubSqlFunction(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 )',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_)',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2  WHERE LOWER(u2.name) LIKE \\'%fabio%\\')',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_ WHERE LOWER(c1_.name) LIKE \\'%fabio%\\')',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE u1.email IN ( SELECT TRIM(IDENTITY(u2.email)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 )',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT TRIM(c1_.email_id) AS sclr_5 FROM cms_users c1_)',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE u1.email IN ( SELECT IDENTITY(u2.email) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 )',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT c1_.email_id AS sclr_5 FROM cms_users c1_)',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE COUNT(u1.id) = ( SELECT SUM(u2.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 )',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_5 FROM cms_users c1_)',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u1 WHERE COUNT(u1.id) <= ( SELECT SUM(u2.id) + COUNT(u2.email) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u2 )',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) <= (SELECT SUM(c1_.id) + COUNT(c1_.email_id) AS sclr_5 FROM cms_users c1_)',\n        );\n    }\n\n    #[Group('DDC-1574')]\n    public function testSupportsNewOperator(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a',\n            'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a',\n            'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.id + c0_.id AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p',\n            'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p',\n            'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) + c0_.id AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(a.id, a.country, a.city), new Doctrine\\Tests\\Models\\CMS\\CmsAddressDTO(u.name, e.email) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name',\n            'SELECT c0_.id AS sclr_0, c0_.country AS sclr_1, c0_.city AS sclr_2, c1_.name AS sclr_3, c2_.email AS sclr_4 FROM cms_users c1_ INNER JOIN cms_emails c2_ ON c1_.email_id = c2_.id INNER JOIN cms_addresses c0_ ON c1_.id = c0_.user_id ORDER BY c1_.name ASC',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT new Doctrine\\Tests\\Models\\CMS\\CmsUserDTO(a.id, (SELECT 1 FROM Doctrine\\Tests\\Models\\CMS\\CmsUser su), a.country, a.city), new Doctrine\\Tests\\Models\\CMS\\CmsAddressDTO(u.name, e.email) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name',\n            'SELECT c0_.id AS sclr_0, (SELECT 1 AS sclr_2 FROM cms_users c1_) AS sclr_1, c0_.country AS sclr_3, c0_.city AS sclr_4, c2_.name AS sclr_5, c3_.email AS sclr_6 FROM cms_users c2_ INNER JOIN cms_emails c3_ ON c2_.email_id = c3_.id INNER JOIN cms_addresses c0_ ON c2_.id = c0_.user_id ORDER BY c2_.name ASC',\n        );\n    }\n\n    #[Group('DDC-2234')]\n    public function testWhereFunctionIsNullComparisonExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE IDENTITY(u.email) IS NULL',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NULL',\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NULL AND IDENTITY(u.email) IS NOT NULL\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NULL AND c0_.email_id IS NOT NULL\",\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE IDENTITY(u.email) IS NOT NULL',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NOT NULL',\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NOT NULL\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NOT NULL\",\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE COALESCE(u.name, u.id) IS NOT NULL',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.name, c0_.id) IS NOT NULL',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE COALESCE(u.id, IDENTITY(u.email)) IS NOT NULL',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.id, c0_.email_id) IS NOT NULL',\n        );\n\n        $this->assertSqlGeneration(\n            \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE COALESCE(IDENTITY(u.email), NULLIF(u.name, 'FabioBatSilva')) IS NOT NULL\",\n            \"SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.email_id, NULLIF(c0_.name, 'FabioBatSilva')) IS NOT NULL\",\n        );\n    }\n\n    public function testCustomTypeValueSql(): void\n    {\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        $this->assertSqlGeneration(\n            'SELECT p.customInteger FROM Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p WHERE p.id = 1',\n            'SELECT -(c0_.customInteger) AS customInteger_0 FROM customtype_parents c0_ WHERE c0_.id = 1',\n        );\n    }\n\n    public function testCustomTypeValueSqlIgnoresIdentifierColumn(): void\n    {\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        $this->assertSqlGeneration(\n            'SELECT p.id FROM Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p WHERE p.id = 1',\n            'SELECT c0_.id AS id_0 FROM customtype_parents c0_ WHERE c0_.id = 1',\n        );\n    }\n\n    public function testCustomTypeValueSqlForAllFields(): void\n    {\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        $this->assertSqlGeneration(\n            'SELECT p FROM Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p',\n            'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_',\n        );\n    }\n\n    public function testCustomTypeValueSqlForPartialObject(): void\n    {\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        $this->assertSqlGeneration(\n            'SELECT partial p.{id, customInteger} FROM Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p',\n            'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_',\n        );\n    }\n\n    #[Group('DDC-1529')]\n    public function testMultipleFromAndInheritanceCondition(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT fix, flex FROM Doctrine\\Tests\\Models\\Company\\CompanyFixContract fix, Doctrine\\Tests\\Models\\Company\\CompanyFlexContract flex',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c1_.id AS id_3, c1_.completed AS completed_4, c1_.hoursWorked AS hoursWorked_5, c1_.pricePerHour AS pricePerHour_6, c1_.maxPrice AS maxPrice_7, c0_.discr AS discr_8, c0_.salesPerson_id AS salesPerson_id_9, c1_.discr AS discr_10, c1_.salesPerson_id AS salesPerson_id_11 FROM company_contracts c0_, company_contracts c1_ WHERE (c0_.discr IN ('fix') AND c1_.discr IN ('flexible', 'flexultra'))\",\n        );\n    }\n\n    #[Group('DDC-775')]\n    public function testOrderByClauseSupportsSimpleArithmeticExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.id + 1 ',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.id + 1 ASC',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY ( ( (u.id + 1) * (u.id - 1) ) / 2)',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY (((c0_.id + 1) * (c0_.id - 1)) / 2) ASC',\n        );\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY ((u.id + 5000) * u.id + 3) ',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY ((c0_.id + 5000) * c0_.id + 3) ASC',\n        );\n    }\n\n    public function testOrderByClauseSupportsFunction(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY CONCAT(u.username, u.name) ',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.username || c0_.name ASC',\n        );\n    }\n\n    #[Group('DDC-1719')]\n    public function testStripNonAlphanumericCharactersFromAlias(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Generic\\NonAlphaColumnsEntity e',\n            'SELECT n0_.\"simple-entity-id\" AS simpleentityid_0, n0_.\"simple-entity-value\" AS simpleentityvalue_1 FROM \"not-a-simple-entity\" n0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT e.value FROM Doctrine\\Tests\\Models\\Generic\\NonAlphaColumnsEntity e ORDER BY e.value',\n            'SELECT n0_.\"simple-entity-value\" AS simpleentityvalue_0 FROM \"not-a-simple-entity\" n0_ ORDER BY n0_.\"simple-entity-value\" ASC',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT TRIM(e.value) FROM Doctrine\\Tests\\Models\\Generic\\NonAlphaColumnsEntity e ORDER BY e.value',\n            'SELECT TRIM(n0_.\"simple-entity-value\") AS sclr_0 FROM \"not-a-simple-entity\" n0_ ORDER BY n0_.\"simple-entity-value\" ASC',\n        );\n    }\n\n    #[Group('DDC-2435')]\n    public function testColumnNameWithNumbersAndNonAlphanumericCharacters(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e FROM Doctrine\\Tests\\Models\\Quote\\NumericEntity e',\n            'SELECT t0_.\"1:1\" AS 11_0, t0_.\"2:2\" AS 22_1 FROM table t0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT e.value FROM Doctrine\\Tests\\Models\\Quote\\NumericEntity e',\n            'SELECT t0_.\"2:2\" AS 22_0 FROM table t0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT TRIM(e.value) FROM Doctrine\\Tests\\Models\\Quote\\NumericEntity e',\n            'SELECT TRIM(t0_.\"2:2\") AS sclr_0 FROM table t0_',\n        );\n    }\n\n    #[Group('DDC-1845')]\n    public function testQuotedTableDeclaration(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u FROM Doctrine\\Tests\\Models\\Quote\\User u',\n            'SELECT q0_.\"user-id\" AS userid_0, q0_.\"user-name\" AS username_1 FROM \"quote-user\" q0_',\n        );\n    }\n\n    #[Group('DDC-1845')]\n    public function testQuotedWalkJoinVariableDeclaration(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\Quote\\User u JOIN u.address a',\n            <<<'SQL'\nSELECT q0_.\"user-id\" AS userid_0, q0_.\"user-name\" AS username_1, q1_.\"address-id\" AS addressid_2, q1_.\"address-zip\" AS addresszip_3, q1_.type AS type_4, q1_.\"user-id\" AS userid_5, q1_.\"city-id\" AS cityid_6 FROM \"quote-user\" q0_ INNER JOIN \"quote-address\" q1_ ON q0_.\"user-id\" = q1_.\"user-id\" AND q1_.type IN ('simple', 'full')\nSQL,\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u, p FROM Doctrine\\Tests\\Models\\Quote\\User u JOIN u.phones p',\n            'SELECT q0_.\"user-id\" AS userid_0, q0_.\"user-name\" AS username_1, q1_.\"phone-number\" AS phonenumber_2, q1_.\"user-id\" AS userid_3 FROM \"quote-user\" q0_ INNER JOIN \"quote-phone\" q1_ ON q0_.\"user-id\" = q1_.\"user-id\"',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u, g FROM Doctrine\\Tests\\Models\\Quote\\User u JOIN u.groups g',\n            'SELECT q0_.\"user-id\" AS userid_0, q0_.\"user-name\" AS username_1, q1_.\"group-id\" AS groupid_2, q1_.\"group-name\" AS groupname_3, q1_.\"parent-id\" AS parentid_4 FROM \"quote-user\" q0_ INNER JOIN \"quote-users-groups\" q2_ ON q0_.\"user-id\" = q2_.\"user-id\" INNER JOIN \"quote-group\" q1_ ON q1_.\"group-id\" = q2_.\"group-id\"',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT a, u FROM Doctrine\\Tests\\Models\\Quote\\Address a JOIN a.user u',\n            'SELECT q0_.\"address-id\" AS addressid_0, q0_.\"address-zip\" AS addresszip_1, q1_.\"user-id\" AS userid_2, q1_.\"user-name\" AS username_3, q0_.type AS type_4, q0_.\"user-id\" AS userid_5, q0_.\"city-id\" AS cityid_6 FROM \"quote-address\" q0_ INNER JOIN \"quote-user\" q1_ ON q0_.\"user-id\" = q1_.\"user-id\" WHERE q0_.type IN (\\'simple\\', \\'full\\')',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT g, u FROM Doctrine\\Tests\\Models\\Quote\\Group g JOIN g.users u',\n            'SELECT q0_.\"group-id\" AS groupid_0, q0_.\"group-name\" AS groupname_1, q1_.\"user-id\" AS userid_2, q1_.\"user-name\" AS username_3, q0_.\"parent-id\" AS parentid_4 FROM \"quote-group\" q0_ INNER JOIN \"quote-users-groups\" q2_ ON q0_.\"group-id\" = q2_.\"group-id\" INNER JOIN \"quote-user\" q1_ ON q1_.\"user-id\" = q2_.\"user-id\"',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT g, p FROM Doctrine\\Tests\\Models\\Quote\\Group g JOIN g.parent p',\n            'SELECT q0_.\"group-id\" AS groupid_0, q0_.\"group-name\" AS groupname_1, q1_.\"group-id\" AS groupid_2, q1_.\"group-name\" AS groupname_3, q0_.\"parent-id\" AS parentid_4, q1_.\"parent-id\" AS parentid_5 FROM \"quote-group\" q0_ INNER JOIN \"quote-group\" q1_ ON q0_.\"parent-id\" = q1_.\"group-id\"',\n        );\n    }\n\n    #[Group('DDC-2208')]\n    public function testCaseThenParameterArithmeticExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE 0 END) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE e.salary + 0 END) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE c0_.salary + 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT SUM(CASE WHEN e.salary <= :value THEN (e.salary - :value) WHEN e.salary >= :value THEN (:value - e.salary) ELSE (e.salary + :value) END) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e',\n            'SELECT SUM(CASE WHEN c0_.salary <= ? THEN (c0_.salary - ?) WHEN c0_.salary >= ? THEN (? - c0_.salary) ELSE (c0_.salary + ?) END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id',\n        );\n    }\n\n    #[Group('DDC-2268')]\n    public function testCaseThenFunction(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertSqlGeneration(\n            'SELECT CASE WHEN LENGTH(u.name) <> 0 THEN CONCAT(u.id, u.name) ELSE u.id END AS name  FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT CASE WHEN LENGTH(c0_.name) <> 0 THEN c0_.id || c0_.name ELSE c0_.id END AS sclr_0 FROM cms_users c0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT CASE WHEN LENGTH(u.name) <> LENGTH(TRIM(u.name)) THEN TRIM(u.name) ELSE u.name END AS name  FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT CASE WHEN LENGTH(c0_.name) <> LENGTH(TRIM(c0_.name)) THEN TRIM(c0_.name) ELSE c0_.name END AS sclr_0 FROM cms_users c0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT CASE WHEN LENGTH(u.name) > :value THEN SUBSTRING(u.name, 0, :value) ELSE TRIM(u.name) END AS name  FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT CASE WHEN LENGTH(c0_.name) > ? THEN SUBSTRING(c0_.name FROM 0 FOR ?) ELSE TRIM(c0_.name) END AS sclr_0 FROM cms_users c0_',\n        );\n    }\n\n    #[Group('DDC-2268')]\n    public function testSupportsMoreThanTwoParametersInConcatFunction(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertSqlGeneration(\n            \"SELECT u.id FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1\",\n            \"SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, c0_.status, 's') = ?\",\n        );\n        $this->assertSqlGeneration(\n            'SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = ?1',\n            'SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?',\n        );\n    }\n\n     #[Group('DDC-2188')]\n    public function testArithmeticPriority(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT 100/(2*2) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT 100 / (2 * 2) AS sclr_0 FROM cms_users c0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT (u.id / (u.id * 2)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u',\n            'SELECT (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT 100/(2*2) + (u.id / (u.id * 2)) FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE (u.id / (u.id * 2)) > 0',\n            'SELECT 100 / (2 * 2) + (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_ WHERE (c0_.id / (c0_.id * 2)) > 0',\n        );\n    }\n\n    #[Group('DDC-2475')]\n    public function testOrderByClauseShouldReplaceOrderByRelationMapping(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT r, b FROM Doctrine\\Tests\\Models\\Routing\\RoutingRoute r JOIN r.bookings b',\n            'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT r, b FROM Doctrine\\Tests\\Models\\Routing\\RoutingRoute r JOIN r.bookings b ORDER BY b.passengerName DESC',\n            'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC',\n        );\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportIsNullExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING u.username IS NULL',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING c0_.username IS NULL',\n        );\n\n        $this->assertSqlGeneration(\n            'SELECT u.name FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING MAX(u.name) IS NULL',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING MAX(c0_.name) IS NULL',\n        );\n    }\n\n    #[Group('DDC-2506')]\n    public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT e.id FROM Doctrine\\Tests\\Models\\Company\\CompanyOrganization o JOIN o.events e WITH e.id = ?1',\n            'SELECT c0_.id AS id_0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)',\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceLeftJoinWithCondition(): void\n    {\n        // Regression test for the bug\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e LEFT JOIN Doctrine\\Tests\\Models\\Company\\CompanyContract c ON c.salesPerson = e.id',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceLeftJoinWithConditionAndWhere(): void\n    {\n        // Ensure other WHERE predicates are passed through to the main WHERE clause\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e LEFT JOIN Doctrine\\Tests\\Models\\Company\\CompanyContract c ON c.salesPerson = e.id WHERE e.salary > 1000',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000\",\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceInnerJoinWithCondition(): void\n    {\n        // Test inner joins too\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e INNER JOIN Doctrine\\Tests\\Models\\Company\\CompanyContract c ON c.salesPerson = e.id',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceLeftJoinNonAssociationWithConditionAndWhere(): void\n    {\n        // Test that the discriminator IN() predicate is still added into\n        // the where clause when not joining onto that table\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c LEFT JOIN Doctrine\\Tests\\Models\\Company\\CompanyEmployee e ON e.id = c.salesPerson WHERE c.completed = true',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ LEFT JOIN (company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id) ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceJoinCreatesOnCondition(): void\n    {\n        // Test that the discriminator IN() predicate is still added\n        // into the where clause when not joining onto a single table inheritance entity\n        // via a join association\n        $this->assertSqlGeneration(\n            'SELECT c FROM Doctrine\\Tests\\Models\\Company\\CompanyContract c JOIN c.salesPerson s WHERE c.completed = true',\n            \"SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')\",\n        );\n    }\n\n    #[Group('DDC-2235')]\n    public function testSingleTableInheritanceCreatesOnConditionAndWhere(): void\n    {\n        // Test that when joining onto an entity using single table inheritance via\n        // a join association that the discriminator IN() predicate is placed\n        // into the ON clause of the join\n        $this->assertSqlGeneration(\n            'SELECT e, COUNT(c) FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee e JOIN e.contracts c WHERE e.department = :department',\n            \"SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, COUNT(c3_.id) AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN company_contract_employees c4_ ON c1_.id = c4_.employee_id INNER JOIN company_contracts c3_ ON c3_.id = c4_.contract_id AND c3_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?\",\n            [],\n            ['department' => 'foobar'],\n        );\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportResultVariableInExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u.name AS foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING foo IN (?1)',\n            'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 IN (?)',\n        );\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportResultVariableLikeExpression(): void\n    {\n        $this->assertSqlGeneration(\n            \"SELECT u.name AS foo FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING foo LIKE '3'\",\n            \"SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 LIKE '3'\",\n        );\n    }\n\n    #[Group('DDC-3085')]\n    public function testHavingSupportResultVariableNullComparisonExpression(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT u AS user, SUM(a.id) AS score FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN Doctrine\\Tests\\Models\\CMS\\CmsAddress a ON a.user = u GROUP BY u HAVING score IS NOT NULL AND score >= 5',\n            'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, SUM(c1_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON (c1_.user_id = c0_.id) GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id HAVING sclr_4 IS NOT NULL AND sclr_4 >= 5',\n        );\n    }\n\n    #[Group('DDC-1858')]\n    public function testHavingSupportResultVariableInAggregateFunction(): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT COUNT(u.name) AS countName FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING countName IS NULL',\n            'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING sclr_0 IS NULL',\n        );\n    }\n\n    /**\n     * GitHub issue #4764: https://github.com/doctrine/orm/issues/4764\n     */\n    #[DataProvider('mathematicOperatorsProvider')]\n    #[Group('DDC-3907')]\n    public function testHavingRegressionUsingVariableWithMathOperatorsExpression($operator): void\n    {\n        $this->assertSqlGeneration(\n            'SELECT COUNT(u.name) AS countName FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u HAVING 1 ' . $operator . ' countName > 0',\n            'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING 1 ' . $operator . ' sclr_0 > 0',\n        );\n    }\n\n    /** @phpstan-return list<array{string}> */\n    public static function mathematicOperatorsProvider(): array\n    {\n        return [['+'], ['-'], ['*'], ['/']];\n    }\n}\n\nclass MyAbsFunction extends FunctionNode\n{\n    /** @var SimpleArithmeticExpression */\n    public $simpleArithmeticExpression;\n\n    public function getSql(SqlWalker $sqlWalker): string\n    {\n        return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) . ')';\n    }\n\n    public function parse(Parser $parser): void\n    {\n        $lexer = $parser->getLexer();\n\n        $parser->match(TokenType::T_IDENTIFIER);\n        $parser->match(TokenType::T_OPEN_PARENTHESIS);\n\n        $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();\n\n        $parser->match(TokenType::T_CLOSE_PARENTHESIS);\n    }\n}\n#[Entity]\nclass DDC1384Model\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalkerFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo;\n}\n\n\n#[Entity]\nclass DDC1474Entity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    protected $id;\n\n    public function __construct(\n        #[Column(type: 'float')]\n        private string $value,\n    ) {\n    }\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n\n    public function getValue(): float\n    {\n        return $this->value;\n    }\n\n    public function setValue(float $value): void\n    {\n        $this->value = $value;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/SqlExpressionVisitorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\Common\\Collections\\ExpressionBuilder as CriteriaBuilder;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister;\nuse Doctrine\\ORM\\Persisters\\SqlExpressionVisitor;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SqlExpressionVisitorTest extends TestCase\n{\n    private SqlExpressionVisitor $visitor;\n    private BasicEntityPersister&MockObject $persister;\n    private ClassMetadata $classMetadata;\n\n    protected function setUp(): void\n    {\n        $this->persister     = $this->createMock(BasicEntityPersister::class);\n        $this->classMetadata = new ClassMetadata('Dummy');\n        $this->visitor       = new SqlExpressionVisitor($this->persister, $this->classMetadata);\n    }\n\n    public function testWalkNotCompositeExpression(): void\n    {\n        $cb = new CriteriaBuilder();\n\n        $this->persister\n            ->expects(self::once())\n            ->method('getSelectConditionStatementSQL')\n            ->willReturn('dummy expression');\n\n        $expr = $this->visitor->walkCompositeExpression(\n            $cb->not(\n                $cb->eq('foo', 1),\n            ),\n        );\n\n        self::assertEquals('NOT (dummy expression)', $expr);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/SqlWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\ParserResult;\nuse Doctrine\\ORM\\Query\\SqlWalker;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Query\\SqlWalker}\n */\n#[CoversClass(SqlWalker::class)]\nclass SqlWalkerTest extends OrmTestCase\n{\n    private SqlWalker $sqlWalker;\n\n    protected function setUp(): void\n    {\n        $this->sqlWalker = new SqlWalker(new Query($this->getTestEntityManager()), new ParserResult(), []);\n    }\n\n    #[DataProvider('getColumnNamesAndSqlAliases')]\n    public function testGetSQLTableAlias($tableName, $expectedAlias): void\n    {\n        self::assertSame($expectedAlias, $this->sqlWalker->getSQLTableAlias($tableName));\n    }\n\n    #[DataProvider('getColumnNamesAndSqlAliases')]\n    public function testGetSQLTableAliasIsSameForMultipleCalls($tableName): void\n    {\n        self::assertSame(\n            $this->sqlWalker->getSQLTableAlias($tableName),\n            $this->sqlWalker->getSQLTableAlias($tableName),\n        );\n    }\n\n    /**\n     * @return string[][]\n     *\n     * @private data provider\n     */\n    public static function getColumnNamesAndSqlAliases(): array\n    {\n        return [\n            ['aaaaa', 'a0_'],\n            ['table', 't0_'],\n            ['çtable', 't0_'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Query/UpdateSqlGenerationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Query;\n\nuse Doctrine\\DBAL\\Types\\Type as DBALType;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\Tests\\DbalTypes\\NegativeToPositiveType;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Test case for testing the saving and referencing of query identifiers.\n *\n * @link        http://www.phpdoctrine.org\n *\n * @todo        1) [romanb] We  might want to split the SQL generation tests into multiple\n *              testcases later since we'll have a lot of them and we might want to have special SQL\n *              generation tests for some dbms specific SQL syntaxes.\n */\nclass UpdateSqlGenerationTest extends OrmTestCase\n{\n    private EntityManagerInterface $entityManager;\n\n    protected function setUp(): void\n    {\n        if (DBALType::hasType('negative_to_positive')) {\n            DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class);\n        } else {\n            DBALType::addType('negative_to_positive', NegativeToPositiveType::class);\n        }\n\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed): void\n    {\n        $query = $this->entityManager->createQuery($dqlToBeTested);\n\n        parent::assertEquals($sqlToBeConfirmed, $query->getSql());\n\n        $query->free();\n    }\n\n    public function testSupportsQueriesWithoutWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1',\n            'UPDATE cms_users SET name = ?',\n        );\n    }\n\n    public function testSupportsMultipleFieldsWithoutWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1, u.username = ?2',\n            'UPDATE cms_users SET name = ?, username = ?',\n        );\n    }\n\n    public function testSupportsWhereClauses(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.id = ?2',\n            'UPDATE cms_users SET name = ? WHERE id = ?',\n        );\n    }\n\n    public function testSupportsWhereClausesOnTheUpdatedField(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.name = ?2',\n            'UPDATE cms_users SET name = ? WHERE name = ?',\n        );\n    }\n\n    public function testSupportsMultipleWhereClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.name = ?2 AND u.status = ?3',\n            'UPDATE cms_users SET name = ? WHERE name = ? AND status = ?',\n        );\n    }\n\n    public function testSupportsInClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.id IN (1, 3, 4)',\n            'UPDATE cms_users SET name = ? WHERE id IN (1, 3, 4)',\n        );\n    }\n\n    public function testSupportsParametrizedInClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.id IN (?2, ?3, ?4)',\n            'UPDATE cms_users SET name = ? WHERE id IN (?, ?, ?)',\n        );\n    }\n\n    public function testSupportsNotInClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.name = ?1 WHERE u.id NOT IN (1, 3, 4)',\n            'UPDATE cms_users SET name = ? WHERE id NOT IN (1, 3, 4)',\n        );\n    }\n\n    public function testSupportsGreaterThanClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = ?1 WHERE u.id > ?2',\n            'UPDATE cms_users SET status = ? WHERE id > ?',\n        );\n    }\n\n    public function testSupportsGreaterThanOrEqualToClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = ?1 WHERE u.id >= ?2',\n            'UPDATE cms_users SET status = ? WHERE id >= ?',\n        );\n    }\n\n    public function testSupportsLessThanClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = ?1 WHERE u.id < ?2',\n            'UPDATE cms_users SET status = ? WHERE id < ?',\n        );\n    }\n\n    public function testSupportsLessThanOrEqualToClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = ?1 WHERE u.id <= ?2',\n            'UPDATE cms_users SET status = ? WHERE id <= ?',\n        );\n    }\n\n    public function testSupportsBetweenClause(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = ?1 WHERE u.id BETWEEN :from AND :to',\n            'UPDATE cms_users SET status = ? WHERE id BETWEEN ? AND ?',\n        );\n    }\n\n    public function testSingleValuedAssociationFieldInWhere(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber p SET p.phonenumber = 1234 WHERE p.user = ?1',\n            'UPDATE cms_phonenumbers SET phonenumber = 1234 WHERE user_id = ?',\n        );\n    }\n\n    public function testSingleValuedAssociationFieldInSetClause(): void\n    {\n        $this->assertSqlGeneration(\n            'update Doctrine\\Tests\\Models\\CMS\\CmsComment c set c.article = null where c.article=?1',\n            'UPDATE cms_comments SET article_id = NULL WHERE article_id = ?',\n        );\n    }\n\n    #[Group('DDC-980')]\n    public function testSubselectTableAliasReferencing(): void\n    {\n        $this->assertSqlGeneration(\n            \"UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.status = 'inactive' WHERE SIZE(u.groups) = 10\",\n            \"UPDATE cms_users SET status = 'inactive' WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10\",\n        );\n    }\n\n    public function testCustomTypeValueSqlCompletelyIgnoredInUpdateStatements(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent p SET p.customInteger = 1 WHERE p.id = 1',\n            'UPDATE customtype_parents SET customInteger = 1 WHERE id = 1',\n        );\n    }\n\n    public function testUpdateWithSubselectAsNewValue(): void\n    {\n        $this->assertSqlGeneration(\n            'UPDATE Doctrine\\Tests\\Models\\Company\\CompanyFixContract fc SET fc.fixPrice = (SELECT ce2.salary FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee ce2 WHERE ce2.id = 2) WHERE fc.id = 1',\n            \"UPDATE company_contracts SET fixPrice = (SELECT c0_.salary FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id WHERE c1_.id = 2) WHERE (id = 1) AND discr IN ('fix')\",\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/QueryBuilderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse BadMethodCallException;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Criteria;\nuse Doctrine\\Common\\Collections\\Order;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse Doctrine\\ORM\\Cache;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Expr\\Join;\nuse Doctrine\\ORM\\Query\\Parameter;\nuse Doctrine\\ORM\\Query\\ParameterTypeInferer;\nuse Doctrine\\ORM\\QueryBuilder;\nuse Doctrine\\ORM\\QueryType;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\OrmTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\Attributes\\WithoutErrorHandler;\nuse PHPUnit\\Framework\\TestCase;\nuse RuntimeException;\n\nuse function array_filter;\n\n/**\n * Test case for the QueryBuilder class used to build DQL query string in a\n * object oriented way.\n */\nclass QueryBuilderTest extends OrmTestCase\n{\n    use VerifyDeprecations;\n\n    private EntityManagerMock $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n\n    protected function assertValidQueryBuilder(QueryBuilder $qb, string $expectedDql): void\n    {\n        $dql = $qb->getDQL();\n        $q   = $qb->getQuery();\n\n        self::assertEquals($expectedDql, $dql);\n    }\n\n    public function testSelectSetsType(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testDeleteSetsType(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->delete();\n\n        $this->assertValidQueryBuilder($qb, 'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testDeleteWithLimitNotSupported(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Setting a limit is not supported for delete or update queries.');\n\n        $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'c')\n            ->setMaxResults(1);\n    }\n\n    public function testUpdateWithLimitNotSupported(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Setting a limit is not supported for delete or update queries.');\n\n        $this->entityManager->createQueryBuilder()\n            ->update(CmsUser::class, 'c')\n            ->setMaxResults(1);\n    }\n\n    public function testUpdateSetsType(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->update();\n\n        $this->assertValidQueryBuilder($qb, 'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSimpleSelect(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select('u.id', 'u.username');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSimpleSelectArray(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->select(['u.id', 'u.username']);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSimpleDelete(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u');\n\n        $this->assertValidQueryBuilder($qb, 'DELETE Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n    }\n\n    public function testSimpleSelectWithFromIndexBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u', 'u.id')\n            ->select('u.id', 'u.username');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INDEX BY u.id');\n    }\n\n    public function testSimpleSelectWithIndexBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->from(CmsUser::class, 'u')\n            ->indexBy('u', 'u.id')\n            ->select('u.id', 'u.username');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INDEX BY u.id');\n    }\n\n    public function testSimpleUpdate(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->update(CmsUser::class, 'u')\n            ->set('u.username', ':username');\n\n        $this->assertValidQueryBuilder($qb, 'UPDATE Doctrine\\Tests\\Models\\CMS\\CmsUser u SET u.username = :username');\n    }\n\n    public function testInnerJoin(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin('u.articles', 'a');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a');\n    }\n\n    public function testComplexInnerJoin(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id');\n\n        $this->assertValidQueryBuilder(\n            $qb,\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id',\n        );\n    }\n\n    public function testComplexInnerJoinWithComparisonCondition(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin(CmsArticle::class, 'a', Join::ON, $qb->expr()->eq('u.id', 'a.author_id'));\n\n        $this->assertValidQueryBuilder(\n            $qb,\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a ON u.id = a.author_id',\n        );\n    }\n\n    public function testComplexInnerJoinWithCompositeCondition(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin('u.articles', 'a', Join::ON, $qb->expr()->andX(\n                $qb->expr()->eq('u.id', 'a.author_id'),\n                $qb->expr()->isNotNull('u.name'),\n            ));\n\n        $this->assertValidQueryBuilder(\n            $qb,\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id AND u.name IS NOT NULL',\n        );\n    }\n\n    public function testComplexInnerJoinWithFuncCondition(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin('u.articles', 'a', Join::WITH, $qb->expr()->in(\n                'u.id',\n                [1, 2, 3],\n            ));\n\n        $this->assertValidQueryBuilder(\n            $qb,\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a WITH u.id IN(1, 2, 3)',\n        );\n    }\n\n    public function testComplexInnerJoinWithIndexBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id', 'a.name');\n\n        $this->assertValidQueryBuilder(\n            $qb,\n            'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a INDEX BY a.name ON u.id = a.author_id',\n        );\n    }\n\n    public function testLeftJoin(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->leftJoin('u.articles', 'a');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a');\n    }\n\n    public function testLeftJoinWithIndexBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'a')\n            ->from(CmsUser::class, 'u')\n            ->leftJoin('u.articles', 'a', null, null, 'a.name');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN u.articles a INDEX BY a.name');\n    }\n\n    public function testMultipleFrom(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'g')\n            ->from(CmsUser::class, 'u')\n            ->from(CmsGroup::class, 'g');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsGroup g');\n    }\n\n    public function testMultipleFromWithIndexBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'g')\n            ->from(CmsUser::class, 'u')\n            ->from(CmsGroup::class, 'g')\n            ->indexBy('g', 'g.id');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u, Doctrine\\Tests\\Models\\CMS\\CmsGroup g INDEX BY g.id');\n    }\n\n    public function testMultipleFromWithJoin(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'g')\n            ->from(CmsUser::class, 'u')\n            ->from(CmsGroup::class, 'g')\n            ->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id, Doctrine\\Tests\\Models\\CMS\\CmsGroup g');\n    }\n\n    public function testMultipleFromWithMultipleJoin(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u', 'g')\n            ->from(CmsUser::class, 'u')\n            ->from(CmsArticle::class, 'a')\n            ->innerJoin('u.groups', 'g')\n            ->leftJoin('u.address', 'ad')\n            ->innerJoin('a.comments', 'c');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.groups g LEFT JOIN u.address ad, Doctrine\\Tests\\Models\\CMS\\CmsArticle a INNER JOIN a.comments c');\n    }\n\n    public function testWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :uid');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid');\n    }\n\n    public function testWhereWithUnexpectedNamedArguments(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n\n        $this->expectException(BadMethodCallException::class);\n        $this->expectExceptionMessage('Invalid call to Doctrine\\ORM\\QueryBuilder::where(), unknown named arguments: foo, bar');\n\n        $qb->where(foo: 'u.id = :uid', bar: 'u.name = :name');\n    }\n\n    public function testComplexAndWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :uid OR u.id = :uid2 OR u.id = :uid3')\n            ->andWhere('u.name = :name');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE (u.id = :uid OR u.id = :uid2 OR u.id = :uid3) AND u.name = :name');\n    }\n\n    public function testAndWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :uid')\n            ->andWhere('u.id = :uid2');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid AND u.id = :uid2');\n    }\n\n    public function testOrWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :uid')\n            ->orWhere('u.id = :uid2');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid OR u.id = :uid2');\n    }\n\n    public function testComplexAndWhereOrWhereNesting(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :uid')\n           ->orWhere('u.id = :uid2')\n           ->andWhere('u.id = :uid3')\n           ->orWhere('u.name = :name1', 'u.name = :name2')\n           ->andWhere('u.name <> :noname');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE (((u.id = :uid OR u.id = :uid2) AND u.id = :uid3) OR u.name = :name1 OR u.name = :name2) AND u.name <> :noname');\n    }\n\n    public function testAndWhereIn(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :uid')\n           ->andWhere($qb->expr()->in('u.id', [1, 2, 3]));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid AND u.id IN(1, 2, 3)');\n    }\n\n    public function testOrWhereIn(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :uid')\n           ->orWhere($qb->expr()->in('u.id', [1, 2, 3]));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid OR u.id IN(1, 2, 3)');\n    }\n\n    public function testAndWhereNotIn(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :uid')\n           ->andWhere($qb->expr()->notIn('u.id', [1, 2, 3]));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid AND u.id NOT IN(1, 2, 3)');\n    }\n\n    public function testOrWhereNotIn(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :uid')\n           ->orWhere($qb->expr()->notIn('u.id', [1, 2, 3]));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid OR u.id NOT IN(1, 2, 3)');\n    }\n\n    public function testGroupBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->groupBy('u.id')\n            ->addGroupBy('u.username');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id, u.username');\n    }\n\n    public function testHaving(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->groupBy('u.id')\n            ->having('COUNT(u.id) > 1');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id HAVING COUNT(u.id) > 1');\n    }\n\n    public function testAndHaving(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->groupBy('u.id')\n            ->having('COUNT(u.id) > 1')\n            ->andHaving('COUNT(u.id) < 1');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id HAVING COUNT(u.id) > 1 AND COUNT(u.id) < 1');\n    }\n\n    public function testOrHaving(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->groupBy('u.id')\n            ->having('COUNT(u.id) > 1')\n            ->andHaving('COUNT(u.id) < 1')\n            ->orHaving('COUNT(u.id) > 1');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u GROUP BY u.id HAVING (COUNT(u.id) > 1 AND COUNT(u.id) < 1) OR COUNT(u.id) > 1');\n    }\n\n    public function testOrderBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->orderBy('u.username', 'ASC');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username ASC');\n    }\n\n    public function testOrderByWithExpression(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->orderBy($qb->expr()->asc('u.username'));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username ASC');\n    }\n\n    public function testAddOrderBy(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->orderBy('u.username', 'ASC')\n            ->addOrderBy('u.username', 'DESC');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username ASC, u.username DESC');\n    }\n\n    public function testAddOrderByWithExpression(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->orderBy('u.username', 'ASC')\n            ->addOrderBy($qb->expr()->desc('u.username'));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u ORDER BY u.username ASC, u.username DESC');\n    }\n\n    public function testAddCriteriaWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field', 'value'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('u.field = :field', (string) $qb->getDQLPart('where'));\n        self::assertNotNull($qb->getParameter('field'));\n    }\n\n    public function testAddMultipleSameCriteriaWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->andX(\n            $criteria->expr()->eq('field', 'value1'),\n            $criteria->expr()->eq('field', 'value2'),\n        ));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field = :field AND alias1.field = :field_1', (string) $qb->getDQLPart('where'));\n        self::assertNotNull($qb->getParameter('field'));\n        self::assertNotNull($qb->getParameter('field_1'));\n    }\n\n    #[Group('DDC-2844')]\n    public function testAddCriteriaWhereWithMultipleParametersWithSameField(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('field', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field = :field AND alias1.field > :field_1', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('field')->getValue());\n        self::assertSame('value2', $qb->getParameter('field_1')->getValue());\n    }\n\n    #[Group('DDC-2844')]\n    public function testAddCriteriaWhereWithMultipleParametersWithDifferentFields(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field1', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('field2', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field1 = :field1 AND alias1.field2 > :field2', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('field1')->getValue());\n        self::assertSame('value2', $qb->getParameter('field2')->getValue());\n    }\n\n    #[Group('DDC-2844')]\n    public function testAddCriteriaWhereWithMultipleParametersWithSubpathsAndDifferentProperties(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field1', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('field2', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field1 = :field1 AND alias1.field2 > :field2', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('field1')->getValue());\n        self::assertSame('value2', $qb->getParameter('field2')->getValue());\n    }\n\n    #[Group('DDC-2844')]\n    public function testAddCriteriaWhereWithMultipleParametersWithSubpathsAndSameProperty(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field1', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('field1', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field1 = :field1 AND alias1.field1 > :field1_1', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('field1')->getValue());\n        self::assertSame('value2', $qb->getParameter('field1_1')->getValue());\n    }\n\n    public function testAddCriteriaOrder(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u');\n\n        $criteria = Criteria::create(true);\n        $criteria->orderBy(['field' => Order::Descending]);\n\n        $qb->addCriteria($criteria);\n\n        self::assertCount(1, $orderBy = $qb->getDQLPart('orderBy'));\n        self::assertEquals('u.field DESC', (string) $orderBy[0]);\n    }\n\n    #[Group('DDC-3108')]\n    public function testAddCriteriaOrderOnJoinAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->join('u.article', 'a');\n\n        $criteria = Criteria::create(true);\n        $criteria->orderBy(['a.field' => Order::Descending]);\n\n        $qb->addCriteria($criteria);\n\n        self::assertCount(1, $orderBy = $qb->getDQLPart('orderBy'));\n        self::assertEquals('a.field DESC', (string) $orderBy[0]);\n    }\n\n    public function testAddCriteriaLimit(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u');\n\n        $criteria = Criteria::create(true);\n        $criteria->setFirstResult(2);\n        $criteria->setMaxResults(10);\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals(2, $qb->getFirstResult());\n        self::assertEquals(10, $qb->getMaxResults());\n    }\n\n    public function testAddCriteriaUndefinedLimit(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->setFirstResult(2)\n            ->setMaxResults(10);\n\n        $criteria = Criteria::create(true);\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals(2, $qb->getFirstResult());\n        self::assertEquals(10, $qb->getMaxResults());\n    }\n\n    public function testGetQuery(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n        $q  = $qb->getQuery();\n\n        self::assertEquals(Query::class, $q::class);\n    }\n\n    public function testSetParameter(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :id')\n            ->setParameter('id', 1);\n\n        $parameter = new Parameter('id', 1, ParameterTypeInferer::inferType(1));\n        $inferred  = $qb->getParameter('id');\n\n        self::assertSame($parameter->getValue(), $inferred->getValue());\n        self::assertSame($parameter->getType(), $inferred->getType());\n        self::assertFalse($inferred->typeWasSpecified());\n    }\n\n    public function testSetParameters(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($qb->expr()->orX('u.username = :username', 'u.username = :username2'));\n\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter('username', 'jwage'));\n        $parameters->add(new Parameter('username2', 'jonwage'));\n\n        $qb->setParameters($parameters);\n\n        self::assertEquals($parameters, $qb->getQuery()->getParameters());\n    }\n\n    public function testGetParameters(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where('u.id = :id');\n\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter('id', 1));\n\n        $qb->setParameters($parameters);\n\n        self::assertEquals($parameters, $qb->getParameters());\n    }\n\n    public function testGetParameter(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :id');\n\n        $parameters = new ArrayCollection();\n        $parameters->add(new Parameter('id', 1));\n\n        $qb->setParameters($parameters);\n\n        self::assertEquals($parameters->first(), $qb->getParameter('id'));\n    }\n\n    public function testMultipleWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.id = :uid', 'u.id = :uid2');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid AND u.id = :uid2');\n    }\n\n    public function testMultipleAndWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->andWhere('u.id = :uid', 'u.id = :uid2');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid AND u.id = :uid2');\n    }\n\n    public function testMultipleOrWhere(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->orWhere('u.id = :uid', $qb->expr()->eq('u.id', ':uid2'));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid OR u.id = :uid2');\n    }\n\n    public function testComplexWhere(): void\n    {\n        $qb     = $this->entityManager->createQueryBuilder();\n        $orExpr = $qb->expr()->orX();\n        $orExpr->add($qb->expr()->eq('u.id', ':uid3'));\n        $orExpr->add($qb->expr()->in('u.id', [1]));\n\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($orExpr);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid3 OR u.id IN(1)');\n    }\n\n    public function testWhereInWithStringLiterals(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($qb->expr()->in('u.name', ['one', 'two', 'three']));\n\n        $this->assertValidQueryBuilder($qb, \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IN('one', 'two', 'three')\");\n\n        $qb->where($qb->expr()->in('u.name', [\"O'Reilly\", \"O'Neil\", 'Smith']));\n\n        $this->assertValidQueryBuilder($qb, \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IN('O''Reilly', 'O''Neil', 'Smith')\");\n    }\n\n    public function testWhereInWithObjectLiterals(): void\n    {\n        $qb   = $this->entityManager->createQueryBuilder();\n        $expr = $this->entityManager->getExpressionBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($expr->in('u.name', [$expr->literal('one'), $expr->literal('two'), $expr->literal('three')]));\n\n        $this->assertValidQueryBuilder($qb, \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IN('one', 'two', 'three')\");\n\n        $qb->where($expr->in('u.name', [$expr->literal(\"O'Reilly\"), $expr->literal(\"O'Neil\"), $expr->literal('Smith')]));\n\n        $this->assertValidQueryBuilder($qb, \"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name IN('O''Reilly', 'O''Neil', 'Smith')\");\n    }\n\n    public function testNegation(): void\n    {\n        $expr   = $this->entityManager->getExpressionBuilder();\n        $orExpr = $expr->orX();\n        $orExpr->add($expr->eq('u.id', ':uid3'));\n        $orExpr->add($expr->not($expr->in('u.id', [1])));\n\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($orExpr);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id = :uid3 OR NOT(u.id IN(1))');\n    }\n\n    public function testSomeAllAny(): void\n    {\n        $qb   = $this->entityManager->createQueryBuilder();\n        $expr = $this->entityManager->getExpressionBuilder();\n\n        $qb->select('u')\n           ->from(CmsUser::class, 'u')\n           ->where($expr->gt('u.id', $expr->all('select a.id from Doctrine\\Tests\\Models\\CMS\\CmsArticle a')));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.id > ALL(select a.id from Doctrine\\Tests\\Models\\CMS\\CmsArticle a)');\n    }\n\n    public function testMultipleIsolatedQueryConstruction(): void\n    {\n        $qb   = $this->entityManager->createQueryBuilder();\n        $expr = $this->entityManager->getExpressionBuilder();\n\n        $qb->select('u')->from(CmsUser::class, 'u');\n        $qb->where($expr->eq('u.name', ':name'));\n        $qb->setParameter('name', 'romanb');\n\n        $q1 = $qb->getQuery();\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = :name', $q1->getDQL());\n        self::assertCount(1, $q1->getParameters());\n\n        // add another condition and construct a second query\n        $qb->andWhere($expr->eq('u.id', ':id'));\n        $qb->setParameter('id', 42);\n\n        $q2 = $qb->getQuery();\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = :name AND u.id = :id', $q2->getDQL());\n        self::assertNotSame($q1, $q2); // two different, independent queries\n        self::assertCount(2, $q2->getParameters());\n        self::assertCount(1, $q1->getParameters()); // $q1 unaffected\n    }\n\n    public function testGetEntityManager(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        self::assertEquals($this->entityManager, $qb->getEntityManager());\n    }\n\n    public function testSelectWithFuncExpression(): void\n    {\n        $qb   = $this->entityManager->createQueryBuilder();\n        $expr = $qb->expr();\n        $qb->select($expr->count('e.id'));\n\n        $this->assertValidQueryBuilder($qb, 'SELECT COUNT(e.id)');\n    }\n\n    public function testResetDQLPart(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.username = ?1')->orderBy('u.username');\n\n        self::assertEquals('u.username = ?1', (string) $qb->getDQLPart('where'));\n        self::assertCount(1, $qb->getDQLPart('orderBy'));\n\n        $qb->resetDQLPart('where')->resetDQLPart('orderBy');\n\n        self::assertNull($qb->getDQLPart('where'));\n        self::assertCount(0, $qb->getDQLPart('orderBy'));\n    }\n\n    public function testResetDQLParts(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.username = ?1')->orderBy('u.username');\n\n        $qb->resetDQLParts(['where', 'orderBy']);\n\n        self::assertCount(1, $qb->getDQLPart('select'));\n        self::assertNull($qb->getDQLPart('where'));\n        self::assertCount(0, $qb->getDQLPart('orderBy'));\n    }\n\n    public function testResetAllDQLParts(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where('u.username = ?1')->orderBy('u.username');\n\n        $qb->resetDQLParts();\n\n        self::assertCount(0, $qb->getDQLPart('select'));\n        self::assertNull($qb->getDQLPart('where'));\n        self::assertCount(0, $qb->getDQLPart('orderBy'));\n    }\n\n    #[Group('DDC-867')]\n    public function testDeepClone(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->andWhere('u.username = ?1')\n            ->andWhere('u.status = ?2');\n\n        $expr = $qb->getDQLPart('where');\n        self::assertEquals(2, $expr->count(), 'Modifying the second query should affect the first one.');\n\n        $qb2 = clone $qb;\n        $qb2->andWhere('u.name = ?3');\n\n        self::assertEquals(2, $expr->count(), 'Modifying the second query should affect the first one.');\n    }\n\n    #[Group('DDC-3108')]\n    public function testAddCriteriaWhereWithJoinAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n        $qb->join('alias1.articles', 'alias2');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('field', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field = :field AND alias2.field > :alias2_field', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('field')->getValue());\n        self::assertSame('value2', $qb->getParameter('alias2_field')->getValue());\n    }\n\n    #[Group('DDC-3108')]\n    public function testAddCriteriaWhereWithDefaultAndJoinAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n        $qb->join('alias1.articles', 'alias2');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('alias1.field', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('alias1.field = :alias1_field AND alias2.field > :alias2_field', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('alias1_field')->getValue());\n        self::assertSame('value2', $qb->getParameter('alias2_field')->getValue());\n    }\n\n    #[Group('DDC-3108')]\n    public function testAddCriteriaWhereOnJoinAliasWithDuplicateFields(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('alias1')->from(CmsUser::class, 'alias1');\n        $qb->join('alias1.articles', 'alias2');\n\n        $criteria = Criteria::create(true);\n        $criteria->where($criteria->expr()->eq('alias1.field', 'value1'));\n        $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2'));\n        $criteria->andWhere($criteria->expr()->lt('alias2.field', 'value3'));\n\n        $qb->addCriteria($criteria);\n\n        self::assertEquals('(alias1.field = :alias1_field AND alias2.field > :alias2_field) AND alias2.field < :alias2_field_2', (string) $qb->getDQLPart('where'));\n        self::assertSame('value1', $qb->getParameter('alias1_field')->getValue());\n        self::assertSame('value2', $qb->getParameter('alias2_field')->getValue());\n        self::assertSame('value3', $qb->getParameter('alias2_field_2')->getValue());\n    }\n\n    #[Group('DDC-1933')]\n    public function testParametersAreCloned(): void\n    {\n        $originalQb = new QueryBuilder($this->entityManager);\n\n        $originalQb->setParameter('parameter1', 'value1');\n\n        $copy = clone $originalQb;\n        $copy->setParameter('parameter2', 'value2');\n\n        self::assertCount(1, $originalQb->getParameters());\n        self::assertSame('value1', $copy->getParameter('parameter1')->getValue());\n        self::assertSame('value2', $copy->getParameter('parameter2')->getValue());\n    }\n\n    public function testGetRootAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n\n        self::assertEquals('u', $qb->getRootAlias());\n    }\n\n    public function testGetRootAliases(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n\n        self::assertEquals(['u'], $qb->getRootAliases());\n    }\n\n    public function testGetRootEntities(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n\n        self::assertEquals([CmsUser::class], $qb->getRootEntities());\n    }\n\n    public function testGetSeveralRootAliases(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->from(CmsUser::class, 'u2');\n\n        self::assertEquals(['u', 'u2'], $qb->getRootAliases());\n        self::assertEquals('u', $qb->getRootAlias());\n    }\n\n    #[WithoutErrorHandler]\n    public function testBCAddJoinWithoutRootAlias(): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/12051');\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->add('join', ['INNER JOIN u.groups g'], true);\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u INNER JOIN u.groups g', $qb->getDQL());\n    }\n\n    #[Group('DDC-1211')]\n    public function testEmptyStringLiteral(): void\n    {\n        $expr = $this->entityManager->getExpressionBuilder();\n        $qb   = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where($expr->eq('u.username', $expr->literal('')));\n\n        self::assertEquals(\"SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = ''\", $qb->getDQL());\n    }\n\n    #[Group('DDC-1211')]\n    public function testEmptyNumericLiteral(): void\n    {\n        $expr = $this->entityManager->getExpressionBuilder();\n        $qb   = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where($expr->eq('u.username', $expr->literal(0)));\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.username = 0', $qb->getDQL());\n    }\n\n    #[Group('DDC-1227')]\n    public function testAddFromString(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->add('select', 'u')\n            ->add('from', CmsUser::class . ' u');\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $qb->getDQL());\n    }\n\n    #[Group('DDC-1619')]\n    public function testAddDistinct(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->distinct()\n            ->from(CmsUser::class, 'u');\n\n        self::assertEquals('SELECT DISTINCT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $qb->getDQL());\n    }\n\n    public function testDistinctUpdatesState(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u');\n\n        $qb->getDQL();\n        $qb->distinct();\n\n        self::assertEquals('SELECT DISTINCT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u', $qb->getDQL());\n    }\n\n    #[Group('DDC-2192')]\n    public function testWhereAppend(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Using \\$append = true does not have an effect with 'where' or 'having' parts. See QueryBuilder#andWhere() for an example for correct usage.\");\n\n        $qb = $this->entityManager->createQueryBuilder()\n            ->add('where', 'u.foo = ?1')\n            ->add('where', 'u.bar = ?2', true);\n    }\n\n    public function testSecondLevelCacheQueryBuilderOptions(): void\n    {\n        $defaultQueryBuilder = $this->entityManager->createQueryBuilder()\n            ->select('s')\n            ->from(State::class, 's');\n\n        self::assertFalse($defaultQueryBuilder->isCacheable());\n        self::assertEquals(0, $defaultQueryBuilder->getLifetime());\n        self::assertNull($defaultQueryBuilder->getCacheRegion());\n        self::assertNull($defaultQueryBuilder->getCacheMode());\n\n        $defaultQuery = $defaultQueryBuilder->getQuery();\n\n        self::assertFalse($defaultQuery->isCacheable());\n        self::assertEquals(0, $defaultQuery->getLifetime());\n        self::assertNull($defaultQuery->getCacheRegion());\n        self::assertNull($defaultQuery->getCacheMode());\n\n        $builder = $this->entityManager->createQueryBuilder()\n            ->select('s')\n            ->setLifetime(123)\n            ->setCacheable(true)\n            ->setCacheRegion('foo_reg')\n            ->setCacheMode(Cache::MODE_REFRESH)\n            ->from(State::class, 's');\n\n        self::assertTrue($builder->isCacheable());\n        self::assertEquals(123, $builder->getLifetime());\n        self::assertEquals('foo_reg', $builder->getCacheRegion());\n        self::assertEquals(Cache::MODE_REFRESH, $builder->getCacheMode());\n\n        $query = $builder->getQuery();\n\n        self::assertTrue($query->isCacheable());\n        self::assertEquals(123, $query->getLifetime());\n        self::assertEquals('foo_reg', $query->getCacheRegion());\n        self::assertEquals(Cache::MODE_REFRESH, $query->getCacheMode());\n    }\n\n    #[Group('DDC-2253')]\n    public function testRebuildsFromParts(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n          ->select('u')\n          ->from(CmsUser::class, 'u')\n          ->join('u.article', 'a');\n\n        $dqlParts = $qb->getDQLParts();\n        $dql      = $qb->getDQL();\n\n        $qb2 = $this->entityManager->createQueryBuilder();\n        foreach (array_filter($dqlParts) as $name => $part) {\n            $qb2->add($name, $part);\n        }\n\n        $dql2 = $qb2->getDQL();\n\n        self::assertEquals($dql, $dql2);\n    }\n\n    public function testGetAllAliasesWithNoJoins(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n        $qb->select('u')->from(CmsUser::class, 'u');\n\n        $aliases = $qb->getAllAliases();\n\n        self::assertEquals(['u'], $aliases);\n    }\n\n    public function testGetAllAliasesWithJoins(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->join('u.groups', 'g');\n\n        $aliases = $qb->getAllAliases();\n\n        self::assertEquals(['u', 'g'], $aliases);\n    }\n\n    #[Group('6699')]\n    public function testGetParameterTypeJuggling(): void\n    {\n        $builder = $this->entityManager->createQueryBuilder()\n                             ->select('u')\n                             ->from(CmsUser::class, 'u')\n                             ->where('u.id = ?0');\n\n        $builder->setParameter(0, 0);\n\n        self::assertCount(1, $builder->getParameters());\n        self::assertSame(0, $builder->getParameter(0)->getValue());\n        self::assertSame(0, $builder->getParameter('0')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithNameZeroIsNotOverridden(): void\n    {\n        $builder = $this->entityManager->createQueryBuilder()\n                             ->select('u')\n                             ->from(CmsUser::class, 'u')\n                             ->where('u.id != ?0')\n                             ->andWhere('u.username = :name');\n\n        $builder->setParameter(0, 0);\n        $builder->setParameter('name', 'Doctrine');\n\n        self::assertCount(2, $builder->getParameters());\n        self::assertSame(0, $builder->getParameter('0')->getValue());\n        self::assertSame('Doctrine', $builder->getParameter('name')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): void\n    {\n        $builder = $this->entityManager->createQueryBuilder()\n                             ->select('u')\n                             ->from(CmsUser::class, 'u')\n                             ->where('u.id != ?0')\n                             ->andWhere('u.username = :name');\n\n        $builder->setParameter('name', 'Doctrine');\n        $builder->setParameter(0, 0);\n\n        self::assertCount(2, $builder->getParameters());\n        self::assertSame(0, $builder->getParameter(0)->getValue());\n        self::assertSame('Doctrine', $builder->getParameter('name')->getValue());\n    }\n\n    #[Group('6699')]\n    public function testSetParameterWithTypeJugglingWorks(): void\n    {\n        $builder = $this->entityManager->createQueryBuilder()\n                             ->select('u')\n                             ->from(CmsUser::class, 'u')\n                             ->where('u.id != ?0')\n                             ->andWhere('u.username = :name');\n\n        $builder->setParameter('0', 1);\n        $builder->setParameter('name', 'Doctrine');\n        $builder->setParameter(0, 2);\n        $builder->setParameter('0', 3);\n\n        self::assertCount(2, $builder->getParameters());\n        self::assertSame(3, $builder->getParameter(0)->getValue());\n        self::assertSame(3, $builder->getParameter('0')->getValue());\n        self::assertSame('Doctrine', $builder->getParameter('name')->getValue());\n    }\n\n    public function testJoin(): void\n    {\n        $builder = $this->entityManager->createQueryBuilder()\n            ->select('u')\n            ->from(CmsUser::class, 'u')\n            ->leftJoin(CmsArticle::class, 'a0')\n            ->innerJoin(CmsArticle::class, 'a1');\n\n        self::assertSame('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u LEFT JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a0 INNER JOIN Doctrine\\Tests\\Models\\CMS\\CmsArticle a1', $builder->getDQL());\n    }\n\n    public function testUpdateWithoutAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Doctrine\\ORM\\QueryBuilder::update(): The alias for entity Doctrine\\Tests\\Models\\CMS\\CmsUser u must not be omitted.');\n        $qb->update(CmsUser::class . ' u');\n    }\n\n    public function testDeleteWithoutAlias(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Doctrine\\ORM\\QueryBuilder::delete(): The alias for entity Doctrine\\Tests\\Models\\CMS\\CmsUser u must not be omitted.');\n        $qb->delete(CmsUser::class . ' u');\n    }\n\n    public function testCreateNamedParameter(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where(\n                $qb->expr()->eq('u.name', $qb->createNamedParameter('john doe', Types::STRING)),\n            )\n            ->orWhere(\n                $qb->expr()->eq('u.rank', $qb->createNamedParameter(100, Types::INTEGER)),\n            );\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = :dcValue1 OR u.rank = :dcValue2', $qb->getDQL());\n        self::assertEquals('john doe', $qb->getParameter('dcValue1')->getValue());\n        self::assertEquals(Types::STRING, $qb->getParameter('dcValue1')->getType());\n        self::assertEquals(100, $qb->getParameter('dcValue2')->getValue());\n        self::assertEquals(Types::INTEGER, $qb->getParameter('dcValue2')->getType());\n    }\n\n    public function testCreateNamedParameterCustomPlaceholder(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder();\n\n        $qb->select('u')\n            ->from(CmsUser::class, 'u')\n            ->where(\n                $qb->expr()->eq('u.name', $qb->createNamedParameter('john doe', Types::STRING, ':test')),\n            )\n            ->andWhere(\n                $qb->expr()->eq('u.rank', $qb->createNamedParameter(100, Types::INTEGER)),\n            );\n\n        self::assertEquals('SELECT u FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u WHERE u.name = :test AND u.rank = :dcValue1', $qb->getDQL());\n        self::assertEquals('john doe', $qb->getParameter('test')->getValue());\n        self::assertEquals(Types::STRING, $qb->getParameter('test')->getType());\n        self::assertEquals(100, $qb->getParameter('dcValue1')->getValue());\n        self::assertEquals(Types::INTEGER, $qb->getParameter('dcValue1')->getType());\n    }\n\n    public function testType(): void\n    {\n        $qb = new class ($this->entityManager) extends QueryBuilder {\n            public function test(): void\n            {\n                TestCase::assertSame(QueryType::Select, $this->getType());\n\n                $this->delete();\n                TestCase::assertSame(QueryType::Delete, $this->getType());\n\n                $this->update();\n                TestCase::assertSame(QueryType::Update, $this->getType());\n\n                $this->select();\n                TestCase::assertSame(QueryType::Select, $this->getType());\n            }\n        };\n\n        $qb->test();\n    }\n\n    #[DataProvider('provideHint')]\n    public function testSingleHint(mixed $expected): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username')\n            ->setHint('foo', $expected);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $query = $qb->getQuery();\n        self::assertTrue($query->hasHint('foo'));\n        self::assertEquals($expected, $query->getHint('foo'));\n    }\n\n    public static function provideHint(): array\n    {\n        return [\n            ['bar'],\n            [new CmsUser()],\n            [['a','b','c']],\n            [1],\n            [true],\n        ];\n    }\n\n    public function testMultipleHints(): void\n    {\n        $object = new CmsUser();\n        $qb     = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username')\n            ->setHint('string', 'bar')\n            ->setHint('object', $object)\n            ->setHint('array', ['a', 'b', 'c'])\n            ->setHint('int', 5)\n            ->setHint('bool', true);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        $query = $qb->getQuery();\n        self::assertTrue($query->hasHint('string'));\n        self::assertTrue($query->hasHint('object'));\n        self::assertTrue($query->hasHint('array'));\n        self::assertTrue($query->hasHint('int'));\n        self::assertTrue($query->hasHint('bool'));\n\n        self::assertEquals('bar', $query->getHint('string'));\n        self::assertInstanceOf(CmsUser::class, $query->getHint('object'));\n        self::assertEquals(['a', 'b', 'c'], $query->getHint('array'));\n        self::assertEquals(5, $query->getHint('int'));\n        self::assertTrue($query->getHint('bool'));\n    }\n\n    public function testHasHint(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username')\n            ->setHint('foo', 'bar');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        self::assertTrue($qb->hasHint('foo'));\n    }\n\n    public function testGetHint(): void\n    {\n        $qb = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username')\n            ->setHint('foo', 'bar');\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        self::assertEquals('bar', $qb->getHint('foo'));\n    }\n\n    public function testGetHints(): void\n    {\n        $object = new CmsUser();\n        $qb     = $this->entityManager->createQueryBuilder()\n            ->delete(CmsUser::class, 'u')\n            ->select('u.id', 'u.username')\n            ->setHint('string', 'bar')\n            ->setHint('object', $object)\n            ->setHint('array', ['a', 'b', 'c'])\n            ->setHint('int', 5)\n            ->setHint('bool', true);\n\n        $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u');\n\n        self::assertCount(5, $qb->getHints('foo'));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Repository;\n\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Repository\\DefaultRepositoryFactory;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753DefaultRepository;\nuse Doctrine\\Tests\\Models\\DDC753\\DDC753EntityWithDefaultCustomRepository;\nuse Doctrine\\Tests\\Models\\DDC869\\DDC869PaymentRepository;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Repository\\DefaultRepositoryFactory}\n */\n#[CoversClass(DefaultRepositoryFactory::class)]\nclass DefaultRepositoryFactoryTest extends TestCase\n{\n    private EntityManagerInterface&MockObject $entityManager;\n    private Configuration&MockObject $configuration;\n    private DefaultRepositoryFactory $repositoryFactory;\n\n    protected function setUp(): void\n    {\n        $this->configuration     = $this->createMock(Configuration::class);\n        $this->entityManager     = $this->createEntityManager();\n        $this->repositoryFactory = new DefaultRepositoryFactory();\n\n        $this->configuration\n            ->expects(self::any())\n            ->method('getDefaultRepositoryClassName')\n            ->willReturn(DDC869PaymentRepository::class);\n    }\n\n    public function testCreatesRepositoryFromDefaultRepositoryClass(): void\n    {\n        $this->entityManager\n            ->expects(self::any())\n            ->method('getClassMetadata')\n            ->willReturnCallback($this->buildClassMetadata(...));\n\n        self::assertInstanceOf(\n            DDC869PaymentRepository::class,\n            $this->repositoryFactory->getRepository($this->entityManager, self::class),\n        );\n    }\n\n    public function testCreatedRepositoriesAreCached(): void\n    {\n        $this->entityManager\n            ->expects(self::any())\n            ->method('getClassMetadata')\n            ->willReturnCallback($this->buildClassMetadata(...));\n\n        self::assertSame(\n            $this->repositoryFactory->getRepository($this->entityManager, self::class),\n            $this->repositoryFactory->getRepository($this->entityManager, self::class),\n        );\n    }\n\n    public function testCreatesRepositoryFromCustomClassMetadata(): void\n    {\n        $customMetadata                            = $this->buildClassMetadata(DDC753EntityWithDefaultCustomRepository::class);\n        $customMetadata->customRepositoryClassName = DDC753DefaultRepository::class;\n\n        $this->entityManager\n            ->expects(self::any())\n            ->method('getClassMetadata')\n            ->willReturn($customMetadata);\n\n        self::assertInstanceOf(\n            DDC753DefaultRepository::class,\n            $this->repositoryFactory->getRepository($this->entityManager, self::class),\n        );\n    }\n\n    public function testCachesDistinctRepositoriesPerDistinctEntityManager(): void\n    {\n        $em1 = $this->createEntityManager();\n        $em2 = $this->createEntityManager();\n\n        $em1->expects(self::any())\n            ->method('getClassMetadata')\n            ->willReturnCallback($this->buildClassMetadata(...));\n\n        $em2->expects(self::any())\n            ->method('getClassMetadata')\n            ->willReturnCallback($this->buildClassMetadata(...));\n\n        $repo1 = $this->repositoryFactory->getRepository($em1, self::class);\n        $repo2 = $this->repositoryFactory->getRepository($em2, self::class);\n\n        self::assertSame($repo1, $this->repositoryFactory->getRepository($em1, self::class));\n        self::assertSame($repo2, $this->repositoryFactory->getRepository($em2, self::class));\n\n        self::assertNotSame($repo1, $repo2);\n    }\n\n    /**\n     * @param class-string<TEntity> $className\n     *\n     * @return ClassMetadata<TEntity>&MockObject\n     *\n     * @template TEntity of object\n     */\n    private function buildClassMetadata(string $className): ClassMetadata&MockObject\n    {\n        $metadata = $this->createMock(ClassMetadata::class);\n        $metadata->method('getName')->willReturn($className);\n        $metadata->name = $className;\n\n        $metadata->customRepositoryClassName = null;\n\n        return $metadata;\n    }\n\n    private function createEntityManager(): EntityManagerInterface&MockObject\n    {\n        $entityManager = $this->createMock(EntityManagerInterface::class);\n        $entityManager->method('getConfiguration')->willReturn($this->configuration);\n\n        return $entityManager;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/AttachEntityListenersListenerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\EntityListeners;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Tools\\AttachEntityListenersListener;\nuse Doctrine\\Tests\\OrmTestCase;\n\nuse function func_get_args;\n\nclass AttachEntityListenersListenerTest extends OrmTestCase\n{\n    private EntityManagerInterface $em;\n\n    private AttachEntityListenersListener $listener;\n\n    private ClassMetadataFactory $factory;\n\n    protected function setUp(): void\n    {\n        $this->listener = new AttachEntityListenersListener();\n        $driver         = $this->createAttributeDriver();\n        $this->em       = $this->getTestEntityManager();\n        $evm            = $this->em->getEventManager();\n        $this->factory  = new ClassMetadataFactory();\n\n        $evm->addEventListener(Events::loadClassMetadata, $this->listener);\n        $this->em->getConfiguration()->setMetadataDriverImpl($driver);\n        $this->factory->setEntityManager($this->em);\n    }\n\n    public function testAttachEntityListeners(): void\n    {\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestFooEntity::class,\n            AttachEntityListenersListenerTestListener::class,\n            Events::postLoad,\n            'postLoadHandler',\n        );\n\n        $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class);\n\n        self::assertArrayHasKey('postLoad', $metadata->entityListeners);\n        self::assertCount(1, $metadata->entityListeners['postLoad']);\n        self::assertEquals('postLoadHandler', $metadata->entityListeners['postLoad'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['postLoad'][0]['class']);\n\n        // Can reattach entity listeners even class metadata factory recreated.\n        $factory2 = new ClassMetadataFactory();\n        $factory2->setEntityManager($this->em);\n\n        $metadata2 = $factory2->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class);\n\n        self::assertArrayHasKey('postLoad', $metadata2->entityListeners);\n        self::assertCount(1, $metadata2->entityListeners['postLoad']);\n        self::assertEquals('postLoadHandler', $metadata2->entityListeners['postLoad'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata2->entityListeners['postLoad'][0]['class']);\n    }\n\n    public function testAttachToExistingEntityListeners(): void\n    {\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestBarEntity::class,\n            AttachEntityListenersListenerTestListener2::class,\n            Events::prePersist,\n        );\n\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestBarEntity::class,\n            AttachEntityListenersListenerTestListener2::class,\n            Events::postPersist,\n            'postPersistHandler',\n        );\n\n        $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestBarEntity::class);\n\n        self::assertArrayHasKey('postPersist', $metadata->entityListeners);\n        self::assertArrayHasKey('prePersist', $metadata->entityListeners);\n\n        self::assertCount(2, $metadata->entityListeners['prePersist']);\n        self::assertCount(2, $metadata->entityListeners['postPersist']);\n\n        self::assertEquals('prePersist', $metadata->entityListeners['prePersist'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['prePersist'][0]['class']);\n\n        self::assertEquals('prePersist', $metadata->entityListeners['prePersist'][1]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener2::class, $metadata->entityListeners['prePersist'][1]['class']);\n\n        self::assertEquals('postPersist', $metadata->entityListeners['postPersist'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['postPersist'][0]['class']);\n\n        self::assertEquals('postPersistHandler', $metadata->entityListeners['postPersist'][1]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener2::class, $metadata->entityListeners['postPersist'][1]['class']);\n    }\n\n    public function testDuplicateEntityListenerException(): void\n    {\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage('Entity Listener \"Doctrine\\Tests\\ORM\\Tools\\AttachEntityListenersListenerTestListener#postPersist()\" in \"Doctrine\\Tests\\ORM\\Tools\\AttachEntityListenersListenerTestFooEntity\" was already declared, but it must be declared only once.');\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestFooEntity::class,\n            AttachEntityListenersListenerTestListener::class,\n            Events::postPersist,\n        );\n\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestFooEntity::class,\n            AttachEntityListenersListenerTestListener::class,\n            Events::postPersist,\n        );\n\n        $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class);\n    }\n\n    public function testAttachWithoutSpecifyingAnEventName(): void\n    {\n        $this->listener->addEntityListener(\n            AttachEntityListenersListenerTestFooEntity::class,\n            AttachEntityListenersListenerTestListener::class,\n        );\n\n        $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class);\n\n        self::assertCount(2, $metadata->entityListeners);\n\n        self::assertArrayHasKey('prePersist', $metadata->entityListeners);\n        self::assertArrayHasKey('postPersist', $metadata->entityListeners);\n\n        self::assertCount(1, $metadata->entityListeners['prePersist']);\n        self::assertCount(1, $metadata->entityListeners['postPersist']);\n\n        self::assertEquals('prePersist', $metadata->entityListeners['prePersist'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['prePersist'][0]['class']);\n\n        self::assertEquals('postPersist', $metadata->entityListeners['postPersist'][0]['method']);\n        self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['postPersist'][0]['class']);\n    }\n}\n\n#[Entity]\nclass AttachEntityListenersListenerTestFooEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\n#[Entity]\n#[EntityListeners(['AttachEntityListenersListenerTestListener'])]\nclass AttachEntityListenersListenerTestBarEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    public $id;\n}\n\nclass AttachEntityListenersListenerTestListener\n{\n    /** @phpstan-var array<string,list<list<mixed>>> */\n    public $calls;\n\n    public function prePersist(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postLoadHandler(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postPersist(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n}\n\nclass AttachEntityListenersListenerTestListener2\n{\n    /** @phpstan-var array<string,list<list<mixed>>> */\n    public $calls;\n\n    public function prePersist(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n\n    public function postPersistHandler(): void\n    {\n        $this->calls[__FUNCTION__][] = func_get_args();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/ClearCacheCollectionRegionCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache\\CollectionRegionCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\n#[Group('DDC-2183')]\nclass ClearCacheCollectionRegionCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n\n    private CollectionRegionCommand $command;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->command = new CollectionRegionCommand(new SingleManagerProvider($this->_em));\n\n        $this->application = new Application();\n        self::addCommandToApplication($this->application, $this->command);\n    }\n\n    public function testClearAllRegion(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:collection');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command' => $command->getName(),\n                '--all'   => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(' // Clearing all second-level cache collection regions', $tester->getDisplay());\n    }\n\n    public function testClearByOwnerEntityClassName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:collection');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'owner-class' => State::class,\n                'association' => 'cities',\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Clearing second-level cache for collection \"Doctrine\\Tests\\Models\\Cache\\State#cities\"',\n            $tester->getDisplay(),\n        );\n    }\n\n    public function testClearCacheEntryName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:collection');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'owner-class' => State::class,\n                'association' => 'cities',\n                'owner-id'    => 1,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Clearing second-level cache entry for collection \"Doctrine\\Tests\\Models\\Cache\\State#cities\" owner',\n            $tester->getDisplay(),\n        );\n\n        self::assertStringContainsString('identified by \"1\"', $tester->getDisplay());\n    }\n\n    public function testFlushRegionName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:collection');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'owner-class' => State::class,\n                'association' => 'cities',\n                '--flush'     => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Flushing cache provider configured for \"Doctrine\\Tests\\Models\\Cache\\State#cities\"',\n            $tester->getDisplay(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/ClearCacheEntityRegionCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache\\EntityRegionCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function preg_replace;\nuse function trim;\n\n#[Group('DDC-2183')]\nclass ClearCacheEntityRegionCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n\n    private EntityRegionCommand $command;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->command = new EntityRegionCommand(new SingleManagerProvider($this->_em));\n\n        $this->application = new Application();\n        self::addCommandToApplication($this->application, $this->command);\n    }\n\n    public function testClearAllRegion(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:entity');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command' => $command->getName(),\n                '--all'   => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(' // Clearing all second-level cache entity regions', $tester->getDisplay());\n    }\n\n    public function testClearByEntityClassName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:entity');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'      => $command->getName(),\n                'entity-class' => Country::class,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Clearing second-level cache for entity \"Doctrine\\Tests\\Models\\Cache\\Country\"',\n            $tester->getDisplay(),\n        );\n    }\n\n    public function testClearCacheEntryName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:entity');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'      => $command->getName(),\n                'entity-class' => Country::class,\n                'entity-id'    => 1,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertSame(\n            'Clearing second-level cache entry for entity \"Doctrine\\Tests\\Models\\Cache\\Country\" identified by \"1\"',\n            trim(preg_replace('#\\s+//\\s#', ' ', $tester->getDisplay())),\n        );\n    }\n\n    public function testFlushRegionName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:entity');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'      => $command->getName(),\n                'entity-class' => Country::class,\n                '--flush'      => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Flushing cache provider configured for entity named \"Doctrine\\Tests\\Models\\Cache\\Country\"',\n            $tester->getDisplay(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/ClearCacheQueryRegionCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\ClearCache\\QueryRegionCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\n#[Group('DDC-2183')]\nclass ClearCacheQueryRegionCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n\n    private QueryRegionCommand $command;\n\n    protected function setUp(): void\n    {\n        $this->enableSecondLevelCache();\n\n        parent::setUp();\n\n        $this->command = new QueryRegionCommand(new SingleManagerProvider($this->_em));\n\n        $this->application = new Application();\n        self::addCommandToApplication($this->application, $this->command);\n    }\n\n    public function testClearAllRegion(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:query');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command' => $command->getName(),\n                '--all'   => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(' // Clearing all second-level cache query regions', $tester->getDisplay());\n    }\n\n    public function testClearDefaultRegionName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:query');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'region-name' => null,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Clearing second-level cache query region named \"query_cache_region\"',\n            $tester->getDisplay(),\n        );\n    }\n\n    public function testClearByRegionName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:query');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'region-name' => 'my_region',\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Clearing second-level cache query region named \"my_region\"',\n            $tester->getDisplay(),\n        );\n    }\n\n    public function testFlushRegionName(): void\n    {\n        $command = $this->application->find('orm:clear-cache:region:query');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(\n            [\n                'command'     => $command->getName(),\n                'region-name' => 'my_region',\n                '--flush'     => true,\n            ],\n            ['decorated' => false],\n        );\n\n        self::assertStringContainsString(\n            ' // Flushing cache provider configured for second-level cache query region named \"my_region\"',\n            $tester->getDisplay(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/Debug/DebugEntityListenersDoctrineCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug;\n\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\Debug\\DebugEntityListenersDoctrineCommand;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BarListener;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\FooListener;\nuse PHPUnit\\Framework\\Attributes\\TestWith;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Completion\\CompletionInput;\nuse Symfony\\Component\\Console\\Completion\\CompletionSuggestions;\nuse Symfony\\Component\\Console\\Completion\\Suggestion;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function array_map;\n\nclass DebugEntityListenersDoctrineCommandTest extends TestCase\n{\n    use ApplicationCompatibility;\n\n    private DebugEntityListenersDoctrineCommand $command;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $application   = new Application();\n        $this->command = new DebugEntityListenersDoctrineCommand($this->getMockManagerRegistry());\n\n        self::addCommandToApplication($application, $this->command);\n    }\n\n    public function testExecute(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName(), 'entity' => self::class],\n        );\n\n        self::assertSame(<<<'TXT'\n\nEntity listeners for Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\DebugEntityListenersDoctrineCommandTest\n===========================================================================================================\n\n ------------- ------- ------------------------------------------------------------------------------------ \n  Event         Order   Listener                                                                            \n ------------- ------- ------------------------------------------------------------------------------------ \n  postPersist   #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener::postPersist()  \n ------------- ------- ------------------------------------------------------------------------------------ \n  preUpdate     #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\FooListener::preUpdate()    \n                #2      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BarListener::__invoke()     \n ------------- ------- ------------------------------------------------------------------------------------ \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    public function testExecuteWithEvent(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName(), 'entity' => self::class, 'event' => 'postPersist'],\n        );\n\n        self::assertSame(<<<'TXT'\n\nEntity listeners for Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\DebugEntityListenersDoctrineCommandTest\n===========================================================================================================\n\n ------------- ------- ------------------------------------------------------------------------------------ \n  Event         Order   Listener                                                                            \n ------------- ------- ------------------------------------------------------------------------------------ \n  postPersist   #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener::postPersist()  \n ------------- ------- ------------------------------------------------------------------------------------ \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    public function testExecuteWithMissingEvent(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName(), 'entity' => self::class, 'event' => 'preRemove'],\n        );\n\n        self::assertSame(<<<'TXT'\n\n [INFO] No listeners are configured for the \"preRemove\" event.                                                          \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    /**\n     * @param list<string> $args\n     * @param list<string> $expectedSuggestions\n     */\n    #[TestWith([['console'], 1, [self::class]])]\n    #[TestWith([['console', self::class], 2, ['preUpdate', 'postPersist']])]\n    #[TestWith([['console', 'NonExistentEntity'], 2, []])]\n    public function testComplete(array $args, int $currentIndex, array $expectedSuggestions): void\n    {\n        $input = CompletionInput::fromTokens($args, $currentIndex);\n        $input->bind($this->command->getDefinition());\n        $suggestions = new CompletionSuggestions();\n\n        $this->command->complete($input, $suggestions);\n\n        self::assertSame($expectedSuggestions, array_map(static fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));\n    }\n\n    /** @return MockObject&ManagerRegistry */\n    private function getMockManagerRegistry(): ManagerRegistry\n    {\n        $mappingDriverMock = $this->createMock(MappingDriver::class);\n        $mappingDriverMock->method('getAllClassNames')->willReturn([self::class]);\n\n        $config = new Configuration();\n        $config->setMetadataDriverImpl($mappingDriverMock);\n\n        $classMetadata = new ClassMetadata(self::class);\n        $classMetadata->addEntityListener('preUpdate', FooListener::class, 'preUpdate');\n        $classMetadata->addEntityListener('preUpdate', BarListener::class, '__invoke');\n        $classMetadata->addEntityListener('postPersist', BazListener::class, 'postPersist');\n\n        $emMock = $this->createMock(EntityManagerInterface::class);\n        $emMock->method('getConfiguration')->willReturn($config);\n        $emMock->method('getClassMetadata')->willReturn($classMetadata);\n\n        $doctrineMock = $this->createMock(ManagerRegistry::class);\n        $doctrineMock->method('getManagerNames')->willReturn(['default' => 'entity_manager.default']);\n        $doctrineMock->method('getManager')->willReturn($emMock);\n        $doctrineMock->method('getManagerForClass')->willReturn($emMock);\n\n        return $doctrineMock;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/Debug/DebugEventManagerDoctrineCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\Debug\\DebugEventManagerDoctrineCommand;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BarListener;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\FooListener;\nuse PHPUnit\\Framework\\Attributes\\TestWith;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Completion\\CompletionInput;\nuse Symfony\\Component\\Console\\Completion\\CompletionSuggestions;\nuse Symfony\\Component\\Console\\Completion\\Suggestion;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function array_map;\n\nclass DebugEventManagerDoctrineCommandTest extends TestCase\n{\n    use ApplicationCompatibility;\n\n    private DebugEventManagerDoctrineCommand $command;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $application   = new Application();\n        $this->command = new DebugEventManagerDoctrineCommand($this->getMockManagerRegistry());\n\n        self::addCommandToApplication($application, $this->command);\n    }\n\n    public function testExecute(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName()],\n        );\n\n        self::assertSame(<<<'TXT'\n\nEvent listeners for default entity manager\n==========================================\n\n ------------- ------- ------------------------------------------------------------------------------------ \n  Event         Order   Listener                                                                            \n ------------- ------- ------------------------------------------------------------------------------------ \n  postPersist   #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener::postPersist()  \n ------------- ------- ------------------------------------------------------------------------------------ \n  preUpdate     #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\FooListener::preUpdate()    \n                #2      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BarListener::__invoke()     \n ------------- ------- ------------------------------------------------------------------------------------ \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    public function testExecuteWithEvent(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName(), 'event' => 'postPersist'],\n        );\n\n        self::assertSame(<<<'TXT'\n\nEvent listeners for default entity manager\n==========================================\n\n ------------- ------- ------------------------------------------------------------------------------------ \n  Event         Order   Listener                                                                            \n ------------- ------- ------------------------------------------------------------------------------------ \n  postPersist   #1      Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures\\BazListener::postPersist()  \n ------------- ------- ------------------------------------------------------------------------------------ \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    public function testExecuteWithMissingEvent(): void\n    {\n        $commandTester = new CommandTester($this->command);\n        $commandTester->execute(\n            ['command' => $this->command->getName(), 'event' => 'preRemove'],\n        );\n\n        self::assertSame(<<<'TXT'\n\n [INFO] No listeners are configured for the \"preRemove\" event.                                                          \n\n\nTXT\n            , $commandTester->getDisplay(true));\n    }\n\n    /**\n     * @param list<string> $args\n     * @param list<string> $expectedSuggestions\n     */\n    #[TestWith([['console'], 1, ['preUpdate', 'postPersist']])]\n    #[TestWith([['console', '--em'], 1, ['default']])]\n    public function testComplete(array $args, int $currentIndex, array $expectedSuggestions): void\n    {\n        $input = CompletionInput::fromTokens($args, $currentIndex);\n        $input->bind($this->command->getDefinition());\n        $suggestions = new CompletionSuggestions();\n\n        $this->command->complete($input, $suggestions);\n\n        self::assertSame($expectedSuggestions, array_map(static fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));\n    }\n\n    /** @return MockObject&ManagerRegistry */\n    private function getMockManagerRegistry(): ManagerRegistry\n    {\n        $eventManager = new EventManager();\n        $eventManager->addEventListener('preUpdate', new FooListener());\n        $eventManager->addEventListener('preUpdate', new BarListener());\n        $eventManager->addEventListener('postPersist', new BazListener());\n\n        $emMock = $this->createMock(EntityManagerInterface::class);\n        $emMock->method('getEventManager')->willReturn($eventManager);\n\n        $doctrineMock = $this->createMock(ManagerRegistry::class);\n        $doctrineMock->method('getDefaultManagerName')->willReturn('default');\n        $doctrineMock->method('getManager')->willReturn($emMock);\n        $doctrineMock->method('getManagerNames')->willReturn(['default' => 'entity_manager.default']);\n\n        return $doctrineMock;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/Debug/Fixtures/BarListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures;\n\nclass BarListener\n{\n    public function __invoke(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/Debug/Fixtures/BazListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures;\n\nclass BazListener\n{\n    public function postPersist(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/Debug/Fixtures/FooListener.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\Debug\\Fixtures;\n\nclass FooListener\n{\n    public function preUpdate(): void\n    {\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/InfoCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\InfoCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionInfo;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nclass InfoCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n    private InfoCommand $command;\n    private CommandTester $tester;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->application = new Application();\n\n        self::addCommandToApplication($this->application, new InfoCommand(new SingleManagerProvider($this->_em)));\n\n        $this->command = $this->application->find('orm:info');\n        $this->tester  = new CommandTester($this->command);\n    }\n\n    public function testListAllClasses(): void\n    {\n        $this->tester->execute(['command' => $this->command->getName()]);\n\n        self::assertStringContainsString(AttractionInfo::class, $this->tester->getDisplay());\n        self::assertStringContainsString(City::class, $this->tester->getDisplay());\n    }\n\n    public function testEmptyEntityClassNames(): void\n    {\n        $mappingDriver = $this->createMock(MappingDriver::class);\n        $configuration = $this->createMock(Configuration::class);\n        $em            = $this->createMock(EntityManagerInterface::class);\n\n        $mappingDriver->method('getAllClassNames')\n                      ->willReturn([]);\n\n        $configuration->method('getMetadataDriverImpl')\n                      ->willReturn($mappingDriver);\n\n        $em->method('getConfiguration')\n           ->willReturn($configuration);\n\n        $application = new Application();\n        self::addCommandToApplication($application, new InfoCommand(new SingleManagerProvider($em)));\n\n        $command = $application->find('orm:info');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(['command' => $command->getName()]);\n\n        self::assertStringContainsString(\n            ' ! [CAUTION] You do not have any mapped Doctrine ORM entities according to the current configuration',\n            $tester->getDisplay(),\n        );\n\n        self::assertStringContainsString(\n            ' !           If you have entities or mapping files you should check your mapping configuration for errors.',\n            $tester->getDisplay(),\n        );\n    }\n\n    public function testInvalidEntityClassMetadata(): void\n    {\n        $mappingDriver = $this->createMock(MappingDriver::class);\n        $configuration = $this->createMock(Configuration::class);\n        $em            = $this->createMock(EntityManagerInterface::class);\n\n        $mappingDriver->method('getAllClassNames')\n                      ->willReturn(['InvalidEntity']);\n\n        $configuration->method('getMetadataDriverImpl')\n                      ->willReturn($mappingDriver);\n\n        $em->method('getConfiguration')\n           ->willReturn($configuration);\n\n        $em->method('getClassMetadata')\n           ->with('InvalidEntity')\n           ->willThrowException(new MappingException('exception message'));\n\n        $application = new Application();\n        self::addCommandToApplication($application, new InfoCommand(new SingleManagerProvider($em)));\n\n        $command = $application->find('orm:info');\n        $tester  = new CommandTester($command);\n\n        $tester->execute(['command' => $command->getName()]);\n\n        self::assertStringContainsString('[FAIL] InvalidEntity', $tester->getDisplay());\n        self::assertStringContainsString('exception message', $tester->getDisplay());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/MappingDescribeCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionInfo;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandCompletionTester;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function json_decode;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand}\n */\n#[CoversClass(MappingDescribeCommand::class)]\nclass MappingDescribeCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n\n    private MappingDescribeCommand $command;\n\n    private CommandTester $tester;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->application = new Application();\n        self::addCommandToApplication($this->application, new MappingDescribeCommand(new SingleManagerProvider($this->_em)));\n\n        $this->command = $this->application->find('orm:mapping:describe');\n        $this->tester  = new CommandTester($this->command);\n    }\n\n    public function testShowSpecificFuzzySingle(): void\n    {\n        $this->tester->execute(\n            [\n                'command'    => $this->command->getName(),\n                'entityName' => 'AttractionInfo',\n            ],\n        );\n\n        $display = $this->tester->getDisplay();\n\n        self::assertStringContainsString(AttractionInfo::class, $display);\n        self::assertStringContainsString('Root entity name', $display);\n    }\n\n    public function testShowSpecificFuzzySingleJson(): void\n    {\n        $this->tester->execute([\n            'command' => $this->command->getName(),\n            'entityName' => 'AttractionInfo',\n            '--format' => 'json',\n        ]);\n\n        $display     = $this->tester->getDisplay();\n        $decodedJson = json_decode($display, true);\n\n        self::assertJson($display);\n        self::assertSame(AttractionInfo::class, $decodedJson['name']);\n        self::assertArrayHasKey('rootEntityName', $decodedJson);\n        self::assertArrayHasKey('fieldMappings', $decodedJson);\n        self::assertArrayHasKey('associationMappings', $decodedJson);\n        self::assertArrayHasKey('id', $decodedJson['fieldMappings']);\n    }\n\n    public function testShowSpecificFuzzyAmbiguous(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('possible matches');\n        $this->tester->execute(\n            [\n                'command'    => $this->command->getName(),\n                'entityName' => 'Attraction',\n            ],\n        );\n    }\n\n    public function testShowSpecificNotFound(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Could not find any mapped Entity classes matching \"AttractionFooBar\"');\n        $this->tester->execute(\n            [\n                'command'    => $this->command->getName(),\n                'entityName' => 'AttractionFooBar',\n            ],\n        );\n    }\n\n    /**\n     * @param string[] $input\n     * @param string[] $expectedSuggestions\n     */\n    #[DataProvider('provideCompletionSuggestions')]\n    public function testComplete(array $input, array $expectedSuggestions): void\n    {\n        $this->useModelSet('cache');\n\n        parent::setUp();\n\n        $completionTester = new CommandCompletionTester(new MappingDescribeCommand(new SingleManagerProvider($this->_em)));\n\n        $suggestions = $completionTester->complete($input);\n\n        foreach ($expectedSuggestions as $expected) {\n            self::assertContains($expected, $suggestions);\n        }\n    }\n\n    /** @return iterable<string, array{string[], string[]}> */\n    public static function provideCompletionSuggestions(): iterable\n    {\n        yield 'entityName' => [\n            [''],\n            [\n                'Doctrine\\\\\\\\Tests\\\\\\\\Models\\\\\\\\Cache\\\\\\\\Restaurant',\n                'Doctrine\\\\\\\\Tests\\\\\\\\Models\\\\\\\\Cache\\\\\\\\Beach',\n                'Doctrine\\\\\\\\Tests\\\\\\\\Models\\\\\\\\Cache\\\\\\\\Bar',\n            ],\n        ];\n\n        yield 'format option value' => [\n            ['--format='],\n            ['text', 'json'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/RunDqlCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\RunDqlCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function trim;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Tools\\Console\\Command\\RunDqlCommand}\n */\n#[CoversClass(RunDqlCommand::class)]\nclass RunDqlCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private Application $application;\n\n    private RunDqlCommand $command;\n\n    private CommandTester $tester;\n\n    protected function setUp(): void\n    {\n        $this->useModelSet('generic');\n\n        parent::setUp();\n\n        $this->command = new RunDqlCommand(new SingleManagerProvider($this->_em));\n\n        $this->application = new Application();\n        self::addCommandToApplication($this->application, $this->command);\n\n        $this->tester = new CommandTester($this->command);\n    }\n\n    public function testCommandName(): void\n    {\n        self::assertSame($this->command, $this->application->get('orm:run-dql'));\n    }\n\n    public function testWillRunQuery(): void\n    {\n        $this->_em->persist(new DateTimeModel());\n        $this->_em->flush();\n\n        self::assertSame(\n            0,\n            $this->tester->execute(\n                [\n                    'command' => $this->command->getName(),\n                    'dql'     => 'SELECT e FROM ' . DateTimeModel::class . ' e',\n                ],\n            ),\n        );\n\n        self::assertStringContainsString(DateTimeModel::class, $this->tester->getDisplay());\n    }\n\n    public function testWillShowQuery(): void\n    {\n        $this->_em->persist(new DateTimeModel());\n        $this->_em->flush();\n\n        self::assertSame(\n            0,\n            $this->tester->execute(\n                [\n                    'command'    => $this->command->getName(),\n                    'dql'        => 'SELECT e FROM ' . DateTimeModel::class . ' e',\n                    '--show-sql' => 'true',\n                ],\n            ),\n        );\n\n        self::assertStringMatchesFormat('SELECT %a', trim($this->tester->getDisplay()));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/SchemaTool/CommandTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool\\AbstractCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nabstract class CommandTestCase extends OrmFunctionalTestCase\n{\n    /** @param class-string<AbstractCommand> $commandClass */\n    protected function getCommandTester(string $commandClass, string|null $commandName = null): CommandTester\n    {\n        $attributeDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/Models']);\n        $entityManager   = $this->getEntityManager(null, $attributeDriver);\n\n        if (! $entityManager->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('We are testing the symfony/console integration');\n        }\n\n        $command = new $commandClass(\n            new SingleManagerProvider($entityManager),\n        );\n\n        if ($commandName !== null) {\n            $command->setName($commandName);\n        }\n\n        return new CommandTester($command);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/SchemaTool/CreateCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool\\CreateCommand;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\n\nclass CreateCommandTest extends CommandTestCase\n{\n    #[DoesNotPerformAssertions]\n    public function testItPrintsTheSql(): void\n    {\n        $tester = $this->getCommandTester(CreateCommand::class);\n        $tester->execute(['--dump-sql' => true]);\n\n        self::$sharedConn->executeStatement($tester->getDisplay());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/SchemaTool/DropCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\SchemaTool;\n\nuse Doctrine\\ORM\\Tools\\Console\\Command\\SchemaTool\\DropCommand;\nuse Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\SchemaTool\\Models\\Keyboard;\nuse PHPUnit\\Framework\\Attributes\\DoesNotPerformAssertions;\n\nfinal class DropCommandTest extends CommandTestCase\n{\n    #[DoesNotPerformAssertions]\n    public function testItPrintsTheSql(): void\n    {\n        $this->createSchemaForModels(Keyboard::class);\n        $tester = $this->getCommandTester(DropCommand::class);\n        $tester->execute(['--dump-sql' => true]);\n\n        self::$sharedConn->executeStatement($tester->getDisplay());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/SchemaTool/Models/Keyboard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command\\SchemaTool\\Models;\n\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Table;\n\n#[Table(name: 'keyboard')]\n#[Entity]\nfinal class Keyboard\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'NONE')]\n    private $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    private $name;\n\n    public function __construct(int $id, string $name)\n    {\n        $this->id   = $id;\n        $this->name = $name;\n    }\n\n    public function id(): int\n    {\n        return $this->id;\n    }\n\n    public function name(): string\n    {\n        return $this->name;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/Command/ValidateSchemaCommandTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console\\Command;\n\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\DBAL\\Schema\\SchemaDiff;\nuse Doctrine\\ORM\\Tools\\Console\\ApplicationCompatibility;\nuse Doctrine\\ORM\\Tools\\Console\\Command\\ValidateSchemaCommand;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider\\SingleManagerProvider;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Tester\\CommandTester;\n\nuse function method_exists;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Tools\\Console\\Command\\ValidateSchemaCommand}\n */\n#[CoversClass(ValidateSchemaCommand::class)]\nclass ValidateSchemaCommandTest extends OrmFunctionalTestCase\n{\n    use ApplicationCompatibility;\n\n    private ValidateSchemaCommand $command;\n\n    private CommandTester $tester;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {\n            self::markTestSkipped('Only with sqlite');\n        }\n\n        if (! method_exists(SchemaDiff::class, 'toSaveSql')) {\n            self::markTestSkipped('FIXME for DBAL 4.');\n        }\n\n        $application = new Application();\n        self::addCommandToApplication($application, new ValidateSchemaCommand(new SingleManagerProvider($this->_em)));\n\n        $this->command = $application->find('orm:validate-schema');\n        $this->tester  = new CommandTester($this->command);\n    }\n\n    public function testNotInSync(): void\n    {\n        $this->tester->execute(\n            [\n                'command' => $this->command->getName(),\n            ],\n        );\n\n        $display = $this->tester->getDisplay();\n\n        self::assertStringContainsString('The database schema is not in sync with the current mapping file', $display);\n        self::assertStringNotContainsString('cache_login', $display);\n    }\n\n    public function testNotInSyncVerbose(): void\n    {\n        $schemaManager = $this->createSchemaManager();\n        if ($schemaManager->tablesExist(['cache_login'])) {\n            $schemaManager->dropTable('cache_login');\n        }\n\n        $this->tester->execute(\n            [\n                'command' => $this->command->getName(),\n            ],\n            [\n                'verbosity' => OutputInterface::VERBOSITY_VERBOSE,\n            ],\n        );\n\n        $display = $this->tester->getDisplay();\n        self::assertStringContainsString('The database schema is not in sync with the current mapping file', $display);\n        self::assertStringContainsString('cache_login', $display);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/ConsoleRunnerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console;\n\nuse Composer\\InstalledVersions;\nuse DBALConsole\\Command\\ReservedWordsCommand;\nuse Doctrine\\ORM\\Tools\\Console\\ConsoleRunner;\nuse Doctrine\\ORM\\Tools\\Console\\EntityManagerProvider;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Console\\Command\\Command;\n\nuse function class_exists;\n\n#[CoversClass(ConsoleRunner::class)]\n#[Group('DDC-3186')]\nfinal class ConsoleRunnerTest extends TestCase\n{\n    public function testCreateApplicationShouldReturnAnApplicationWithTheCorrectCommands(): void\n    {\n        $app = ConsoleRunner::createApplication($this->createStub(EntityManagerProvider::class));\n\n        self::assertSame(InstalledVersions::getVersion('doctrine/orm'), $app->getVersion());\n        if (class_exists(ReservedWordsCommand::class)) {\n            self::assertTrue($app->has('dbal:reserved-words'));\n        }\n\n        self::assertTrue($app->has('dbal:run-sql'));\n        self::assertTrue($app->has('orm:clear-cache:region:collection'));\n        self::assertTrue($app->has('orm:clear-cache:region:entity'));\n        self::assertTrue($app->has('orm:clear-cache:region:query'));\n        self::assertTrue($app->has('orm:clear-cache:metadata'));\n        self::assertTrue($app->has('orm:clear-cache:query'));\n        self::assertTrue($app->has('orm:clear-cache:result'));\n        self::assertTrue($app->has('orm:generate-proxies'));\n        self::assertTrue($app->has('orm:generate:proxies'));\n        self::assertTrue($app->has('orm:info'));\n        self::assertTrue($app->has('orm:mapping:describe'));\n        self::assertTrue($app->has('orm:run-dql'));\n        self::assertTrue($app->has('orm:schema-tool:create'));\n        self::assertTrue($app->has('orm:schema-tool:drop'));\n        self::assertTrue($app->has('orm:schema-tool:update'));\n        self::assertTrue($app->has('orm:validate-schema'));\n    }\n\n    public function testCreateApplicationShouldAppendGivenCommands(): void\n    {\n        $command = 'my:lovely-command';\n        $app     = ConsoleRunner::createApplication($this->createStub(EntityManagerProvider::class), [new Command($command)]);\n\n        self::assertTrue($app->has($command));\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Console/MetadataFilterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Console;\n\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Tools\\Console\\MetadataFilter;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\n\nuse function count;\n\n/**\n * Tests for {@see \\Doctrine\\ORM\\Tools\\Console\\MetadataFilter}\n */\n#[CoversClass(MetadataFilter::class)]\nclass MetadataFilterTest extends OrmTestCase\n{\n    private ClassMetadataFactory $cmf;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $driver = $this->createAttributeDriver();\n        $em     = $this->getTestEntityManager();\n\n        $em->getConfiguration()->setMetadataDriverImpl($driver);\n\n        $this->cmf = new ClassMetadataFactory();\n        $this->cmf->setEntityManager($em);\n    }\n\n    public function testFilterWithEmptyArray(): void\n    {\n        $originalMetadatas = [\n            $metadataAaa = $this->cmf->getMetadataFor(MetadataFilterTestEntityAaa::class),\n            $metadataBbb = $this->cmf->getMetadataFor(MetadataFilterTestEntityBbb::class),\n        ];\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, []);\n\n        self::assertContains($metadataAaa, $metadatas);\n        self::assertContains($metadataBbb, $metadatas);\n        self::assertCount(count($originalMetadatas), $metadatas);\n    }\n\n    public function testFilterWithString(): void\n    {\n        $originalMetadatas = [\n            $metadataAaa = $this->cmf->getMetadataFor(MetadataFilterTestEntityAaa::class),\n            $metadataBbb = $this->cmf->getMetadataFor(MetadataFilterTestEntityBbb::class),\n            $metadataCcc = $this->cmf->getMetadataFor(MetadataFilterTestEntityCcc::class),\n        ];\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'MetadataFilterTestEntityAaa');\n\n        self::assertContains($metadataAaa, $metadatas);\n        self::assertNotContains($metadataBbb, $metadatas);\n        self::assertNotContains($metadataCcc, $metadatas);\n        self::assertCount(1, $metadatas);\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'MetadataFilterTestEntityBbb');\n\n        self::assertNotContains($metadataAaa, $metadatas);\n        self::assertContains($metadataBbb, $metadatas);\n        self::assertNotContains($metadataCcc, $metadatas);\n        self::assertCount(1, $metadatas);\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'MetadataFilterTestEntityCcc');\n\n        self::assertNotContains($metadataAaa, $metadatas);\n        self::assertNotContains($metadataBbb, $metadatas);\n        self::assertContains($metadataCcc, $metadatas);\n        self::assertCount(1, $metadatas);\n    }\n\n    public function testFilterWithString2(): void\n    {\n        $originalMetadatas = [\n            $metadataFoo    = $this->cmf->getMetadataFor(MetadataFilterTestEntityFoo::class),\n            $metadataFooBar = $this->cmf->getMetadataFor(MetadataFilterTestEntityFooBar::class),\n            $metadataBar    = $this->cmf->getMetadataFor(MetadataFilterTestEntityBar::class),\n        ];\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'MetadataFilterTestEntityFoo');\n\n        self::assertContains($metadataFoo, $metadatas);\n        self::assertContains($metadataFooBar, $metadatas);\n        self::assertNotContains($metadataBar, $metadatas);\n        self::assertCount(2, $metadatas);\n    }\n\n    public function testFilterWithArray(): void\n    {\n        $originalMetadatas = [\n            $metadataAaa = $this->cmf->getMetadataFor(MetadataFilterTestEntityAaa::class),\n            $metadataBbb = $this->cmf->getMetadataFor(MetadataFilterTestEntityBbb::class),\n            $metadataCcc = $this->cmf->getMetadataFor(MetadataFilterTestEntityCcc::class),\n        ];\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, [\n            'MetadataFilterTestEntityAaa',\n            'MetadataFilterTestEntityCcc',\n        ]);\n\n        self::assertContains($metadataAaa, $metadatas);\n        self::assertNotContains($metadataBbb, $metadatas);\n        self::assertContains($metadataCcc, $metadatas);\n        self::assertCount(2, $metadatas);\n    }\n\n    public function testFilterWithRegex(): void\n    {\n        $originalMetadatas = [\n            $metadataFoo    = $this->cmf->getMetadataFor(MetadataFilterTestEntityFoo::class),\n            $metadataFooBar = $this->cmf->getMetadataFor(MetadataFilterTestEntityFooBar::class),\n            $metadataBar    = $this->cmf->getMetadataFor(MetadataFilterTestEntityBar::class),\n        ];\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'Foo$');\n\n        self::assertContains($metadataFoo, $metadatas);\n        self::assertNotContains($metadataFooBar, $metadatas);\n        self::assertNotContains($metadataBar, $metadatas);\n        self::assertCount(1, $metadatas);\n\n        $metadatas = $originalMetadatas;\n        $metadatas = MetadataFilter::filter($metadatas, 'Bar$');\n\n        self::assertNotContains($metadataFoo, $metadatas);\n        self::assertContains($metadataFooBar, $metadatas);\n        self::assertContains($metadataBar, $metadatas);\n        self::assertCount(2, $metadatas);\n    }\n}\n\n#[Entity]\nclass MetadataFilterTestEntityAaa\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n\n#[Entity]\nclass MetadataFilterTestEntityBbb\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n\n#[Entity]\nclass MetadataFilterTestEntityCcc\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n\n#[Entity]\nclass MetadataFilterTestEntityFoo\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n\n#[Entity]\nclass MetadataFilterTestEntityBar\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n\n#[Entity]\nclass MetadataFilterTestEntityFooBar\n{\n    /** @var int */\n    #[Id]\n    #[Column]\n    protected $id;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/DebugTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools;\n\nuse ArrayIterator;\nuse ArrayObject;\nuse DateTime;\nuse DateTimeImmutable;\nuse DateTimeZone;\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\ORM\\Tools\\Debug;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse stdClass;\n\nuse function print_r;\nuse function strpos;\nuse function substr;\n\nclass DebugTest extends OrmTestCase\n{\n    public function testExportObject(): void\n    {\n        $obj      = new stdClass();\n        $obj->foo = 'bar';\n        $obj->bar = 1234;\n\n        $var = Debug::export($obj, 2);\n        self::assertEquals('stdClass', $var->__CLASS__);\n    }\n\n    public function testExportObjectWithReference(): void\n    {\n        $foo = 'bar';\n        $bar = ['foo' => & $foo];\n        $baz = (object) $bar;\n\n        $var      = Debug::export($baz, 2);\n        $baz->foo = 'tab';\n\n        self::assertEquals('bar', $var->foo);\n        self::assertEquals('tab', $bar['foo']);\n    }\n\n    public function testExportArray(): void\n    {\n        $array              = ['a' => 'b', 'b' => ['c', 'd' => ['e', 'f']]];\n        $var                = Debug::export($array, 2);\n        $expected           = $array;\n        $expected['b']['d'] = 'Array(2)';\n        self::assertEquals($expected, $var);\n    }\n\n    public function testExportDateTime(): void\n    {\n        $obj = new DateTime('2010-10-10 10:10:10', new DateTimeZone('UTC'));\n\n        $var = Debug::export($obj, 2);\n        self::assertEquals('DateTime', $var->__CLASS__);\n        self::assertEquals('2010-10-10T10:10:10+00:00', $var->date);\n    }\n\n    public function testExportDateTimeImmutable(): void\n    {\n        $obj = new DateTimeImmutable('2010-10-10 10:10:10', new DateTimeZone('UTC'));\n\n        $var = Debug::export($obj, 2);\n        self::assertEquals('DateTimeImmutable', $var->__CLASS__);\n        self::assertEquals('2010-10-10T10:10:10+00:00', $var->date);\n    }\n\n    public function testExportDateTimeZone(): void\n    {\n        $obj = new DateTimeImmutable('2010-10-10 12:34:56', new DateTimeZone('Europe/Rome'));\n\n        $var = Debug::export($obj, 2);\n        self::assertEquals('DateTimeImmutable', $var->__CLASS__);\n        self::assertEquals('2010-10-10T12:34:56+02:00', $var->date);\n    }\n\n    public function testExportArrayTraversable(): void\n    {\n        $obj = new ArrayObject(['foobar']);\n\n        $var = Debug::export($obj, 2);\n        self::assertContains('foobar', $var->__STORAGE__);\n\n        $it = new ArrayIterator(['foobar']);\n\n        $var = Debug::export($it, 5);\n        self::assertContains('foobar', $var->__STORAGE__);\n    }\n\n    /** @param array<string, int> $expected */\n    #[DataProvider('provideAttributesCases')]\n    public function testExportParentAttributes(TestAsset\\ParentClass $class, array $expected): void\n    {\n        $actualRepresentation   = print_r($class, true);\n        $expectedRepresentation = print_r($expected, true);\n\n        $actualRepresentation   = substr($actualRepresentation, strpos($actualRepresentation, '('));\n        $expectedRepresentation = substr($expectedRepresentation, strpos($expectedRepresentation, '('));\n\n        self::assertSame($expectedRepresentation, $actualRepresentation);\n\n        $var = Debug::export($class, 3);\n        $var = (array) $var;\n        unset($var['__CLASS__']);\n\n        self::assertSame($expected, $var);\n    }\n\n    public function testCollectionsAreCastIntoArrays(): void\n    {\n        $collection = new ArrayCollection();\n        $collection->add('foo');\n        $collection->add('bar');\n\n        $var = Debug::export($collection, 2);\n        self::assertEquals(['foo', 'bar'], $var);\n    }\n\n    /** @phpstan-return array<string, array{TestAsset\\ParentClass, mixed[]}> */\n    public static function provideAttributesCases(): iterable\n    {\n        return [\n            'different-attributes' => [\n                new TestAsset\\ChildClass(),\n                [\n                    'parentPublicAttribute' => 1,\n                    'parentProtectedAttribute:protected' => 2,\n                    'parentPrivateAttribute:Doctrine\\Tests\\ORM\\Tools\\TestAsset\\ParentClass:private' => 3,\n                    'childPublicAttribute' => 4,\n                    'childProtectedAttribute:protected' => 5,\n                    'childPrivateAttribute:Doctrine\\Tests\\ORM\\Tools\\TestAsset\\ChildClass:private' => 6,\n                ],\n            ],\n            'same-attributes' => [\n                new TestAsset\\ChildWithSameAttributesClass(),\n                [\n                    'parentPublicAttribute' => 4,\n                    'parentProtectedAttribute:protected' => 5,\n                    'parentPrivateAttribute:Doctrine\\Tests\\ORM\\Tools\\TestAsset\\ParentClass:private' => 3,\n                    'parentPrivateAttribute:Doctrine\\Tests\\ORM\\Tools\\TestAsset\\ChildWithSameAttributesClass:private' => 6,\n                ],\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/CountOutputWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Platforms\\SQLServerPlatform;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Tools\\Pagination\\CountOutputWalker;\n\nclass CountOutputWalkerTest extends PaginationTestCase\n{\n    public function testCountQuery(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN p.category c JOIN p.author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, c1_.id AS id_1, a2_.id AS id_2, a2_.name AS name_3, b0_.author_id AS author_id_4, b0_.category_id AS category_id_5 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id) dctrn_result) dctrn_table',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryMixedResultsWithName(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result) dctrn_table',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryGroupBy(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p.name FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Person p GROUP BY p.name',\n        );\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertSame(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT p0_.name AS name_0 FROM Person p0_ GROUP BY p0_.name) dctrn_table',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryHaving(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT g, u, count(u.id) AS userCount FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Group g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0',\n        );\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertSame(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT count(u0_.id) AS sclr_0, g1_.id AS id_1, u0_.id AS id_2 FROM groups g1_ LEFT JOIN user_group u2_ ON g1_.id = u2_.group_id LEFT JOIN User u0_ ON u0_.id = u2_.user_id GROUP BY g1_.id HAVING sclr_0 > 0) dctrn_table',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryOrderBySqlServer(): void\n    {\n        $platform = $this->entityManager->getConnection()->getDatabasePlatform();\n        if (! $platform instanceof SQLServerPlatform) {\n            self::markTestSkipped('SQLServer only test.');\n        }\n\n        $query = $this->entityManager->createQuery(\n            'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p ORDER BY p.id',\n        );\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_) dctrn_result) dctrn_table',\n            $query->getSQL(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/CountWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Tools\\Pagination\\CountWalker;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse RuntimeException;\n\n#[Group('DDC-1613')]\nclass CountWalkerTest extends PaginationTestCase\n{\n    public function testCountQuery(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN p.category c JOIN p.author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryWithoutDistinctUsesCountStar(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN p.category c JOIN p.author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, false);\n\n        self::assertEquals(\n            'SELECT count(*) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryMixedResultsWithName(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT a0_.id) AS sclr_0 FROM Author a0_',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryKeepsGroupBy(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT b FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost b GROUP BY b.id',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ GROUP BY b0_.id',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryRemovesOrderBy(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN p.category c JOIN p.author a ORDER BY a.name',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryRemovesLimits(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN p.category c JOIN p.author a',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryHavingException(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\\Tests\\Models\\CMS\\CmsGroup g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Cannot count query that uses a HAVING clause. Use the output walkers for pagination');\n\n        $query->getSQL();\n    }\n\n    /**\n     * Arbitrary Join\n     */\n    public function testCountQueryWithArbitraryJoin(): void\n    {\n        $query = $this->entityManager->createQuery(\n            'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p LEFT JOIN Doctrine\\Tests\\ORM\\Tools\\Pagination\\Category c ON p.category = c',\n        );\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]);\n        $query->setHint(CountWalker::HINT_DISTINCT, true);\n        $query->setFirstResult(0)->setMaxResults(null);\n\n        self::assertEquals(\n            'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ LEFT JOIN Category c1_ ON (b0_.category_id = c1_.id)',\n            $query->getSQL(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryOutputWalker;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nfinal class LimitSubqueryOutputWalkerTest extends PaginationTestCase\n{\n    public function testSubqueryClonedCompletely(): void\n    {\n        $query = $this->createQuery('SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p');\n        $query->setParameter('dummy-param', 123);\n        $query->setHint('dummy-hint', 'dummy-value');\n        $query->setCacheable(true);\n\n        $walker = new LimitSubqueryOutputWalker($query, new Query\\ParserResult(), []);\n\n        self::assertNotSame($query, $walker->getQuery());\n        self::assertTrue($walker->getQuery()->hasHint('dummy-hint'));\n        self::assertSame('dummy-value', $walker->getQuery()->getHint('dummy-hint'));\n        self::assertNotSame($query->getParameters(), $walker->getQuery()->getParameters());\n        self::assertInstanceOf(Query\\Parameter::class, $param = $walker->getQuery()->getParameter('dummy-param'));\n        self::assertSame(123, $param->getValue());\n        self::assertFalse($walker->getQuery()->isCacheable());\n    }\n\n    public function testLimitSubquery(): void\n    {\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result',\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a',\n        );\n    }\n\n    public function testLimitSubqueryWithSortPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0, MIN(sclr_5) AS dctrn_minrownum FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS sclr_5, m0_.author_id AS author_id_6, m0_.category_id AS category_id_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title',\n        );\n    }\n\n    public function testLimitSubqueryWithScalarSortPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',\n            'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g ORDER BY g_quantity',\n        );\n    }\n\n    public function testLimitSubqueryWithMixedSortPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',\n            'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC',\n        );\n    }\n\n    public function testLimitSubqueryWithHiddenScalarSortPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',\n            'SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC',\n        );\n    }\n\n    public function testLimitSubqueryPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->testLimitSubquery();\n    }\n\n    public function testLimitSubqueryWithSortOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS SCLR_5, m0_.author_id AS AUTHOR_ID_6, m0_.category_id AS CATEGORY_ID_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title',\n        );\n    }\n\n    public function testLimitSubqueryWithScalarSortOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC',\n            'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g ORDER BY g_quantity',\n        );\n    }\n\n    public function testLimitSubqueryWithMixedSortOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC',\n            'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC',\n        );\n    }\n\n    public function testLimitSubqueryOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_0 FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, m0_.author_id AS AUTHOR_ID_5, m0_.category_id AS CATEGORY_ID_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result',\n            'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a',\n        );\n    }\n\n    public function testCountQueryMixedResultsWithName(): void\n    {\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result',\n            'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a',\n        );\n    }\n\n    #[Group('DDC-3336')]\n    public function testCountQueryWithArithmeticOrderByCondition(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, (1 - 1000) * 1 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1 FROM Author a0_) dctrn_result_inner ORDER BY (1 - 1000) * 1 DESC) dctrn_result',\n            'SELECT a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY (1 - 1000) * 1 DESC',\n        );\n    }\n\n    public function testCountQueryWithComplexScalarOrderByItemWithoutJoin(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_2 * imageWidth_3 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageHeight_2 * imageWidth_3 DESC) dctrn_result',\n            'SELECT a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC',\n        );\n    }\n\n    public function testCountQueryWithComplexScalarOrderByItemJoinedWithoutPartial(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_3 * imageWidth_4 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.image AS image_2, a1_.imageHeight AS imageHeight_3, a1_.imageWidth AS imageWidth_4, a1_.imageAltDesc AS imageAltDesc_5, a1_.user_id AS user_id_6 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_3 * imageWidth_4 DESC) dctrn_result',\n            'SELECT u FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC',\n        );\n    }\n\n    public function testCountQueryWithComplexScalarOrderByItemJoinedWithPartial(): void\n    {\n        $entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $query = $entityManager->createQuery(\n            'SELECT u, partial a.{id, imageAltDesc} FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC',\n        );\n\n        $query->setHydrationMode(Query::HYDRATE_ARRAY);\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);\n\n        self::assertSame(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_5 * imageWidth_6 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.imageAltDesc AS imageAltDesc_2, a1_.id AS id_3, a1_.image AS image_4, a1_.imageHeight AS imageHeight_5, a1_.imageWidth AS imageWidth_6, a1_.imageAltDesc AS imageAltDesc_7 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_5 * imageWidth_6 DESC) dctrn_result',\n            $query->getSQL(),\n        );\n    }\n\n    public function testCountQueryWithComplexScalarOrderByItemOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.image AS IMAGE_1, a0_.imageHeight AS IMAGEHEIGHT_2, a0_.imageWidth AS IMAGEWIDTH_3, a0_.imageAltDesc AS IMAGEALTDESC_4, ROW_NUMBER() OVER(ORDER BY a0_.imageHeight * a0_.imageWidth DESC) AS SCLR_5, a0_.user_id AS USER_ID_6 FROM Avatar a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC',\n        );\n    }\n\n    #[Group('DDC-3434')]\n    public function testLimitSubqueryWithHiddenSelectionInOrderBy(): void\n    {\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, a0_.name AS name_2 FROM Author a0_) dctrn_result_inner ORDER BY name_2 DESC) dctrn_result',\n            'SELECT a, a.name AS HIDDEN ord FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY ord DESC',\n        );\n    }\n\n    public function testLimitSubqueryWithColumnWithSortDirectionInName(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageAltDesc_4 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageAltDesc_4 DESC) dctrn_result',\n            'SELECT a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Avatar a ORDER BY a.imageAltDesc DESC',\n        );\n    }\n\n    public function testLimitSubqueryWithOrderByInnerJoined(): void\n    {\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT b0_.id AS id_0, a1_.id AS id_1, a1_.name AS name_2, b0_.author_id AS author_id_3, b0_.category_id AS category_id_4 FROM BlogPost b0_ INNER JOIN Author a1_ ON b0_.author_id = a1_.id) dctrn_result_inner ORDER BY name_2 ASC) dctrn_result',\n            'SELECT b FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost b JOIN b.author a ORDER BY a.name ASC',\n        );\n    }\n\n    public function testLimitSubqueryWithOrderByAndSubSelectInWhereClauseMySql(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result',\n            'SELECT b FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost b WHERE  ((SELECT COUNT(simple.id) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost simple) = 1) ORDER BY b.id DESC',\n        );\n    }\n\n    public function testLimitSubqueryWithOrderByAndSubSelectInWhereClausePgSql(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT b FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost b WHERE  ((SELECT COUNT(simple.id) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost simple) = 1) ORDER BY b.id DESC',\n        );\n    }\n\n    /**\n     * This tests ordering by property that has the 'declared' field.\n     */\n    public function testLimitSubqueryOrderByFieldFromMappedSuperclass(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.name AS name_1 FROM Banner b0_) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result',\n            'SELECT b FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Banner b ORDER BY b.id DESC',\n        );\n    }\n\n    /**\n     * Tests order by on a subselect expression (mysql).\n     */\n    public function testLimitSubqueryOrderBySubSelectOrderByExpression(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, sclr_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result_inner ORDER BY sclr_2 DESC) dctrn_result',\n            'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY first_blog_post DESC',\n        );\n    }\n\n    /**\n     * Tests order by on a subselect expression invoking RowNumberOverFunction (postgres).\n     */\n    public function testLimitSubqueryOrderBySubSelectOrderByExpressionPg(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY first_blog_post DESC',\n        );\n    }\n\n    /**\n     * Tests order by on a subselect expression invoking RowNumberOverFunction (oracle).\n     */\n    public function testLimitSubqueryOrderBySubSelectOrderByExpressionOracle(): void\n    {\n        $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());\n\n        $this->assertQuerySql(\n            'SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',\n            'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY first_blog_post DESC',\n        );\n    }\n\n    public function testParsingQueryWithDifferentLimitOffsetValuesTakesOnlyOneCacheEntry(): void\n    {\n        $queryCache = new ArrayAdapter();\n        $this->entityManager->getConfiguration()->setQueryCache($queryCache);\n\n        $query = $this->createQuery('SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a');\n\n        self::assertSame(\n            'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 20 OFFSET 10',\n            $query->getSQL(),\n        );\n\n        $query->setFirstResult(30)->setMaxResults(40);\n\n        self::assertSame(\n            'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 40 OFFSET 30',\n            $query->getSQL(),\n        );\n\n        self::assertCount(1, $queryCache->getValues());\n    }\n\n    private function createQuery(string $dql): Query\n    {\n        $query = $this->entityManager->createQuery($dql);\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);\n        $query->setFirstResult(10);\n        $query->setMaxResults(20);\n\n        return $query;\n    }\n\n    private function assertQuerySql(string $expectedSql, string $dql): void\n    {\n        $sql   = $this->entityManager->getConnection()->getDatabasePlatform()->modifyLimitQuery($expectedSql, 20, 10);\n        $query = $this->createQuery($dql);\n\n        self::assertSame($sql, $query->getSQL());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryWalker;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1613')]\nclass LimitSubqueryWalkerTest extends PaginationTestCase\n{\n    public function testLimitSubquery(): void\n    {\n        $dql        = 'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testHintCanDisableDistinct(): void\n    {\n        $dql        = 'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n        $limitQuery->setHint(Paginator::HINT_ENABLE_DISTINCT, false);\n\n        self::assertEquals(\n            'SELECT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testLimitSubqueryWithSort(): void\n    {\n        $dql        = 'SELECT p, c, a FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id ORDER BY m0_.title ASC',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testLimitSubqueryWithSortFunction(): void\n    {\n        $dql   = 'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN p.category c GROUP BY p.id ORDER BY COUNT(c.id)';\n        $query = $this->entityManager->createQuery($dql);\n\n        $limitQuery = clone $query;\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertSame(\n            'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id GROUP BY m0_.id ORDER BY COUNT(c1_.id) ASC',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testCountQueryMixedResultsWithName(): void\n    {\n        $dql        = 'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT a0_.id AS id_0 FROM Author a0_',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testAggQueryMixedResultsWithNameAndSort(): void\n    {\n        $dql   = 'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY foo DESC';\n        $query = $this->entityManager->createQuery($dql);\n\n        $limitQuery = clone $query;\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertSame(\n            'SELECT DISTINCT a0_.id AS id_0, sum(a0_.name) AS sclr_1 FROM Author a0_ ORDER BY sclr_1 DESC',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testAggQueryMultipleMixedResultsWithSort(): void\n    {\n        $dql   = 'SELECT a, sum(a.name) as foo, (SELECT count(subA.id) FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author subA WHERE subA.id = a.id ) as bar FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a ORDER BY foo DESC, bar ASC';\n        $query = $this->entityManager->createQuery($dql);\n\n        $limitQuery = clone $query;\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertSame(\n            'SELECT DISTINCT a0_.id AS id_0, sum(a0_.name) AS sclr_1, (SELECT count(a1_.id) AS sclr_3 FROM Author a1_ WHERE a1_.id = a0_.id) AS sclr_2 FROM Author a0_ ORDER BY sclr_1 DESC, sclr_2 ASC',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    #[Group('DDC-2890')]\n    public function testLimitSubqueryWithSortOnAssociation(): void\n    {\n        $dql        = 'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p ORDER BY p.author';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT m0_.id AS id_0, m0_.author_id AS sclr_1 FROM MyBlogPost m0_ ORDER BY m0_.author_id ASC',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    /**\n     * Arbitrary Join\n     */\n    public function testLimitSubqueryWithArbitraryJoin(): void\n    {\n        $dql        = 'SELECT p, c FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN Doctrine\\Tests\\ORM\\Tools\\Pagination\\Category c ON p.category = c';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id)',\n            $limitQuery->getSQL(),\n        );\n    }\n\n    public function testLimitSubqueryWithSortWithArbitraryJoin(): void\n    {\n        $dql        = 'SELECT p, c FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\MyBlogPost p JOIN Doctrine\\Tests\\ORM\\Tools\\Pagination\\Category c ON p.category = c ORDER BY p.title';\n        $query      = $this->entityManager->createQuery($dql);\n        $limitQuery = clone $query;\n\n        $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [LimitSubqueryWalker::class]);\n\n        self::assertEquals(\n            'SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id) ORDER BY m0_.title ASC',\n            $limitQuery->getSQL(),\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/PaginationTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\Tests\\OrmTestCase;\n\nabstract class PaginationTestCase extends OrmTestCase\n{\n    /** @var EntityManagerInterface */\n    public $entityManager;\n\n    protected function setUp(): void\n    {\n        $this->entityManager = $this->getTestEntityManager();\n    }\n}\n\n\n#[Entity]\nclass MyBlogPost\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Author */\n    #[ManyToOne(targetEntity: 'Author')]\n    public $author;\n\n    /** @var Category */\n    #[ManyToOne(targetEntity: 'Category')]\n    public $category;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $title;\n}\n\n#[Entity]\nclass MyAuthor\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass MyCategory\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n\n#[Entity]\nclass BlogPost\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var Author */\n    #[ManyToOne(targetEntity: 'Author')]\n    public $author;\n\n    /** @var Category */\n    #[ManyToOne(targetEntity: 'Category')]\n    public $category;\n}\n\n#[Entity]\nclass Author\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n}\n\n#[Entity]\nclass Person\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $biography;\n}\n\n#[Entity]\nclass Category\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n}\n\n\n#[Table(name: 'groups')]\n#[Entity]\nclass Group\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, User> */\n    #[ManyToMany(targetEntity: 'User', mappedBy: 'groups')]\n    public $users;\n}\n\n#[Entity]\nclass User\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @phpstan-var Collection<int, Group> */\n    #[JoinTable(name: 'user_group')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]\n    #[ManyToMany(targetEntity: 'Group', inversedBy: 'users')]\n    public $groups;\n\n    /** @var Avatar */\n    #[OneToOne(targetEntity: 'Avatar', mappedBy: 'user')]\n    public $avatar;\n}\n\n#[Entity]\nclass Avatar\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    public $id;\n\n    /** @var User */\n    #[OneToOne(targetEntity: 'User', inversedBy: 'avatar')]\n    #[JoinColumn(name: 'user_id', referencedColumnName: 'id')]\n    public $user;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $image;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $imageHeight;\n\n    /** @var int */\n    #[Column(type: 'integer')]\n    public $imageWidth;\n\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $imageAltDesc;\n}\n\n#[MappedSuperclass]\nabstract class Identified\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Entity]\nclass Banner extends Identified\n{\n    /** @var string */\n    #[Column(type: 'string', length: 255)]\n    public $name;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/PaginatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Result;\nuse Doctrine\\DBAL\\Schema\\Name\\UnquotedIdentifierFolding;\nuse Doctrine\\ORM\\Decorator\\EntityManagerDecorator;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\QueryException;\nuse Doctrine\\ORM\\Tools\\Pagination\\Paginator;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\n\nuse function enum_exists;\n\nclass PaginatorTest extends OrmTestCase\n{\n    private Connection&MockObject $connection;\n    private EntityManagerInterface&MockObject $em;\n    private AbstractHydrator&MockObject $hydrator;\n\n    protected function setUp(): void\n    {\n        $platform = $this->getMockBuilder(AbstractPlatform::class)\n            ->setConstructorArgs(enum_exists(UnquotedIdentifierFolding::class) ? [UnquotedIdentifierFolding::UPPER] : [])\n            ->getMock();\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        $this->connection = $this->getMockBuilder(Connection::class)\n            ->onlyMethods(['executeQuery'])\n            ->setConstructorArgs([[], $driver])\n            ->getMock();\n\n        $this->em = $this->getMockBuilder(EntityManagerDecorator::class)\n            ->onlyMethods(['newHydrator'])\n            ->setConstructorArgs([$this->createTestEntityManagerWithConnection($this->connection)])\n            ->getMock();\n\n        $this->hydrator = $this->createMock(AbstractHydrator::class);\n        $this->em->method('newHydrator')->willReturn($this->hydrator);\n    }\n\n    public function testExtraParametersAreStrippedWhenWalkerRemovingOriginalSelectElementsIsUsed(): void\n    {\n        $paramInWhere     = 1;\n        $paramInSubSelect = 2;\n        $returnedIds      = [10];\n\n        $this->hydrator->method('hydrateAll')->willReturn([$returnedIds]);\n\n        $query = new Query($this->em);\n        $query->setDQL(\n            'SELECT u,\n                (\n                    SELECT MAX(a.version)\n                    FROM Doctrine\\\\Tests\\\\Models\\\\CMS\\\\CmsArticle a\n                    WHERE a.user = u AND 1 = :paramInSubSelect\n                ) AS HIDDEN max_version\n            FROM Doctrine\\\\Tests\\\\Models\\\\CMS\\\\CmsUser u\n            WHERE u.id = :paramInWhere',\n        );\n        $query->setParameters(['paramInWhere' => $paramInWhere, 'paramInSubSelect' => $paramInSubSelect]);\n        $query->setMaxResults(1);\n        $paginator = (new Paginator($query, true))->setUseOutputWalkers(false);\n\n        $receivedParams = [];\n        $resultMock     = $this->createMock(Result::class);\n        $this->connection\n            ->method('executeQuery')\n            ->willReturnCallback(static function (string $sql, array $params) use (&$receivedParams, $resultMock): Result {\n                $receivedParams[] = $params;\n\n                return $resultMock;\n            });\n\n        $paginator->count();\n        $paginator->getIterator();\n\n        self::assertSame([\n            [$paramInWhere],\n            [$paramInWhere],\n            [$paramInSubSelect, $paramInWhere, $returnedIds],\n        ], $receivedParams);\n    }\n\n    public function testPaginatorNotCaringAboutExtraParametersWithoutOutputWalkers(): void\n    {\n        $result = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock();\n        $this->connection->expects(self::exactly(3))->method('executeQuery')->willReturn($result);\n\n        $this->createPaginatorWithExtraParametersWithoutOutputWalkers([])->count();\n        $this->createPaginatorWithExtraParametersWithoutOutputWalkers([[10]])->count();\n        $this->createPaginatorWithExtraParametersWithoutOutputWalkers([])->getIterator();\n    }\n\n    public function testgetIteratorDoesCareAboutExtraParametersWithoutOutputWalkersWhenResultIsNotEmpty(): void\n    {\n        $result = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock();\n        $this->connection->expects(self::exactly(1))->method('executeQuery')->willReturn($result);\n        $this->expectException(QueryException::class);\n        $this->expectExceptionMessage('Too many parameters: the query defines 1 parameters and you bound 2');\n\n        $this->createPaginatorWithExtraParametersWithoutOutputWalkers([[10]])->getIterator();\n    }\n\n    /** @param int[][] $willReturnRows */\n    private function createPaginatorWithExtraParametersWithoutOutputWalkers(array $willReturnRows): Paginator\n    {\n        $this->hydrator->method('hydrateAll')->willReturn($willReturnRows);\n        $this->connection->method('executeQuery')->with(self::anything(), []);\n\n        $query = new Query($this->em);\n        $query->setDQL('SELECT u FROM Doctrine\\\\Tests\\\\Models\\\\CMS\\\\CmsUser u');\n        $query->setParameters(['paramInWhere' => 1]);\n        $query->setMaxResults(1);\n\n        return (new Paginator($query, true))->setUseOutputWalkers(false);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Tools\\Pagination\\RootTypeWalker;\nuse Doctrine\\Tests\\DbalTypes\\Rot13Type;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\AuxiliaryEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneIdForeignKeyEntity;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\n\nclass RootTypeWalkerTest extends PaginationTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        if (! Type::hasType('rot13')) {\n            Type::addType('rot13', Rot13Type::class);\n        }\n    }\n\n    #[DataProvider('exampleQueries')]\n    public function testResolveTypeMapping(string $dqlQuery, string $expectedType): void\n    {\n        $query = $this->entityManager->createQuery($dqlQuery);\n        $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, RootTypeWalker::class);\n\n        self::assertSame($expectedType, $query->getSQL());\n    }\n\n    /** @return Generator<string, array{string, string}> */\n    public static function exampleQueries(): Generator\n    {\n        yield 'Entity with #Id column of special type' => [\n            'SELECT e.id4 FROM ' . AuxiliaryEntity::class . ' e',\n            'rot13',\n        ];\n\n        yield 'Entity where #Id is a to-one relation with special type identifier' => [\n            'SELECT e FROM ' . OwningManyToOneIdForeignKeyEntity::class . ' e',\n            'rot13',\n        ];\n\n        yield 'Simple integer ID in a query with a JOIN' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g',\n            'integer',\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\Pagination;\n\nuse Doctrine\\ORM\\Query;\nuse Doctrine\\ORM\\Query\\Parser;\nuse Doctrine\\ORM\\Tools\\Pagination\\WhereInWalker;\nuse Generator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n#[Group('DDC-1613')]\nclass WhereInWalkerTest extends PaginationTestCase\n{\n    #[DataProvider('exampleQueries')]\n    public function testDqlQueryTransformation(string $dql, string $expectedSql): void\n    {\n        $query = $this->entityManager->createQuery($dql);\n        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [WhereInWalker::class]);\n        $query->setHint(WhereInWalker::HINT_PAGINATOR_HAS_IDS, true);\n\n        $result   = (new Parser($query))->parse();\n        $executor = $result->prepareSqlExecutor($query);\n\n        self::assertEquals($expectedSql, $executor->getSqlStatements());\n        self::assertEquals([0], $result->getSqlParameterPositions(WhereInWalker::PAGINATOR_ID_ALIAS));\n    }\n\n    public static function exampleQueries(): Generator\n    {\n        yield 'no WHERE condition' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE u0_.id IN (?)',\n        ];\n\n        yield 'mixed results with name' => [\n            'SELECT a, sum(a.name) as foo FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\Author a',\n            'SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_ WHERE a0_.id IN (?)',\n        ];\n\n        yield 'single WHERE condition' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE 1 = 1',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE 1 = 1 AND u0_.id IN (?)',\n        ];\n\n        yield 'multiple WHERE conditions with AND' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE 1 = 1 AND 2 = 2',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE 1 = 1 AND 2 = 2 AND u0_.id IN (?)',\n        ];\n\n        yield 'multiple WHERE conditions with OR' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE 1 = 1 OR 2 = 2',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 OR 2 = 2) AND u0_.id IN (?)',\n        ];\n\n        yield 'multiple WHERE conditions with mixed clauses 1' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE (1 = 1 OR 2 = 2) AND 3 = 3',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 OR 2 = 2) AND 3 = 3 AND u0_.id IN (?)',\n        ];\n\n        yield 'multiple WHERE conditions with mixed clauses 2' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE 1 = 1 AND 2 = 2 OR 3 = 3',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 AND 2 = 2 OR 3 = 3) AND u0_.id IN (?)',\n        ];\n\n        yield 'WHERE NOT condition' => [\n            'SELECT u, g FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\User u JOIN u.groups g WHERE NOT 1 = 2',\n            'SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (NOT 1 = 2) AND u0_.id IN (?)',\n        ];\n\n        yield 'arbitary join with no WHERE' => [\n            'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN Doctrine\\Tests\\ORM\\Tools\\Pagination\\Category c ON p.category = c',\n            'SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE b0_.id IN (?)',\n        ];\n\n        yield 'arbitary join with single WHERE' => [\n            'SELECT p FROM Doctrine\\Tests\\ORM\\Tools\\Pagination\\BlogPost p JOIN Doctrine\\Tests\\ORM\\Tools\\Pagination\\Category c ON p.category = c WHERE 1 = 1',\n            'SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE 1 = 1 AND b0_.id IN (?)',\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Events;\nuse Doctrine\\ORM\\Mapping\\ClassMetadataFactory;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Tools\\ResolveTargetEntityListener;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversNothing;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass ResolveTargetEntityListenerTest extends OrmTestCase\n{\n    private EntityManagerInterface $em;\n\n    private ResolveTargetEntityListener $listener;\n\n    private ClassMetadataFactory $factory;\n\n    protected function setUp(): void\n    {\n        $this->em = $this->getTestEntityManager();\n        $this->em->getConfiguration()->setMetadataDriverImpl($this->createAttributeDriver());\n        $this->factory  = $this->em->getMetadataFactory();\n        $this->listener = new ResolveTargetEntityListener();\n    }\n\n    #[Group('DDC-1544')]\n    public function testResolveTargetEntityListenerCanResolveTargetEntity(): void\n    {\n        $evm = $this->em->getEventManager();\n        $this->listener->addResolveTargetEntity(ResolveTarget::class, ResolveTargetEntity::class, []);\n        $this->listener->addResolveTargetEntity(Target::class, TargetEntity::class, []);\n        $evm->addEventSubscriber($this->listener);\n\n        $cm   = $this->factory->getMetadataFor(ResolveTargetEntity::class);\n        $meta = $cm->associationMappings;\n\n        self::assertSame(TargetEntity::class, $meta['manyToMany']->targetEntity);\n        self::assertSame(ResolveTargetEntity::class, $meta['manyToOne']->targetEntity);\n        self::assertSame(ResolveTargetEntity::class, $meta['oneToMany']->targetEntity);\n        self::assertSame(TargetEntity::class, $meta['oneToOne']->targetEntity);\n\n        self::assertSame($cm, $this->factory->getMetadataFor(ResolveTarget::class));\n    }\n\n    #[Group('DDC-3385')]\n    #[Group('1181')]\n    #[Group('385')]\n    public function testResolveTargetEntityListenerCanRetrieveTargetEntityByInterfaceName(): void\n    {\n        $this->listener->addResolveTargetEntity(ResolveTarget::class, ResolveTargetEntity::class, []);\n\n        $this->em->getEventManager()->addEventSubscriber($this->listener);\n\n        $cm = $this->factory->getMetadataFor(ResolveTarget::class);\n\n        self::assertSame($this->factory->getMetadataFor(ResolveTargetEntity::class), $cm);\n    }\n\n    #[Group('DDC-2109')]\n    public function testAssertTableColumnsAreNotAddedInManyToMany(): void\n    {\n        $evm = $this->em->getEventManager();\n        $this->listener->addResolveTargetEntity(ResolveTarget::class, ResolveTargetEntity::class, []);\n        $this->listener->addResolveTargetEntity(Target::class, TargetEntity::class, []);\n\n        $evm->addEventListener(Events::loadClassMetadata, $this->listener);\n        $cm   = $this->factory->getMetadataFor(ResolveTargetEntity::class);\n        $meta = $cm->associationMappings['manyToMany'];\n\n        self::assertSame(TargetEntity::class, $meta->targetEntity);\n        self::assertEquals(['resolvetargetentity_id', 'target_id'], $meta->joinTableColumns);\n    }\n\n    #[CoversNothing]\n    #[Group('1572')]\n    #[Group('functional')]\n    public function testDoesResolveTargetEntitiesInDQLAlsoWithInterfaces(): void\n    {\n        $evm = $this->em->getEventManager();\n        $this->listener->addResolveTargetEntity(ResolveTarget::class, ResolveTargetEntity::class, []);\n\n        $evm->addEventSubscriber($this->listener);\n\n        self::assertStringMatchesFormat(\n            'SELECT%AFROM ResolveTargetEntity%A',\n            $this\n                ->em\n                ->createQuery('SELECT f FROM Doctrine\\Tests\\ORM\\Tools\\ResolveTarget f')\n                ->getSQL(),\n        );\n    }\n}\n\ninterface ResolveTarget\n{\n    public function getId(): int;\n}\n\ninterface Target extends ResolveTarget\n{\n}\n\n#[Entity]\nclass ResolveTargetEntity implements ResolveTarget\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    /** @phpstan-var Collection<int, Target> */\n    #[ManyToMany(targetEntity: 'Doctrine\\Tests\\ORM\\Tools\\Target')]\n    private $manyToMany;\n\n    #[ManyToOne(targetEntity: 'Doctrine\\Tests\\ORM\\Tools\\ResolveTarget', inversedBy: 'oneToMany')]\n    private ResolveTarget $manyToOne;\n\n    /** @phpstan-var Collection<int, ResolveTarget> */\n    #[OneToMany(targetEntity: 'Doctrine\\Tests\\ORM\\Tools\\ResolveTarget', mappedBy: 'manyToOne')]\n    private $oneToMany;\n\n    #[OneToOne(targetEntity: 'Doctrine\\Tests\\ORM\\Tools\\Target')]\n    #[JoinColumn(name: 'target_entity_id', referencedColumnName: 'id')]\n    private Target $oneToOne;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n\n#[Entity]\nclass TargetEntity implements Target\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    #[GeneratedValue(strategy: 'AUTO')]\n    private int $id;\n\n    public function getId(): int\n    {\n        return $this->id;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/SchemaToolTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools;\n\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\DBAL\\Schema\\ForeignKeyConstraintEditor;\nuse Doctrine\\DBAL\\Schema\\Index as DbalIndex;\nuse Doctrine\\DBAL\\Schema\\Index\\IndexedColumn;\nuse Doctrine\\DBAL\\Schema\\Index\\IndexType;\nuse Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint;\nuse Doctrine\\DBAL\\Schema\\PrimaryKeyConstraintEditor;\nuse Doctrine\\DBAL\\Schema\\Table as DbalTable;\nuse Doctrine\\DBAL\\Types\\EnumType;\nuse Doctrine\\DBAL\\Types\\Types;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\Index;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy;\nuse Doctrine\\ORM\\Mapping\\UniqueConstraint;\nuse Doctrine\\ORM\\Tools\\Event\\GenerateSchemaEventArgs;\nuse Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\ORM\\Tools\\ToolEvents;\nuse Doctrine\\Persistence\\Mapping\\Driver\\StaticPHPDriver;\nuse Doctrine\\Persistence\\Mapping\\RuntimeReflectionService;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmployee;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedDerivedChildClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedDerivedIdentityClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedDerivedRootClass;\nuse Doctrine\\Tests\\Models\\Enums\\Card;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\nuse Doctrine\\Tests\\Models\\Forum\\ForumAvatar;\nuse Doctrine\\Tests\\Models\\Forum\\ForumUser;\nuse Doctrine\\Tests\\Models\\GH10288\\GH10288People;\nuse Doctrine\\Tests\\Models\\NullDefault\\NullDefaultColumn;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nuse function array_map;\nuse function class_exists;\nuse function count;\nuse function current;\nuse function enum_exists;\nuse function method_exists;\n\nclass SchemaToolTest extends OrmTestCase\n{\n    public function testAddUniqueIndexForUniqueFieldAttribute(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $classes = [\n            $em->getClassMetadata(CmsAddress::class),\n            $em->getClassMetadata(CmsArticle::class),\n            $em->getClassMetadata(CmsComment::class),\n            $em->getClassMetadata(CmsEmployee::class),\n            $em->getClassMetadata(CmsGroup::class),\n            $em->getClassMetadata(CmsPhonenumber::class),\n            $em->getClassMetadata(CmsUser::class),\n        ];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('cms_users'), 'Table cms_users should exist.');\n        self::assertTrue(self::columnIsIndexed($schema->getTable('cms_users'), 'username'), 'username column should be indexed.');\n    }\n\n    public function testAttributeOptionsArgument(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $schema = $schemaTool->getSchemaFromMetadata(\n            [$em->getClassMetadata(TestEntityWithAttributeOptionsArgument::class)],\n        );\n        $table  = $schema->getTable('TestEntityWithAttributeOptionsArgument');\n\n        foreach ([$table->getOptions(), $table->getColumn('test')->getPlatformOptions()] as $options) {\n            self::assertArrayHasKey('foo', $options);\n            self::assertSame('bar', $options['foo']);\n            self::assertArrayHasKey('baz', $options);\n            self::assertSame(['key' => 'val'], $options['baz']);\n        }\n    }\n\n    #[Group('DDC-200')]\n    public function testPassColumnDefinitionToJoinColumn(): void\n    {\n        $customColumnDef = 'MEDIUMINT(6) UNSIGNED NOT NULL';\n\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $avatar                                        = $em->getClassMetadata(ForumAvatar::class);\n        $avatar->fieldMappings['id']->columnDefinition = $customColumnDef;\n        $user                                          = $em->getClassMetadata(ForumUser::class);\n\n        $classes = [$avatar, $user];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('forum_users'));\n        $table = $schema->getTable('forum_users');\n        self::assertTrue($table->hasColumn('avatar_id'));\n        self::assertEquals($customColumnDef, $table->getColumn('avatar_id')->getColumnDefinition());\n    }\n\n    #[Group('6830')]\n    public function testPassColumnOptionsToJoinColumn(): void\n    {\n        $em       = $this->getTestEntityManager();\n        $category = $em->getClassMetadata(GH6830Category::class);\n        $board    = $em->getClassMetadata(GH6830Board::class);\n\n        $schemaTool = new SchemaTool($em);\n        $schema     = $schemaTool->getSchemaFromMetadata([$category, $board]);\n\n        self::assertTrue($schema->hasTable('GH6830Category'));\n        self::assertTrue($schema->hasTable('GH6830Board'));\n\n        $tableCategory = $schema->getTable('GH6830Category');\n        $tableBoard    = $schema->getTable('GH6830Board');\n\n        self::assertTrue($tableBoard->hasColumn('category_id'));\n\n        self::assertSame(\n            $tableCategory->getColumn('id')->getFixed(),\n            $tableBoard->getColumn('category_id')->getFixed(),\n            'Foreign key/join column should have the same value of option `fixed` as the referenced column',\n        );\n\n        self::assertEquals(\n            $tableCategory->getColumn('id')->getPlatformOptions(),\n            $tableBoard->getColumn('category_id')->getPlatformOptions(),\n            'Foreign key/join column should have the same custom options as the referenced column',\n        );\n\n        self::assertEquals(\n            ['collation' => 'latin1_bin', 'foo' => 'bar'],\n            $tableBoard->getColumn('category_id')->getPlatformOptions(),\n        );\n    }\n\n    #[Group('DDC-283')]\n    public function testPostGenerateEvents(): void\n    {\n        $listener = new GenerateSchemaEventListener();\n\n        $em = $this->getTestEntityManager();\n        $em->getEventManager()->addEventListener(\n            [ToolEvents::postGenerateSchemaTable, ToolEvents::postGenerateSchema],\n            $listener,\n        );\n        $schemaTool = new SchemaTool($em);\n\n        $classes = [\n            $em->getClassMetadata(CmsAddress::class),\n            $em->getClassMetadata(CmsArticle::class),\n            $em->getClassMetadata(CmsComment::class),\n            $em->getClassMetadata(CmsEmployee::class),\n            $em->getClassMetadata(CmsGroup::class),\n            $em->getClassMetadata(CmsPhonenumber::class),\n            $em->getClassMetadata(CmsUser::class),\n        ];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertEquals(count($classes), $listener->tableCalls);\n        self::assertTrue($listener->schemaCalled);\n    }\n\n    public function testNullDefaultNotAddedToPlatformOptions(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        self::assertSame([], $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(NullDefaultColumn::class)])\n            ->getTable('NullDefaultColumn')\n            ->getColumn('nullDefault')\n            ->getPlatformOptions());\n    }\n\n    public function testEnumTypeAddedToCustomSchemaOptions(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $platformOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(Card::class)])\n            ->getTable('Card')\n            ->getColumn('suit')\n            ->getPlatformOptions();\n\n        self::assertArrayHasKey('enumType', $platformOptions);\n        self::assertSame(Suit::class, $platformOptions['enumType']);\n    }\n\n    #[Group('DDC-3671')]\n    public function testSchemaHasProperIndexesFromUniqueConstraintAttribute(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $classes    = [\n            $em->getClassMetadata(UniqueConstraintAttributeModel::class),\n        ];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('unique_constraint_attribute_table'));\n        $table = $schema->getTable('unique_constraint_attribute_table');\n\n        self::assertCount(2, $table->getIndexes());\n        self::assertTrue($table->hasIndex('primary'));\n        self::assertTrue($table->hasIndex('uniq_hash'));\n    }\n\n    public function testRemoveUniqueIndexOverruledByPrimaryKey(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $classes    = [\n            $em->getClassMetadata(FirstEntity::class),\n            $em->getClassMetadata(SecondEntity::class),\n        ];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');\n\n        $table = $schema->getTable('first_entity');\n\n        self::assertTrue($table->hasIndex('primary'), 'Table should have a primary key.');\n\n        $primaryKey = $table->getIndex('primary');\n        $indexes    = $table->getIndexes();\n\n        self::assertCount(1, $indexes, 'there should be only one index');\n        self::assertSame($primaryKey, current($indexes), 'index should be primary');\n    }\n\n    public function testSetDiscriminatorColumnWithoutLength(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $metadata   = $em->getClassMetadata(FirstEntity::class);\n\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);\n        $metadata->setDiscriminatorColumn(['name' => 'discriminator', 'type' => 'string']);\n\n        $schema = $schemaTool->getSchemaFromMetadata([$metadata]);\n\n        self::assertTrue($schema->hasTable('first_entity'));\n        $table = $schema->getTable('first_entity');\n\n        self::assertTrue($table->hasColumn('discriminator'));\n        $column = $table->getColumn('discriminator');\n\n        self::assertEquals(255, $column->getLength());\n    }\n\n    public function testSetDiscriminatorColumnWithEnumType(): void\n    {\n        if (! class_exists(EnumType::class)) {\n            self::markTestSkipped('Test valid for doctrine/dbal versions with EnumType only.');\n        }\n\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $metadata   = $em->getClassMetadata(FirstEntity::class);\n\n        $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);\n        $metadata->setDiscriminatorColumn([\n            'name' => 'discriminator',\n            'type' => Types::ENUM,\n            'enumType' => GH10288People::class,\n        ]);\n\n        $schema = $schemaTool->getSchemaFromMetadata([$metadata]);\n\n        self::assertTrue($schema->hasTable('first_entity'));\n        $table = $schema->getTable('first_entity');\n\n        self::assertTrue($table->hasColumn('discriminator'));\n        $column = $table->getColumn('discriminator');\n        self::assertEquals(GH10288People::class, $column->getPlatformOption('enumType'));\n        self::assertEquals([0 => 'boss', 1 => 'employee'], $column->getValues());\n\n        $this->expectException(MappingException::class);\n        $this->expectExceptionMessage(\"The entries 'user' in the discriminator map of class '\" . FirstEntity::class . \"' do not correspond to enum cases of '\" . GH10288People::class . \"'.\");\n        $metadata->setDiscriminatorMap(['user' => CmsUser::class, 'employee' => CmsEmployee::class]);\n    }\n\n    public function testDerivedCompositeKey(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $schema = $schemaTool->getSchemaFromMetadata(\n            [\n                $em->getClassMetadata(JoinedDerivedIdentityClass::class),\n                $em->getClassMetadata(JoinedDerivedRootClass::class),\n                $em->getClassMetadata(JoinedDerivedChildClass::class),\n            ],\n        );\n\n        self::assertTrue($schema->hasTable('joined_derived_identity'));\n        self::assertTrue($schema->hasTable('joined_derived_root'));\n        self::assertTrue($schema->hasTable('joined_derived_child'));\n\n        if (class_exists(PrimaryKeyConstraintEditor::class)) {\n            $rootTable = $schema->getTable('joined_derived_root');\n            self::assertNotNull($rootTable->getPrimaryKeyConstraint());\n            self::assertSame(['keyPart1_id', 'keyPart2'], array_map(static fn (UnqualifiedName $name) => $name->toString(), $rootTable->getPrimaryKeyConstraint()->getColumnNames()));\n\n            $childTable = $schema->getTable('joined_derived_child');\n            self::assertNotNull($childTable->getPrimaryKeyConstraint());\n            self::assertSame(['keyPart1_id', 'keyPart2'], array_map(static fn (UnqualifiedName $name) => $name->toString(), $childTable->getPrimaryKeyConstraint()->getColumnNames()));\n        } else {\n            $rootTable = $schema->getTable('joined_derived_root');\n            self::assertNotNull($rootTable->getPrimaryKey());\n            self::assertSame(['keyPart1_id', 'keyPart2'], self::getIndexedColumns($rootTable->getPrimaryKey()));\n\n            $childTable = $schema->getTable('joined_derived_child');\n            self::assertNotNull($childTable->getPrimaryKey());\n            self::assertSame(['keyPart1_id', 'keyPart2'], self::getIndexedColumns($childTable->getPrimaryKey()));\n        }\n\n        $childTableForeignKeys = $childTable->getForeignKeys();\n\n        self::assertCount(2, $childTableForeignKeys);\n\n        $expectedColumns = [\n            'joined_derived_identity' => [['keyPart1_id'], ['id']],\n            'joined_derived_root'     => [['keyPart1_id', 'keyPart2'], ['keyPart1_id', 'keyPart2']],\n        ];\n\n        foreach ($childTableForeignKeys as $foreignKey) {\n            if (class_exists(ForeignKeyConstraintEditor::class)) {\n                self::assertArrayHasKey($foreignKey->getReferencedTableName()->toString(), $expectedColumns);\n\n                [$localColumns, $foreignColumns] = $expectedColumns[$foreignKey->getReferencedTableName()->toString()];\n\n                self::assertSame($localColumns, array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencingColumnNames()));\n                self::assertSame($foreignColumns, array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencedColumnNames()));\n            } else {\n                self::assertArrayHasKey($foreignKey->getForeignTableName(), $expectedColumns);\n\n                [$localColumns, $foreignColumns] = $expectedColumns[$foreignKey->getForeignTableName()];\n\n                self::assertSame($localColumns, $foreignKey->getLocalColumns());\n                self::assertSame($foreignColumns, $foreignKey->getForeignColumns());\n            }\n        }\n    }\n\n    public function testIndexesBasedOnFields(): void\n    {\n        $em = $this->getTestEntityManager();\n        $em->getConfiguration()->setNamingStrategy(new UnderscoreNamingStrategy());\n\n        $schemaTool = new SchemaTool($em);\n        $metadata   = $em->getClassMetadata(IndexByFieldEntity::class);\n        $schema     = $schemaTool->getSchemaFromMetadata([$metadata]);\n        $table      = $schema->getTable('field_index');\n\n        self::assertEquals(['index', 'field_name'], self::getIndexedColumns($table->getIndex('index')));\n        self::assertEquals(['index', 'table'], self::getIndexedColumns($table->getIndex('uniq')));\n    }\n\n    public function testIncorrectIndexesBasedOnFields(): void\n    {\n        $em = $this->getTestEntityManager();\n        $em->getConfiguration()->setNamingStrategy(new UnderscoreNamingStrategy());\n\n        $schemaTool    = new SchemaTool($em);\n        $mappingDriver = new StaticPHPDriver([]);\n        $class         = new ClassMetadata(IncorrectIndexByFieldEntity::class);\n\n        $class->initializeReflection(new RuntimeReflectionService());\n        $mappingDriver->loadMetadataForClass(IncorrectIndexByFieldEntity::class, $class);\n\n        $this->expectException(MappingException::class);\n        $schemaTool->getSchemaFromMetadata([$class]);\n    }\n\n    public function testIncorrectUniqueConstraintsBasedOnFields(): void\n    {\n        $em = $this->getTestEntityManager();\n        $em->getConfiguration()->setNamingStrategy(new UnderscoreNamingStrategy());\n\n        $schemaTool    = new SchemaTool($em);\n        $mappingDriver = new StaticPHPDriver([]);\n        $class         = new ClassMetadata(IncorrectUniqueConstraintByFieldEntity::class);\n\n        $class->initializeReflection(new RuntimeReflectionService());\n        $mappingDriver->loadMetadataForClass(IncorrectUniqueConstraintByFieldEntity::class, $class);\n\n        $this->expectException(MappingException::class);\n        $schemaTool->getSchemaFromMetadata([$class]);\n    }\n\n    #[Group('schema-configuration')]\n    public function testConfigurationSchemaIgnoredEntity(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n\n        $classes = [\n            $em->getClassMetadata(FirstEntity::class),\n            $em->getClassMetadata(SecondEntity::class),\n        ];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');\n        self::assertTrue($schema->hasTable('second_entity'), 'Table second_entity should exist.');\n\n        $em->getConfiguration()->setSchemaIgnoreClasses([\n            SecondEntity::class,\n        ]);\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');\n        self::assertFalse($schema->hasTable('second_entity'), 'Table second_entity should not exist.');\n    }\n\n    #[Group('GH-11314')]\n    public function testLoadUniqueConstraintWithoutName(): void\n    {\n        $em     = $this->getTestEntityManager();\n        $entity = $em->getClassMetadata(GH11314Entity::class);\n\n        $schemaTool = new SchemaTool($em);\n        $schema     = $schemaTool->getSchemaFromMetadata([$entity]);\n\n        self::assertTrue($schema->hasTable('GH11314Entity'));\n\n        $tableEntity = $schema->getTable('GH11314Entity');\n\n        self::assertTrue($tableEntity->hasIndex('uniq_2d81a3ed5bf54558875f7fd5'));\n\n        $tableIndex = $tableEntity->getIndex('uniq_2d81a3ed5bf54558875f7fd5');\n\n        if (enum_exists(IndexType::class)) {\n            self::assertSame(IndexType::UNIQUE, $tableIndex->getType());\n        } else {\n            self::assertTrue($tableIndex->isUnique());\n        }\n\n        self::assertSame(['field', 'anotherField'], self::getIndexedColumns($tableIndex));\n    }\n\n    public function testQuotedIdentifiers(): void\n    {\n        $em         = $this->getTestEntityManager();\n        $schemaTool = new SchemaTool($em);\n        $classes    = [$em->getClassMetadata(QuotedEntity::class)];\n\n        $schema = $schemaTool->getSchemaFromMetadata($classes);\n\n        self::assertTrue($schema->hasTable('quoted-table'), 'Table quoted-table should exist.');\n\n        $table = $schema->getTable('quoted-table');\n\n        self::assertTrue($table->hasIndex('IDX_AA2790FB50D14D90'));\n\n        // DBAL < 4.3\n        if (! class_exists(PrimaryKeyConstraint::class)) {\n            self::assertTrue($table->isQuoted(), 'The table name must be quoted.');\n            self::assertTrue($table->hasIndex('primary'), 'Table should have a primary key.');\n\n            return;\n        }\n\n        $objectName = $table->getObjectName();\n        self::assertTrue($objectName->getUnqualifiedName()->isQuoted());\n        self::assertSame('quoted-table', $objectName->getUnqualifiedName()->getValue());\n\n        $primaryKey = $table->getPrimaryKeyConstraint();\n        self::assertNotNull($primaryKey);\n        self::assertCount(1, $primaryKey->getColumnNames());\n\n        [$pkColumn] = $primaryKey->getColumnNames();\n        self::assertTrue($pkColumn->getIdentifier()->isQuoted());\n        self::assertSame('quoted-id', $pkColumn->getIdentifier()->getValue());\n    }\n\n    /** @return string[] */\n    private static function getIndexedColumns(DbalIndex $index): array\n    {\n        if (method_exists(DbalIndex::class, 'getIndexedColumns')) {\n            return array_map(static fn (IndexedColumn $indexedColumn) => $indexedColumn->getColumnName()->toString(), $index->getIndexedColumns());\n        }\n\n        return $index->getColumns();\n    }\n\n    private static function columnIsIndexed(DbalTable $table, string $column): bool\n    {\n        foreach ($table->getIndexes() as $index) {\n            if ($index->spansColumns([$column])) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n\n#[Table(options: ['foo' => 'bar', 'baz' => ['key' => 'val']])]\n#[Entity]\nclass TestEntityWithAttributeOptionsArgument\n{\n    #[Id]\n    #[Column]\n    private int $id;\n\n    #[Column(type: 'string', options: ['foo' => 'bar', 'baz' => ['key' => 'val']])]\n    private string $test;\n}\n\nclass GenerateSchemaEventListener\n{\n    /** @var int */\n    public $tableCalls = 0;\n\n    /** @var bool */\n    public $schemaCalled = false;\n\n    public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs): void\n    {\n        $this->tableCalls++;\n    }\n\n    public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs): void\n    {\n        $this->schemaCalled = true;\n    }\n}\n\n#[Table(name: 'unique_constraint_attribute_table')]\n#[UniqueConstraint(name: 'uniq_hash', columns: ['hash'])]\n#[Entity]\nclass UniqueConstraintAttributeModel\n{\n    #[Id]\n    #[Column]\n    private int $id;\n\n    #[Column(name: 'hash', type: 'string', length: 8, nullable: false, unique: true)]\n    private string $hash;\n}\n\n#[Table(name: 'first_entity')]\n#[Entity]\nclass FirstEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'id')]\n    public $id;\n\n    /** @var SecondEntity */\n    #[OneToOne(targetEntity: 'SecondEntity')]\n    #[JoinColumn(name: 'id', referencedColumnName: 'first_entity_id')]\n    public $secondEntity;\n\n    /** @var string */\n    #[Column(name: 'name')]\n    public $name;\n}\n\n#[Table(name: 'second_entity')]\n#[Entity]\nclass SecondEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(name: 'first_entity_id')]\n    public $firstEntityId;\n\n    /** @var string */\n    #[Column(name: 'name')]\n    public $name;\n}\n\n#[Entity]\nclass GH6830Board\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var GH6830Category */\n    #[ManyToOne(targetEntity: GH6830Category::class, inversedBy: 'boards')]\n    #[JoinColumn(name: 'category_id', referencedColumnName: 'id')]\n    public $category;\n}\n\n#[Entity]\nclass GH6830Category\n{\n    /** @var string */\n    #[Id]\n    #[Column(type: 'string', length: 8, options: ['fixed' => true, 'collation' => 'latin1_bin', 'foo' => 'bar'])]\n    public $id;\n\n    /** @phpstan-var Collection<int, GH6830Board> */\n    #[OneToMany(targetEntity: GH6830Board::class, mappedBy: 'category')]\n    public $boards;\n}\n\n#[Table(name: 'field_index')]\n#[Index(name: 'index', fields: ['index', 'fieldName'])]\n#[UniqueConstraint(name: 'uniq', fields: ['index', 'table'])]\n#[Entity]\nclass IndexByFieldEntity\n{\n    /** @var int */\n    #[Id]\n    #[Column(type: 'integer')]\n    public $id;\n\n    /** @var string */\n    #[Column]\n    public $index;\n\n    /** @var string */\n    #[Column]\n    public $table;\n\n    /** @var string */\n    #[Column]\n    public $fieldName;\n}\n\n#[Entity]\n#[UniqueConstraint(columns: ['field', 'anotherField'])]\nclass GH11314Entity\n{\n    #[Column]\n    #[Id]\n    private int $id;\n\n    #[Column(name: 'field')]\n    private string $field;\n\n    #[Column(name: 'anotherField')]\n    private string $anotherField;\n}\n\nclass IncorrectIndexByFieldEntity\n{\n    /** @var int */\n    public $id;\n\n    /** @var string */\n    public $index;\n\n    /** @var string */\n    public $table;\n\n    /** @var string */\n    public $fieldName;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'                 => true,\n                'fieldName'          => 'id',\n            ],\n        );\n\n        $metadata->mapField(['fieldName' => 'index']);\n\n        $metadata->mapField(['fieldName' => 'table']);\n\n        $metadata->mapField(['fieldName' => 'fieldName']);\n\n        $metadata->setPrimaryTable(\n            [\n                'indexes' => [\n                    ['columns' => ['index'], 'fields' => ['fieldName']],\n                ],\n            ],\n        );\n    }\n}\n\nclass IncorrectUniqueConstraintByFieldEntity\n{\n    /** @var int */\n    public $id;\n\n    /** @var string */\n    public $index;\n\n    /** @var string */\n    public $table;\n\n    /** @var string */\n    public $fieldName;\n\n    public static function loadMetadata(ClassMetadata $metadata): void\n    {\n        $metadata->mapField(\n            [\n                'id'                 => true,\n                'fieldName'          => 'id',\n            ],\n        );\n\n        $metadata->mapField(['fieldName' => 'index']);\n\n        $metadata->mapField(['fieldName' => 'table']);\n\n        $metadata->mapField(['fieldName' => 'fieldName']);\n\n        $metadata->setPrimaryTable(\n            [\n                'uniqueConstraints' => [\n                    ['columns' => ['index'], 'fields' => ['fieldName']],\n                ],\n            ],\n        );\n    }\n}\n\n#[Entity]\n#[Table(name: '`quoted-table`')]\n#[Index(columns: ['`quoted-name`'])]\nclass QuotedEntity\n{\n    #[Id]\n    #[Column(name: '`quoted-id`')]\n    public int $id = 0;\n\n    #[Column(name: '`quoted-name`')]\n    public string $name = '';\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/SchemaValidatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collection;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\DiscriminatorMap;\nuse Doctrine\\ORM\\Mapping\\Embeddable;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\InheritanceType;\nuse Doctrine\\ORM\\Mapping\\InverseJoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinColumn;\nuse Doctrine\\ORM\\Mapping\\JoinTable;\nuse Doctrine\\ORM\\Mapping\\ManyToMany;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappedSuperclass;\nuse Doctrine\\ORM\\Mapping\\OneToMany;\nuse Doctrine\\ORM\\Mapping\\OneToOne;\nuse Doctrine\\ORM\\Mapping\\OrderBy;\nuse Doctrine\\ORM\\Mapping\\Table;\nuse Doctrine\\ORM\\Tools\\SchemaValidator;\nuse Doctrine\\Tests\\Models\\BigIntegers\\BigIntegers;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\OrmTestCase;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\nclass SchemaValidatorTest extends OrmTestCase\n{\n    private EntityManagerInterface|null $em = null;\n\n    private SchemaValidator|null $validator = null;\n\n    protected function setUp(): void\n    {\n        $this->em        = $this->getTestEntityManager();\n        $this->validator = new SchemaValidator($this->em);\n    }\n\n    #[DataProvider('modelSetProvider')]\n    public function testCmsModelSet(string $path): void\n    {\n        $this->em->getConfiguration()\n                 ->getMetadataDriverImpl()\n                 ->addPaths([$path]);\n\n        self::assertEmpty($this->validator->validateMapping());\n    }\n\n    public static function modelSetProvider(): array\n    {\n        return [\n            'cms'        => [__DIR__ . '/../../Models/CMS'],\n            'company'    => [__DIR__ . '/../../Models/Company'],\n            'ecommerce'  => [__DIR__ . '/../../Models/ECommerce'],\n            'forum'      => [__DIR__ . '/../../Models/Forum'],\n            'navigation' => [__DIR__ . '/../../Models/Navigation'],\n            'routing'    => [__DIR__ . '/../../Models/Routing'],\n        ];\n    }\n\n    #[Group('DDC-1439')]\n    public function testInvalidManyToManyJoinColumnSchema(): void\n    {\n        $class1 = $this->em->getClassMetadata(InvalidEntity1::class);\n        $class2 = $this->em->getClassMetadata(InvalidEntity2::class);\n\n        $ce = $this->validator->validateClass($class1);\n\n        self::assertEquals(\n            [\n                \"The inverse join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the target entity 'Doctrine\\Tests\\ORM\\Tools\\InvalidEntity2', however 'key4' are missing.\",\n                \"The join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the source entity 'Doctrine\\Tests\\ORM\\Tools\\InvalidEntity1', however 'key2' are missing.\",\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-1439')]\n    public function testInvalidToOneJoinColumnSchema(): void\n    {\n        $class1 = $this->em->getClassMetadata(InvalidEntity1::class);\n        $class2 = $this->em->getClassMetadata(InvalidEntity2::class);\n\n        $ce = $this->validator->validateClass($class2);\n\n        self::assertEquals(\n            [\n                \"The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\\Tests\\ORM\\Tools\\InvalidEntity1'.\",\n                \"The join columns of the association 'assoc' have to match to ALL identifier columns of the target entity 'Doctrine\\Tests\\ORM\\Tools\\InvalidEntity1', however 'key1, key2' are missing.\",\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-1587')]\n    public function testValidOneToOneAsIdentifierSchema(): void\n    {\n        $class1 = $this->em->getClassMetadata(DDC1587ValidEntity2::class);\n        $class2 = $this->em->getClassMetadata(DDC1587ValidEntity1::class);\n\n        $ce = $this->validator->validateClass($class1);\n\n        self::assertEquals([], $ce);\n    }\n\n    #[Group('DDC-1649')]\n    public function testInvalidTripleAssociationAsKeyMapping(): void\n    {\n        $classThree = $this->em->getClassMetadata(DDC1649Three::class);\n        $ce         = $this->validator->validateClass($classThree);\n\n        self::assertEquals(\n            [\n                \"Cannot map association 'Doctrine\\Tests\\ORM\\Tools\\DDC1649Three#two as identifier, because the target entity 'Doctrine\\Tests\\ORM\\Tools\\DDC1649Two' also maps an association as identifier.\",\n                \"The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\\Tests\\ORM\\Tools\\DDC1649Two'.\",\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-3274')]\n    public function testInvalidBiDirectionalRelationMappingMissingInversedByAttribute(): void\n    {\n        $class = $this->em->getClassMetadata(DDC3274One::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                'The field Doctrine\\Tests\\ORM\\Tools\\DDC3274One#two is on the inverse side of a bi-directional ' .\n                'relationship, but the specified mappedBy association on the target-entity ' .\n                \"Doctrine\\Tests\\ORM\\Tools\\DDC3274Two#one does not contain the required 'inversedBy: \\\"two\\\"' attribute.\",\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('9536')]\n    public function testInvalidBiDirectionalRelationMappingMissingMappedByAttribute(): void\n    {\n        $class = $this->em->getClassMetadata(Issue9536Owner::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                'The field Doctrine\\Tests\\ORM\\Tools\\Issue9536Owner#one is on the owning side of a bi-directional ' .\n                'relationship, but the specified inversedBy association on the target-entity ' .\n                \"Doctrine\\Tests\\ORM\\Tools\\Issue9536Target#two does not contain the required 'mappedBy: \\\"one\\\"' \" .\n                'attribute.',\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-3322')]\n    public function testInvalidOrderByInvalidField(): void\n    {\n        $class = $this->em->getClassMetadata(DDC3322One::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                'The association Doctrine\\Tests\\ORM\\Tools\\DDC3322One#invalidAssoc is ordered by a foreign field ' .\n                'invalidField that is not a field on the target entity Doctrine\\Tests\\ORM\\Tools\\DDC3322ValidEntity1.',\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-3322')]\n    public function testInvalidOrderByCollectionValuedAssociation(): void\n    {\n        $class = $this->em->getClassMetadata(DDC3322Two::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                'The association Doctrine\\Tests\\ORM\\Tools\\DDC3322Two#invalidAssoc is ordered by a field oneToMany ' .\n                'on Doctrine\\Tests\\ORM\\Tools\\DDC3322ValidEntity1 that is a collection-valued association.',\n            ],\n            $ce,\n        );\n    }\n\n    #[Group('DDC-3322')]\n    public function testInvalidOrderByAssociationInverseSide(): void\n    {\n        $class = $this->em->getClassMetadata(DDC3322Three::class);\n        $ce    = $this->validator->validateClass($class);\n\n        self::assertEquals(\n            [\n                'The association Doctrine\\Tests\\ORM\\Tools\\DDC3322Three#invalidAssoc is ordered by a field oneToOneInverse ' .\n                'on Doctrine\\Tests\\ORM\\Tools\\DDC3322ValidEntity1 that is the inverse side of an association.',\n            ],\n            $ce,\n        );\n    }\n\n    public function testMappedSuperclassNotPresentInDiscriminator(): void\n    {\n        $class1 = $this->em->getClassMetadata(MappedSuperclassEntity::class);\n        $ce     = $this->validator->validateClass($class1);\n\n        $this->assertEquals([], $ce);\n    }\n\n    public function testAbstractChildClassNotPresentInDiscriminator(): void\n    {\n        $class1 = $this->em->getClassMetadata(Issue9095AbstractChild::class);\n        $ce     = $this->validator->validateClass($class1);\n\n        self::assertEmpty($ce);\n    }\n\n    public function testInvalidAssociationTowardsMappedSuperclass(): void\n    {\n        $classThree = $this->em->getClassMetadata(InvalidMappedSuperClass::class);\n        $ce         = $this->validator->validateClass($classThree);\n\n        self::assertEquals(\n            [\"The target entity 'Doctrine\\Tests\\ORM\\Tools\\InvalidMappedSuperClass' specified on Doctrine\\Tests\\ORM\\Tools\\InvalidMappedSuperClass#selfWhatever is a mapped superclass. This is not possible since there is no table that a foreign key could refer to.\"],\n            $ce,\n        );\n    }\n\n    public function testBigIntProperty(): void\n    {\n        $class = $this->em->getClassMetadata(BigIntegers::class);\n\n        self::assertSame(\n            ['The field \\'Doctrine\\Tests\\Models\\BigIntegers\\BigIntegers#three\\' has the property type \\'float\\' that differs from the metadata field type \\'int|string\\' returned by the \\'bigint\\' DBAL type.'],\n            $this->validator->validateClass($class),\n        );\n    }\n}\n\n#[MappedSuperclass]\nabstract class MappedSuperclassEntity extends ParentEntity\n{\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['child' => ChildEntity::class])]\nabstract class ParentEntity\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key;\n}\n\n#[Entity]\nclass ChildEntity extends MappedSuperclassEntity\n{\n}\n\n#[Entity]\nclass InvalidEntity1\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key1;\n\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key2;\n\n    /** @var ArrayCollection */\n    #[JoinTable(name: 'Entity1Entity2')]\n    #[JoinColumn(name: 'key1', referencedColumnName: 'key1')]\n    #[InverseJoinColumn(name: 'key3', referencedColumnName: 'key3')]\n    #[ManyToMany(targetEntity: 'InvalidEntity2')]\n    protected $entity2;\n}\n\n#[Entity]\nclass InvalidEntity2\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key3;\n\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key4;\n\n    /** @var InvalidEntity1 */\n    #[ManyToOne(targetEntity: 'InvalidEntity1')]\n    protected $assoc;\n}\n\n#[Table(name: 'agent')]\n#[Entity(repositoryClass: 'Entity\\Repository\\Agent')]\nclass DDC1587ValidEntity1\n{\n    #[Id]\n    #[GeneratedValue]\n    #[Column(name: 'pk', type: 'integer')]\n    private int $pk;\n\n    #[Column(name: 'name', type: 'string', length: 32)]\n    private string $name;\n\n    /** @var Identifier */\n    #[OneToOne(targetEntity: 'DDC1587ValidEntity2', cascade: ['all'], mappedBy: 'agent')]\n    private $identifier;\n}\n\n#[Table]\n#[Entity]\nclass DDC1587ValidEntity2\n{\n    #[Id]\n    #[OneToOne(targetEntity: 'DDC1587ValidEntity1', inversedBy: 'identifier')]\n    #[JoinColumn(name: 'pk_agent', referencedColumnName: 'pk', nullable: false)]\n    private DDC1587ValidEntity1 $agent;\n\n    #[Column(name: 'num', type: 'string', length: 16, nullable: true)]\n    private string $num;\n}\n\n#[Entity]\nclass DDC1649One\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    public $id;\n}\n\n#[Entity]\nclass DDC1649Two\n{\n    /** @var DDC1649One */\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1649One')]\n    public $one;\n}\n\n#[Entity]\nclass DDC1649Three\n{\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC1649Two')]\n    #[JoinColumn(name: 'id', referencedColumnName: 'id')]\n    private DDC1649Two $two;\n}\n\n#[Entity]\nclass DDC3274One\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private $id;\n\n    #[OneToMany(targetEntity: 'DDC3274Two', mappedBy: 'one')]\n    private ArrayCollection $two;\n}\n\n#[Entity]\nclass DDC3274Two\n{\n    #[Id]\n    #[ManyToOne(targetEntity: 'DDC3274One')]\n    private DDC3274One $one;\n}\n\n#[Entity]\nclass Issue9536Target\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private $id;\n\n    #[OneToOne(targetEntity: 'Issue9536Owner')]\n    private Issue9536Owner $two;\n}\n\n#[Entity]\nclass Issue9536Owner\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private $id;\n\n    #[OneToOne(targetEntity: 'Issue9536Target', inversedBy: 'two')]\n    private Issue9536Target $one;\n}\n\n#[Entity]\nclass DDC3322ValidEntity1\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private $id;\n\n    #[ManyToOne(targetEntity: 'DDC3322One', inversedBy: 'validAssoc')]\n    private DDC3322One $oneValid;\n\n    #[ManyToOne(targetEntity: 'DDC3322One', inversedBy: 'invalidAssoc')]\n    private DDC3322One $oneInvalid;\n\n    #[ManyToOne(targetEntity: 'DDC3322Two', inversedBy: 'validAssoc')]\n    private DDC3322Two $twoValid;\n\n    #[ManyToOne(targetEntity: 'DDC3322Two', inversedBy: 'invalidAssoc')]\n    private DDC3322Two $twoInvalid;\n\n    #[ManyToOne(targetEntity: 'DDC3322Three', inversedBy: 'validAssoc')]\n    private DDC3322Three $threeValid;\n\n    #[ManyToOne(targetEntity: 'DDC3322Three', inversedBy: 'invalidAssoc')]\n    private DDC3322Three $threeInvalid;\n\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity2', mappedBy: 'manyToOne')]\n    private DDC3322ValidEntity2 $oneToMany;\n\n    #[ManyToOne(targetEntity: 'DDC3322ValidEntity2', inversedBy: 'oneToMany')]\n    private DDC3322ValidEntity2 $manyToOne;\n\n    #[OneToOne(targetEntity: 'DDC3322ValidEntity2', mappedBy: 'oneToOneOwning')]\n    private DDC3322ValidEntity2 $oneToOneInverse;\n\n    #[OneToOne(targetEntity: 'DDC3322ValidEntity2', inversedBy: 'oneToOneInverse')]\n    private DDC3322ValidEntity2 $oneToOneOwning;\n}\n\n#[Entity]\nclass DDC3322ValidEntity2\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private int $id;\n\n    #[ManyToOne(targetEntity: 'DDC3322ValidEntity1', inversedBy: 'oneToMany')]\n    private DDC3322ValidEntity1 $manyToOne;\n\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'manyToOne')]\n    private DDC3322ValidEntity1 $oneToMany;\n\n    #[OneToOne(targetEntity: 'DDC3322ValidEntity1', inversedBy: 'oneToOneInverse')]\n    private DDC3322ValidEntity1 $oneToOneOwning;\n\n    #[OneToOne(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneToOneOwning')]\n    private DDC3322ValidEntity1 $oneToOneInverse;\n}\n\n#[Entity]\nclass DDC3322One\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, DDC3322ValidEntity1> */\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneValid')]\n    #[OrderBy(['id' => 'ASC'])]\n    private $validAssoc;\n\n    /** @phpstan-var Collection<int, DDC3322ValidEntity1> */\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneInvalid')]\n    #[OrderBy(['invalidField' => 'ASC'])]\n    private $invalidAssoc;\n}\n\n#[Entity]\nclass DDC3322Two\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private int $id;\n\n    /** @phpstan-var Collection<int, DDC3322ValidEntity1> */\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'twoValid')]\n    #[OrderBy(['manyToOne' => 'ASC'])]\n    private $validAssoc;\n\n    /** @phpstan-var Collection<int, DDC3322ValidEntity1> */\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'twoInvalid')]\n    #[OrderBy(['oneToMany' => 'ASC'])]\n    private $invalidAssoc;\n}\n\n#[Entity]\nclass DDC3322Three\n{\n    #[Id]\n    #[Column]\n    #[GeneratedValue]\n    private int $id;\n\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'threeValid')]\n    #[OrderBy(['oneToOneOwning' => 'ASC'])]\n    private DDC3322ValidEntity1 $validAssoc;\n\n    /** @phpstan-var Collection<int, DDC3322ValidEntity1> */\n    #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'threeInvalid')]\n    #[OrderBy(['oneToOneInverse' => 'ASC'])]\n    private $invalidAssoc;\n}\n\n#[Embeddable]\nclass EmbeddableWithAssociation\n{\n    #[OneToOne(targetEntity: 'Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart')]\n    private ECommerceCart $cart;\n}\n\n#[Entity]\n#[InheritanceType('SINGLE_TABLE')]\n#[DiscriminatorMap(['child' => Issue9095Child::class])]\nabstract class Issue9095Parent\n{\n    /** @var mixed */\n    #[Id]\n    #[Column]\n    protected $key;\n}\n\n#[Entity]\nabstract class Issue9095AbstractChild extends Issue9095Parent\n{\n}\n\n#[Entity]\nclass Issue9095Child extends Issue9095AbstractChild\n{\n}\n\n#[MappedSuperclass]\nclass InvalidMappedSuperClass\n{\n    /** @phpstan-var Collection<int, self> */\n    #[ManyToMany(targetEntity: 'InvalidMappedSuperClass', mappedBy: 'invalid')]\n    private $selfWhatever;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/TestAsset/ChildClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\TestAsset;\n\nfinal class ChildClass extends ParentClass\n{\n    /** @var int */\n    public $childPublicAttribute = 4;\n    /** @var int */\n    protected $childProtectedAttribute = 5;\n    /** @var int */\n    private $childPrivateAttribute = 6;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/TestAsset/ChildWithSameAttributesClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\TestAsset;\n\nfinal class ChildWithSameAttributesClass extends ParentClass\n{\n    /** @var int */\n    public $parentPublicAttribute = 4;\n    /** @var int */\n    protected $parentProtectedAttribute = 5;\n    /** @var int */\n    private $parentPrivateAttribute = 6;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Tools/TestAsset/ParentClass.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Tools\\TestAsset;\n\nabstract class ParentClass\n{\n    /** @var int */\n    public $parentPublicAttribute = 1;\n    /** @var int */\n    protected $parentProtectedAttribute = 2;\n    /** @var int */\n    private $parentPrivateAttribute = 3;\n}\n"
  },
  {
    "path": "tests/Tests/ORM/UnitOfWorkTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Driver\\Statement;\nuse Doctrine\\DBAL\\Exception;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Schema\\Name\\UnquotedIdentifierFolding;\nuse Doctrine\\ORM\\EntityNotFoundException;\nuse Doctrine\\ORM\\Exception\\EntityIdentityCollisionException;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Mapping\\Column;\nuse Doctrine\\ORM\\Mapping\\Entity;\nuse Doctrine\\ORM\\Mapping\\GeneratedValue;\nuse Doctrine\\ORM\\Mapping\\Id;\nuse Doctrine\\ORM\\Mapping\\ManyToOne;\nuse Doctrine\\ORM\\Mapping\\MappingException;\nuse Doctrine\\ORM\\Mapping\\Version;\nuse Doctrine\\ORM\\OptimisticLockException;\nuse Doctrine\\ORM\\ORMInvalidArgumentException;\nuse Doctrine\\ORM\\UnitOfWork;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse Doctrine\\Tests\\Mocks\\EntityPersisterMock;\nuse Doctrine\\Tests\\Mocks\\UnitOfWorkMock;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Forum\\ForumAvatar;\nuse Doctrine\\Tests\\Models\\Forum\\ForumUser;\nuse Doctrine\\Tests\\Models\\MixedToOneIdentity\\Country;\nuse Doctrine\\Tests\\OrmTestCase;\nuse Exception as BaseException;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse stdClass;\n\nuse function enum_exists;\nuse function is_object;\nuse function random_int;\nuse function uniqid;\n\n/**\n * UnitOfWork tests.\n */\nclass UnitOfWorkTest extends OrmTestCase\n{\n    /**\n     * SUT\n     */\n    private UnitOfWorkMock $_unitOfWork;\n\n    /**\n     * Provides a sequence mock to the UnitOfWork\n     */\n    private Connection $connection;\n\n    /**\n     * The EntityManager mock that provides the mock persisters\n     */\n    private EntityManagerMock $_emMock;\n\n    private EventManager&MockObject $eventManager;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $platform = $this->createMock(AbstractPlatform::class);\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $driverStatement = $this->createMock(Statement::class);\n\n        $driverConnection = $this->createMock(Driver\\Connection::class);\n        $driverConnection->method('prepare')\n            ->willReturn($driverStatement);\n        $driverConnection->method('lastInsertId')\n            ->willReturnOnConsecutiveCalls(1, 2, 3, 4, 5, 6);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n        $driver->method('connect')\n            ->willReturn($driverConnection);\n\n        $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock();\n        $this->connection   = new Connection([], $driver, null, $this->eventManager);\n        $this->_emMock      = new EntityManagerMock($this->connection);\n        // SUT\n        $this->_unitOfWork = new UnitOfWorkMock($this->_emMock);\n        $this->_emMock->setUnitOfWork($this->_unitOfWork);\n    }\n\n    public function testRegisterRemovedOnNewEntityIsIgnored(): void\n    {\n        $user           = new ForumUser();\n        $user->username = 'romanb';\n        self::assertFalse($this->_unitOfWork->isScheduledForDelete($user));\n        $this->_unitOfWork->scheduleForDelete($user);\n        self::assertFalse($this->_unitOfWork->isScheduledForDelete($user));\n    }\n\n    /* Operational tests */\n\n    public function testSavingSingleEntityWithIdentityColumnForcesInsert(): void\n    {\n        // Setup fake persister and id generator for identity generation\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class));\n        $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister);\n        $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n\n        // Test\n        $user           = new ForumUser();\n        $user->username = 'romanb';\n        $this->_unitOfWork->persist($user);\n\n        // Check\n        self::assertCount(0, $userPersister->getInserts());\n        self::assertCount(0, $userPersister->getUpdates());\n        self::assertCount(0, $userPersister->getDeletes());\n        self::assertFalse($this->_unitOfWork->isInIdentityMap($user));\n        // should no longer be scheduled for insert\n        self::assertTrue($this->_unitOfWork->isScheduledForInsert($user));\n\n        // Now lets check whether a subsequent commit() does anything\n        $userPersister->reset();\n\n        // Test\n        $this->_unitOfWork->commit();\n\n        // Check.\n        self::assertCount(1, $userPersister->getInserts());\n        self::assertCount(0, $userPersister->getUpdates());\n        self::assertCount(0, $userPersister->getDeletes());\n\n        // should have an id\n        self::assertIsNumeric($user->id);\n    }\n\n    #[Group('#11977')]\n    public function testMultipleInsertsAreBatchedInThePersister(): void\n    {\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(Country::class));\n        $this->_unitOfWork->setEntityPersister(Country::class, $userPersister);\n\n        $country1          = new Country();\n        $country1->country = 'Italy';\n        $country2          = new Country();\n        $country2->country = 'Germany';\n\n        $this->_unitOfWork->persist($country1);\n        $this->_unitOfWork->persist($country2);\n        $this->_unitOfWork->commit();\n\n        self::assertCount(2, $userPersister->getInserts());\n        self::assertSame(1, $userPersister->countOfExecuteInsertCalls());\n    }\n\n    /**\n     * Tests a scenario where a save() operation is cascaded from a ForumUser\n     * to its associated ForumAvatar, both entities using IDENTITY id generation.\n     */\n    public function testCascadedIdentityColumnInsert(): void\n    {\n        // Setup fake persister and id generator for identity generation\n        //ForumUser\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class));\n        $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister);\n        $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n        // ForumAvatar\n        $avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumAvatar::class));\n        $this->_unitOfWork->setEntityPersister(ForumAvatar::class, $avatarPersister);\n        $avatarPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n\n        // Test\n        $user           = new ForumUser();\n        $user->username = 'romanb';\n        $avatar         = new ForumAvatar();\n        $user->avatar   = $avatar;\n        $this->_unitOfWork->persist($user); // save cascaded to avatar\n\n        $this->_unitOfWork->commit();\n\n        self::assertIsNumeric($user->id);\n        self::assertIsNumeric($avatar->id);\n\n        self::assertCount(1, $userPersister->getInserts());\n        self::assertCount(0, $userPersister->getUpdates());\n        self::assertCount(0, $userPersister->getDeletes());\n\n        self::assertCount(1, $avatarPersister->getInserts());\n        self::assertCount(0, $avatarPersister->getUpdates());\n        self::assertCount(0, $avatarPersister->getDeletes());\n    }\n\n    public function testGetEntityStateOnVersionedEntityWithAssignedIdentifier(): void\n    {\n        $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(VersionedAssignedIdentifierEntity::class));\n        $this->_unitOfWork->setEntityPersister(VersionedAssignedIdentifierEntity::class, $persister);\n\n        $e     = new VersionedAssignedIdentifierEntity();\n        $e->id = 42;\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($e));\n        self::assertFalse($persister->isExistsCalled());\n    }\n\n    public function testGetEntityStateWithAssignedIdentity(): void\n    {\n        $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CmsPhonenumber::class));\n        $this->_unitOfWork->setEntityPersister(CmsPhonenumber::class, $persister);\n\n        $ph              = new CmsPhonenumber();\n        $ph->phonenumber = '12345';\n\n        self::assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($ph));\n        self::assertTrue($persister->isExistsCalled());\n\n        $persister->reset();\n\n        // if the entity is already managed the exists() check should be skipped\n        $this->_unitOfWork->registerManaged($ph, ['phonenumber' => '12345'], []);\n        self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($ph));\n        self::assertFalse($persister->isExistsCalled());\n        $ph2              = new CmsPhonenumber();\n        $ph2->phonenumber = '12345';\n        self::assertEquals(UnitOfWork::STATE_DETACHED, $this->_unitOfWork->getEntityState($ph2));\n        self::assertFalse($persister->isExistsCalled());\n    }\n\n    /**\n     * DDC-2086 [GH-484] Prevented 'Undefined index' notice when updating.\n     */\n    public function testNoUndefinedIndexNoticeOnScheduleForUpdateWithoutChanges(): void\n    {\n        // Setup fake persister and id generator\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class));\n        $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n        $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister);\n\n        // Create a test user\n        $user           = new ForumUser();\n        $user->username = 'Jasper';\n        $this->_unitOfWork->persist($user);\n        $this->_unitOfWork->commit();\n\n        // Schedule user for update without changes\n        $this->_unitOfWork->scheduleForUpdate($user);\n\n        self::assertNotEmpty($this->_unitOfWork->getScheduledEntityUpdates());\n\n        // This commit should not raise an E_NOTICE\n        $this->_unitOfWork->commit();\n\n        self::assertEmpty($this->_unitOfWork->getScheduledEntityUpdates());\n    }\n\n    #[DataProvider('invalidAssociationValuesDataProvider')]\n    #[Group('DDC-3490')]\n    public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue(mixed $invalidValue): void\n    {\n        $this->_unitOfWork->setEntityPersister(\n            ForumUser::class,\n            new EntityPersisterMock(\n                $this->_emMock,\n                $this->_emMock->getClassMetadata(ForumUser::class),\n            ),\n        );\n\n        $user           = new ForumUser();\n        $user->username = 'John';\n        $user->avatar   = $invalidValue;\n\n        $this->expectException(ORMInvalidArgumentException::class);\n\n        $this->_unitOfWork->persist($user);\n    }\n\n    #[DataProvider('invalidAssociationValuesDataProvider')]\n    #[Group('DDC-3490')]\n    public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationValue(mixed $invalidValue): void\n    {\n        $metadata = $this->_emMock->getClassMetadata(ForumUser::class);\n\n        $this->_unitOfWork->setEntityPersister(\n            ForumUser::class,\n            new EntityPersisterMock($this->_emMock, $metadata),\n        );\n\n        $user = new ForumUser();\n\n        $this->_unitOfWork->persist($user);\n\n        $user->username = 'John';\n        $user->avatar   = $invalidValue;\n\n        if (\n            is_object($invalidValue) &&\n            ! $invalidValue instanceof ArrayCollection &&\n            $this->_emMock->getConfiguration()->isNativeLazyObjectsEnabled()\n        ) {\n            // in the case of stdClass, the changeset is rejected because\n            // stdClass is not a valid entity\n            // when using native lazy objects, this happens because UnitOfWork::isUninitializedObject()\n            // needs to load the class metadata to do its job\n            $this->expectException(MappingException::class);\n        } else {\n            $this->expectException(ORMInvalidArgumentException::class);\n        }\n\n        $this->_unitOfWork->computeChangeSet($metadata, $user);\n    }\n\n    #[Group('DDC-3619')]\n    #[Group('1338')]\n    public function testRemovedAndRePersistedEntitiesAreInTheIdentityMapAndAreNotGarbageCollected(): void\n    {\n        $entity     = new ForumUser();\n        $entity->id = 123;\n\n        $this->_unitOfWork->registerManaged($entity, ['id' => 123], []);\n        self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity));\n        self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity));\n        self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));\n\n        $this->_unitOfWork->remove($entity);\n        self::assertSame(UnitOfWork::STATE_REMOVED, $this->_unitOfWork->getEntityState($entity));\n        self::assertTrue($this->_unitOfWork->isScheduledForDelete($entity));\n        self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));\n\n        $this->_unitOfWork->persist($entity);\n        self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity));\n        self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity));\n        self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));\n    }\n\n    /**\n     * Data Provider\n     *\n     * @return mixed[][]\n     */\n    public static function invalidAssociationValuesDataProvider(): array\n    {\n        return [\n            ['foo'],\n            [['foo']],\n            [''],\n            [[]],\n            [new stdClass()],\n            [new ArrayCollection()],\n        ];\n    }\n\n    #[DataProvider('entitiesWithValidIdentifiersProvider')]\n    public function testAddToIdentityMapValidIdentifiers(object $entity, string $idHash): void\n    {\n        $this->_unitOfWork->persist($entity);\n        $this->_unitOfWork->addToIdentityMap($entity);\n\n        self::assertSame($entity, $this->_unitOfWork->getByIdHash($idHash, $entity::class));\n    }\n\n    /** @phpstan-return array<string, array{object, string}> */\n    public static function entitiesWithValidIdentifiersProvider(): array\n    {\n        $emptyString = new EntityWithStringIdentifier();\n\n        $emptyString->id = '';\n\n        $nonEmptyString = new EntityWithStringIdentifier();\n\n        $nonEmptyString->id = uniqid('id', true);\n\n        $emptyStrings = new EntityWithCompositeStringIdentifier();\n\n        $emptyStrings->id1 = '';\n        $emptyStrings->id2 = '';\n\n        $nonEmptyStrings = new EntityWithCompositeStringIdentifier();\n\n        $nonEmptyStrings->id1 = uniqid('id1', true);\n        $nonEmptyStrings->id2 = uniqid('id2', true);\n\n        $booleanTrue = new EntityWithBooleanIdentifier();\n\n        $booleanTrue->id = true;\n\n        $booleanFalse = new EntityWithBooleanIdentifier();\n\n        $booleanFalse->id = false;\n\n        return [\n            'empty string, single field'     => [$emptyString, ''],\n            'non-empty string, single field' => [$nonEmptyString, $nonEmptyString->id],\n            'empty strings, two fields'      => [$emptyStrings, ' '],\n            'non-empty strings, two fields'  => [$nonEmptyStrings, $nonEmptyStrings->id1 . ' ' . $nonEmptyStrings->id2],\n            'boolean true'                   => [$booleanTrue, '1'],\n            'boolean false'                  => [$booleanFalse, ''],\n        ];\n    }\n\n    public function testRegisteringAManagedInstanceRequiresANonEmptyIdentifier(): void\n    {\n        $this->expectException(ORMInvalidArgumentException::class);\n\n        $this->_unitOfWork->registerManaged(new EntityWithBooleanIdentifier(), [], []);\n    }\n\n    /** @param array<string, mixed> $identifier */\n    #[DataProvider('entitiesWithInvalidIdentifiersProvider')]\n    public function testAddToIdentityMapInvalidIdentifiers(object $entity, array $identifier): void\n    {\n        $this->expectException(ORMInvalidArgumentException::class);\n\n        $this->_unitOfWork->registerManaged($entity, $identifier, []);\n    }\n\n    /** @phpstan-return array<string, array{object, array<string, mixed>}> */\n    public static function entitiesWithInvalidIdentifiersProvider(): array\n    {\n        $firstNullString = new EntityWithCompositeStringIdentifier();\n\n        $firstNullString->id2 = uniqid('id2', true);\n\n        $secondNullString = new EntityWithCompositeStringIdentifier();\n\n        $secondNullString->id1 = uniqid('id1', true);\n\n        return [\n            'null string, single field'      => [new EntityWithStringIdentifier(), ['id' => null]],\n            'null strings, two fields'       => [new EntityWithCompositeStringIdentifier(), ['id1' => null, 'id2' => null]],\n            'first null string, two fields'  => [$firstNullString, ['id1' => null, 'id2' => $firstNullString->id2]],\n            'second null string, two fields' => [$secondNullString, ['id1' => $secondNullString->id1, 'id2' => null]],\n        ];\n    }\n\n    /**\n     * Unlike next test, this one demonstrates that the problem does\n     * not necessarily reproduce if all the pieces are being flushed together.\n     */\n    #[Group('DDC-2922')]\n    #[Group('#1521')]\n    public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughCascadedAssociationsFirst(): void\n    {\n        $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class));\n        $persister2 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(EntityWithCascadingAssociation::class));\n        $persister3 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(EntityWithNonCascadingAssociation::class));\n        $this->_unitOfWork->setEntityPersister(CascadePersistedEntity::class, $persister1);\n        $this->_unitOfWork->setEntityPersister(EntityWithCascadingAssociation::class, $persister2);\n        $this->_unitOfWork->setEntityPersister(EntityWithNonCascadingAssociation::class, $persister3);\n\n        $cascadePersisted = new CascadePersistedEntity();\n        $cascading        = new EntityWithCascadingAssociation();\n        $nonCascading     = new EntityWithNonCascadingAssociation();\n\n        // First we persist and flush a EntityWithCascadingAssociation with\n        // the cascading association not set. Having the \"cascading path\" involve\n        // a non-new object is important to show that the ORM should be considering\n        // cascades across entity changesets in subsequent flushes.\n        $cascading->cascaded       = $cascadePersisted;\n        $nonCascading->nonCascaded = $cascadePersisted;\n\n        $this->_unitOfWork->persist($cascading);\n        $this->_unitOfWork->persist($nonCascading);\n\n        $this->_unitOfWork->commit();\n\n        self::assertCount(1, $persister1->getInserts());\n        self::assertCount(1, $persister2->getInserts());\n        self::assertCount(1, $persister3->getInserts());\n    }\n\n    /**\n     * This test exhibits the bug describe in the ticket, where an object that\n     * ought to be reachable causes errors.\n     */\n    #[Group('DDC-2922')]\n    #[Group('#1521')]\n    public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughNonCascadedAssociationsFirst(): void\n    {\n        $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class));\n        $persister2 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(EntityWithCascadingAssociation::class));\n        $persister3 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(EntityWithNonCascadingAssociation::class));\n        $this->_unitOfWork->setEntityPersister(CascadePersistedEntity::class, $persister1);\n        $this->_unitOfWork->setEntityPersister(EntityWithCascadingAssociation::class, $persister2);\n        $this->_unitOfWork->setEntityPersister(EntityWithNonCascadingAssociation::class, $persister3);\n\n        $cascadePersisted = new CascadePersistedEntity();\n        $cascading        = new EntityWithCascadingAssociation();\n        $nonCascading     = new EntityWithNonCascadingAssociation();\n\n        // First we persist and flush a EntityWithCascadingAssociation with\n        // the cascading association not set. Having the \"cascading path\" involve\n        // a non-new object is important to show that the ORM should be considering\n        // cascades across entity changesets in subsequent flushes.\n        $cascading->cascaded = null;\n\n        $this->_unitOfWork->persist($cascading);\n        $this->_unitOfWork->commit();\n\n        self::assertCount(0, $persister1->getInserts());\n        self::assertCount(1, $persister2->getInserts());\n        self::assertCount(0, $persister3->getInserts());\n\n        // Note that we have NOT directly persisted the CascadePersistedEntity,\n        // and EntityWithNonCascadingAssociation does NOT have a configured\n        // cascade-persist.\n        $nonCascading->nonCascaded = $cascadePersisted;\n\n        // However, EntityWithCascadingAssociation *does* have a cascade-persist\n        // association, which ought to allow us to save the CascadePersistedEntity\n        // anyway through that connection.\n        $cascading->cascaded = $cascadePersisted;\n\n        $this->_unitOfWork->persist($nonCascading);\n        $this->_unitOfWork->commit();\n\n        self::assertCount(1, $persister1->getInserts());\n        self::assertCount(1, $persister2->getInserts());\n        self::assertCount(1, $persister3->getInserts());\n    }\n\n    /**\n     * This test exhibits the bug describe in the ticket, where an object that\n     * ought to be reachable causes errors.\n     */\n    #[Group('DDC-2922')]\n    #[Group('#1521')]\n    public function testPreviousDetectedIllegalNewNonCascadedEntitiesAreCleanedUpOnSubsequentCommits(): void\n    {\n        $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class));\n        $persister2 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(EntityWithNonCascadingAssociation::class));\n        $this->_unitOfWork->setEntityPersister(CascadePersistedEntity::class, $persister1);\n        $this->_unitOfWork->setEntityPersister(EntityWithNonCascadingAssociation::class, $persister2);\n\n        $cascadePersisted = new CascadePersistedEntity();\n        $nonCascading     = new EntityWithNonCascadingAssociation();\n\n        // We explicitly cause the ORM to detect a non-persisted new entity in the association graph:\n        $nonCascading->nonCascaded = $cascadePersisted;\n\n        $this->_unitOfWork->persist($nonCascading);\n\n        try {\n            $this->_unitOfWork->commit();\n\n            self::fail('An exception was supposed to be raised');\n        } catch (ORMInvalidArgumentException) {\n            self::assertEmpty($persister1->getInserts());\n            self::assertEmpty($persister2->getInserts());\n        }\n\n        $this->_unitOfWork->clear();\n        $this->_unitOfWork->persist(new CascadePersistedEntity());\n        $this->_unitOfWork->commit();\n\n        // Persistence operations should just recover normally:\n        self::assertCount(1, $persister1->getInserts());\n        self::assertCount(0, $persister2->getInserts());\n    }\n\n    #[Group('#7946')] // Throw OptimisticLockException when connection::commit() returns false.\n    public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitFails(): void\n    {\n        $platform = $this->getMockBuilder(AbstractPlatform::class)\n            ->setConstructorArgs(enum_exists(UnquotedIdentifierFolding::class) ? [UnquotedIdentifierFolding::UPPER] : [])\n            ->getMock();\n        $platform->method('supportsIdentityColumns')\n            ->willReturn(true);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        // Set another connection mock that fail on commit\n        $this->connection  = $this->getMockBuilder(Connection::class)\n            ->onlyMethods(['commit'])\n            ->setConstructorArgs([[], $driver])\n            ->getMock();\n        $this->_emMock     = new EntityManagerMock($this->connection);\n        $this->_unitOfWork = new UnitOfWorkMock($this->_emMock);\n        $this->_emMock->setUnitOfWork($this->_unitOfWork);\n\n        $this->connection->method('commit')\n            ->willThrowException($this->createStub(Exception::class));\n\n        // Setup fake persister and id generator\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class));\n        $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n        $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister);\n\n        // Create a test user\n        $user           = new ForumUser();\n        $user->username = 'Jasper';\n        $this->_unitOfWork->persist($user);\n\n        $this->expectException(OptimisticLockException::class);\n        $this->_unitOfWork->commit();\n    }\n\n    public function testItThrowsWhenLookingUpIdentifierForUnknownEntity(): void\n    {\n        $this->expectException(EntityNotFoundException::class);\n        $this->_unitOfWork->getEntityIdentifier(new stdClass());\n    }\n\n    public function testRemovedEntityIsRemovedFromManyToManyCollection(): void\n    {\n        $group       = new CmsGroup();\n        $group->name = 'test';\n        $this->_unitOfWork->persist($group);\n\n        $user       = new CmsUser();\n        $user->name = 'test';\n        $user->groups->add($group);\n        $this->_unitOfWork->persist($user);\n\n        $this->_unitOfWork->commit();\n\n        self::assertFalse($user->groups->isDirty());\n\n        $this->_unitOfWork->remove($group);\n        $this->_unitOfWork->commit();\n\n        // Test that the removed entity has been removed from the many to many collection\n        self::assertEmpty(\n            $user->groups,\n            'the removed entity should have been removed from the many to many collection',\n        );\n\n        // Collection is clean, snapshot has been updated\n        self::assertFalse($user->groups->isDirty());\n        self::assertEmpty($user->groups->getSnapshot());\n    }\n\n    public function testRemovedEntityIsRemovedFromOneToManyCollection(): void\n    {\n        $user       = new CmsUser();\n        $user->name = 'test';\n\n        $phonenumber              = new CmsPhonenumber();\n        $phonenumber->phonenumber = '0800-123456';\n\n        $user->addPhonenumber($phonenumber);\n\n        $this->_unitOfWork->persist($user);\n        $this->_unitOfWork->persist($phonenumber);\n        $this->_unitOfWork->commit();\n\n        self::assertFalse($user->phonenumbers->isDirty());\n\n        $this->_unitOfWork->remove($phonenumber);\n        $this->_unitOfWork->commit();\n\n        // Test that the removed entity has been removed from the one to many collection\n        self::assertEmpty($user->phonenumbers);\n\n        // Collection is clean, snapshot has been updated\n        self::assertFalse($user->phonenumbers->isDirty());\n        self::assertEmpty($user->phonenumbers->getSnapshot());\n    }\n\n    public function testItThrowsWhenApplicationProvidedIdsCollide(): void\n    {\n        // We're using application-provided IDs and assign the same ID twice\n        // Note this is about colliding IDs in the identity map in memory.\n        // Duplicate database-level IDs would be spotted when the EM is flushed.\n\n        $phone1              = new CmsPhonenumber();\n        $phone1->phonenumber = '1234';\n        $this->_unitOfWork->persist($phone1);\n\n        $phone2              = new CmsPhonenumber();\n        $phone2->phonenumber = '1234';\n\n        $this->expectException(EntityIdentityCollisionException::class);\n        $this->expectExceptionMessageMatches('/another object .* was already present for the same ID/');\n\n        $this->_unitOfWork->persist($phone2);\n    }\n\n    public function testItPreservesTheOriginalExceptionOnRollbackFailure(): void\n    {\n        $driver = $this->createStub(Driver::class);\n        $driver->method('connect')\n            ->willReturn($this->createMock(Driver\\Connection::class));\n\n        $connection        = new class (['platform' => $this->createStub(AbstractPlatform::class)], $driver) extends Connection {\n            public function commit(): void\n            {\n                throw new BaseException('Commit failed');\n            }\n\n            public function rollBack(): void\n            {\n                throw new BaseException('Rollback exception');\n            }\n        };\n        $this->_emMock     = new EntityManagerMock($connection);\n        $this->_unitOfWork = new UnitOfWorkMock($this->_emMock);\n        $this->_emMock->setUnitOfWork($this->_unitOfWork);\n\n        // Setup fake persister and id generator\n        $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class));\n        $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);\n        $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister);\n\n        // Create a test user\n        $user           = new ForumUser();\n        $user->username = 'Jasper';\n        $this->_unitOfWork->persist($user);\n\n        try {\n            $this->_unitOfWork->commit();\n            self::fail('Exception expected');\n        } catch (BaseException $e) {\n            self::assertSame('Rollback exception', $e->getMessage());\n            self::assertNotNull($e->getPrevious());\n            self::assertSame('Commit failed', $e->getPrevious()->getMessage());\n        }\n    }\n}\n\n\n#[Entity]\nclass VersionedAssignedIdentifierEntity\n{\n    #[Id]\n    #[Column(type: 'integer')]\n    public int $id;\n\n    #[Version]\n    #[Column(type: 'integer')]\n    public int $version;\n}\n\n#[Entity]\nclass EntityWithStringIdentifier\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public string|null $id = null;\n}\n\n#[Entity]\nclass EntityWithBooleanIdentifier\n{\n    #[Id]\n    #[Column(type: 'boolean')]\n    public bool|null $id = null;\n}\n\n#[Entity]\nclass EntityWithCompositeStringIdentifier\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public string|null $id1 = null;\n\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public string|null $id2 = null;\n}\n\n#[Entity]\nclass EntityWithRandomlyGeneratedField\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    public string $id;\n\n    #[Column(type: 'integer')]\n    public int $generatedField;\n\n    public function __construct()\n    {\n        $this->id             = uniqid('id', true);\n        $this->generatedField = random_int(0, 100000);\n    }\n}\n\n#[Entity]\nclass CascadePersistedEntity\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    private string $id;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n\n#[Entity]\nclass EntityWithCascadingAssociation\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    private string $id;\n\n    #[ManyToOne(targetEntity: CascadePersistedEntity::class, cascade: ['persist'])]\n    public CascadePersistedEntity|null $cascaded = null;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n\n#[Entity]\nclass EntityWithNonCascadingAssociation\n{\n    #[Id]\n    #[Column(type: 'string', length: 255)]\n    #[GeneratedValue(strategy: 'NONE')]\n    private string $id;\n\n    #[ManyToOne(targetEntity: CascadePersistedEntity::class)]\n    public CascadePersistedEntity|null $nonCascaded = null;\n\n    public function __construct()\n    {\n        $this->id = uniqid(self::class, true);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Utility/HierarchyDiscriminatorResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Utility;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Utility\\HierarchyDiscriminatorResolver;\nuse PHPUnit\\Framework\\TestCase;\n\nclass HierarchyDiscriminatorResolverTest extends TestCase\n{\n    public function testResolveDiscriminatorsForClass(): void\n    {\n        $childClassMetadata                     = new ClassMetadata('ChildEntity');\n        $childClassMetadata->name               = 'Some\\Class\\Child\\Name';\n        $childClassMetadata->discriminatorValue = 'child-discriminator';\n\n        $classMetadata                     = new ClassMetadata('Entity');\n        $classMetadata->subClasses         = [$childClassMetadata->name];\n        $classMetadata->name               = 'Some\\Class\\Name';\n        $classMetadata->discriminatorValue = 'discriminator';\n\n        $em = $this->createMock(EntityManagerInterface::class);\n        $em->expects(self::exactly(2))\n            ->method('getClassMetadata')\n            ->willReturnMap(\n                [\n                    [$classMetadata->name, $classMetadata],\n                    [$childClassMetadata->name, $childClassMetadata],\n                ],\n            );\n\n        $discriminators = HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($classMetadata, $em);\n\n        self::assertCount(2, $discriminators);\n        self::assertArrayHasKey($classMetadata->discriminatorValue, $discriminators);\n        self::assertArrayHasKey($childClassMetadata->discriminatorValue, $discriminators);\n    }\n\n    public function testResolveDiscriminatorsForClassWithNoSubclasses(): void\n    {\n        $classMetadata                     = new ClassMetadata('Entity');\n        $classMetadata->subClasses         = [];\n        $classMetadata->name               = 'Some\\Class\\Name';\n        $classMetadata->discriminatorValue = 'discriminator';\n\n        $em = $this->createMock(EntityManagerInterface::class);\n        $em->expects(self::exactly(1))\n            ->method('getClassMetadata')\n            ->with($classMetadata->name)\n            ->willReturn($classMetadata);\n\n        $discriminators = HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($classMetadata, $em);\n\n        self::assertCount(1, $discriminators);\n        self::assertArrayHasKey($classMetadata->discriminatorValue, $discriminators);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Utility/IdentifierFlattenerEnumIdTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Utility;\n\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\nuse Doctrine\\Tests\\Models\\Enums\\Suit;\nuse Doctrine\\Tests\\Models\\Enums\\TypedCardEnumCompositeId;\nuse Doctrine\\Tests\\Models\\Enums\\TypedCardEnumId;\nuse Doctrine\\Tests\\Models\\Enums\\Unit;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Test the IdentifierFlattener utility class\n */\n#[CoversClass(IdentifierFlattener::class)]\nclass IdentifierFlattenerEnumIdTest extends OrmFunctionalTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->createSchemaForModels(\n            TypedCardEnumId::class,\n            TypedCardEnumCompositeId::class,\n        );\n    }\n\n    #[Group('utilities')]\n    public function testFlattenIdentifierWithEnumId(): void\n    {\n        $typedCardEnumIdEntity       = new TypedCardEnumId();\n        $typedCardEnumIdEntity->suit = Suit::Clubs;\n\n        $this->_em->persist($typedCardEnumIdEntity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $findTypedCardEnumIdEntityNotFound = $this->_em->getRepository(TypedCardEnumId::class)->find(Suit::Diamonds);\n\n        self::assertNull($findTypedCardEnumIdEntityNotFound, 'Search by non-persisted Enum ID does not work');\n\n        $findTypedCardEnumIdEntity = $this->_em->getRepository(TypedCardEnumId::class)->find(Suit::Clubs);\n\n        self::assertNotNull($findTypedCardEnumIdEntity, 'Search by Enum ID does not work');\n\n        $class = $this->_em->getClassMetadata(TypedCardEnumId::class);\n\n        $id = $class->getIdentifierValues($findTypedCardEnumIdEntity);\n\n        self::assertCount(1, $id, 'We should have 1 identifier');\n\n        self::assertEquals(Suit::Clubs, $findTypedCardEnumIdEntity->suit);\n    }\n\n    #[Group('utilities')]\n    public function testFlattenIdentifierWithCompositeEnumId(): void\n    {\n        $typedCardEnumCompositeIdEntity       = new TypedCardEnumCompositeId();\n        $typedCardEnumCompositeIdEntity->suit = Suit::Clubs;\n        $typedCardEnumCompositeIdEntity->unit = Unit::Gram;\n\n        $this->_em->persist($typedCardEnumCompositeIdEntity);\n        $this->_em->flush();\n        $this->_em->clear();\n\n        $findTypedCardEnumCompositeIdEntityNotFound = $this->_em->getRepository(TypedCardEnumCompositeId::class)->find(['suit' => Suit::Diamonds, 'unit' => Unit::Gram]);\n\n        self::assertNull($findTypedCardEnumCompositeIdEntityNotFound, 'Search by non-persisted composite Enum ID does not work');\n\n        $findTypedCardEnumCompositeIdEntity = $this->_em->getRepository(TypedCardEnumCompositeId::class)->find(['suit' => Suit::Clubs, 'unit' => Unit::Gram]);\n\n        self::assertNotNull($findTypedCardEnumCompositeIdEntity, 'Search by composite Enum ID does not work');\n\n        $class = $this->_em->getClassMetadata(TypedCardEnumCompositeId::class);\n\n        $id = $class->getIdentifierValues($findTypedCardEnumCompositeIdEntity);\n\n        self::assertCount(2, $id, 'We should have 2 identifiers');\n\n        self::assertEquals(Suit::Clubs, $findTypedCardEnumCompositeIdEntity->suit);\n        self::assertEquals(Unit::Gram, $findTypedCardEnumCompositeIdEntity->unit);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ORM/Utility/IdentifierFlattenerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\ORM\\Utility;\n\nuse Doctrine\\ORM\\Utility\\IdentifierFlattener;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Flight;\nuse Doctrine\\Tests\\Models\\VersionedOneToOne\\FirstRelatedEntity;\nuse Doctrine\\Tests\\Models\\VersionedOneToOne\\SecondRelatedEntity;\nuse Doctrine\\Tests\\OrmFunctionalTestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n/**\n * Test the IdentifierFlattener utility class\n */\n#[CoversClass(IdentifierFlattener::class)]\nclass IdentifierFlattenerTest extends OrmFunctionalTestCase\n{\n    /**\n     * Identifier flattener\n     */\n    private IdentifierFlattener $identifierFlattener;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->identifierFlattener = new IdentifierFlattener(\n            $this->_em->getUnitOfWork(),\n            $this->_em->getMetadataFactory(),\n        );\n\n        $this->createSchemaForModels(\n            FirstRelatedEntity::class,\n            SecondRelatedEntity::class,\n            Flight::class,\n            City::class,\n        );\n    }\n\n    #[Group('utilities')]\n    public function testFlattenIdentifierWithOneToOneId(): void\n    {\n        $secondRelatedEntity       = new SecondRelatedEntity();\n        $secondRelatedEntity->name = 'Bob';\n\n        $this->_em->persist($secondRelatedEntity);\n        $this->_em->flush();\n\n        $firstRelatedEntity               = new FirstRelatedEntity();\n        $firstRelatedEntity->name         = 'Fred';\n        $firstRelatedEntity->secondEntity = $secondRelatedEntity;\n\n        $this->_em->persist($firstRelatedEntity);\n        $this->_em->flush();\n\n        $firstEntity = $this->_em->getRepository(FirstRelatedEntity::class)\n            ->findOneBy(['name' => 'Fred']);\n\n        $class = $this->_em->getClassMetadata(FirstRelatedEntity::class);\n\n        $id = $class->getIdentifierValues($firstEntity);\n\n        self::assertCount(1, $id, 'We should have 1 identifier');\n\n        self::assertArrayHasKey('secondEntity', $id, 'It should be called secondEntity');\n\n        self::assertInstanceOf(\n            SecondRelatedEntity::class,\n            $id['secondEntity'],\n            'The entity should be an instance of SecondRelatedEntity',\n        );\n\n        $flatIds = $this->identifierFlattener->flattenIdentifier($class, $id);\n\n        self::assertCount(1, $flatIds, 'We should have 1 flattened id');\n\n        self::assertArrayHasKey('secondEntity', $flatIds, 'It should be called secondEntity');\n\n        self::assertEquals($id['secondEntity']->id, $flatIds['secondEntity']);\n    }\n\n    #[Group('utilities')]\n    public function testFlattenIdentifierWithMutlipleIds(): void\n    {\n        $leeds  = new City('Leeds');\n        $london = new City('London');\n\n        $this->_em->persist($leeds);\n        $this->_em->persist($london);\n        $this->_em->flush();\n\n        $flight = new Flight($leeds, $london);\n\n        $this->_em->persist($flight);\n        $this->_em->flush();\n\n        $class = $this->_em->getClassMetadata(Flight::class);\n        $id    = $class->getIdentifierValues($flight);\n\n        self::assertCount(2, $id);\n\n        self::assertArrayHasKey('leavingFrom', $id);\n        self::assertArrayHasKey('goingTo', $id);\n\n        self::assertEquals($leeds, $id['leavingFrom']);\n        self::assertEquals($london, $id['goingTo']);\n\n        $flatIds = $this->identifierFlattener->flattenIdentifier($class, $id);\n\n        self::assertCount(2, $flatIds);\n\n        self::assertArrayHasKey('leavingFrom', $flatIds);\n        self::assertArrayHasKey('goingTo', $flatIds);\n\n        self::assertEquals($id['leavingFrom']->getId(), $flatIds['leavingFrom']);\n        self::assertEquals($id['goingTo']->getId(), $flatIds['goingTo']);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/OrmFunctionalTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests;\n\nuse Doctrine\\Common\\EventManager;\nuse Doctrine\\DBAL\\Exception\\DatabaseObjectNotFoundException;\nuse Doctrine\\DBAL\\Platforms\\MySQLPlatform;\nuse Doctrine\\DBAL\\Platforms\\OraclePlatform;\nuse Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform;\nuse Doctrine\\DBAL\\Schema\\AbstractSchemaManager;\nuse Doctrine\\DBAL\\Schema\\NamedObject;\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\DBAL\\Schema\\Table;\nuse Doctrine\\DBAL\\Types\\Type;\nuse Doctrine\\ORM\\Cache\\CacheConfiguration;\nuse Doctrine\\ORM\\Cache\\DefaultCacheFactory;\nuse Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Doctrine\\ORM\\Exception\\ORMException;\nuse Doctrine\\ORM\\Mapping\\ClassMetadata;\nuse Doctrine\\ORM\\Tools\\DebugUnitOfWorkListener;\nuse Doctrine\\ORM\\Tools\\SchemaTool;\nuse Doctrine\\ORM\\Tools\\ToolsException;\nuse Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver;\nuse Doctrine\\Tests\\DbalExtensions\\Connection;\nuse Doctrine\\Tests\\DbalExtensions\\QueryLog;\nuse Doctrine\\Tests\\DbalTypes\\Rot13Type;\nuse Doctrine\\Tests\\EventListener\\CacheMetadataListener;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Models\\Cache\\Action;\nuse Doctrine\\Tests\\Models\\Cache\\Address;\nuse Doctrine\\Tests\\Models\\Cache\\Attraction;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionContactInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionInfo;\nuse Doctrine\\Tests\\Models\\Cache\\AttractionLocationInfo;\nuse Doctrine\\Tests\\Models\\Cache\\Bar;\nuse Doctrine\\Tests\\Models\\Cache\\Beach;\nuse Doctrine\\Tests\\Models\\Cache\\City;\nuse Doctrine\\Tests\\Models\\Cache\\Client;\nuse Doctrine\\Tests\\Models\\Cache\\ComplexAction;\nuse Doctrine\\Tests\\Models\\Cache\\Country;\nuse Doctrine\\Tests\\Models\\Cache\\Flight;\nuse Doctrine\\Tests\\Models\\Cache\\Login;\nuse Doctrine\\Tests\\Models\\Cache\\Person;\nuse Doctrine\\Tests\\Models\\Cache\\Restaurant;\nuse Doctrine\\Tests\\Models\\Cache\\State;\nuse Doctrine\\Tests\\Models\\Cache\\Token;\nuse Doctrine\\Tests\\Models\\Cache\\Travel;\nuse Doctrine\\Tests\\Models\\Cache\\Traveler;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfile;\nuse Doctrine\\Tests\\Models\\Cache\\TravelerProfileInfo;\nuse Doctrine\\Tests\\Models\\CMS\\CmsAddress;\nuse Doctrine\\Tests\\Models\\CMS\\CmsArticle;\nuse Doctrine\\Tests\\Models\\CMS\\CmsComment;\nuse Doctrine\\Tests\\Models\\CMS\\CmsEmail;\nuse Doctrine\\Tests\\Models\\CMS\\CmsGroup;\nuse Doctrine\\Tests\\Models\\CMS\\CmsPhonenumber;\nuse Doctrine\\Tests\\Models\\CMS\\CmsTag;\nuse Doctrine\\Tests\\Models\\CMS\\CmsUser;\nuse Doctrine\\Tests\\Models\\Company\\CompanyAuction;\nuse Doctrine\\Tests\\Models\\Company\\CompanyCar;\nuse Doctrine\\Tests\\Models\\Company\\CompanyContract;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEmployee;\nuse Doctrine\\Tests\\Models\\Company\\CompanyEvent;\nuse Doctrine\\Tests\\Models\\Company\\CompanyManager;\nuse Doctrine\\Tests\\Models\\Company\\CompanyOrganization;\nuse Doctrine\\Tests\\Models\\Company\\CompanyPerson;\nuse Doctrine\\Tests\\Models\\Company\\CompanyRaffle;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedChildClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\JoinedRootClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\SingleChildClass;\nuse Doctrine\\Tests\\Models\\CompositeKeyInheritance\\SingleRootClass;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeChild;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomIdObjectTypeParent;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeChild;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeParent;\nuse Doctrine\\Tests\\Models\\CustomType\\CustomTypeUpperCase;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ApproveChanges;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Article;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117ArticleDetails;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Editor;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Link;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Reference;\nuse Doctrine\\Tests\\Models\\DDC117\\DDC117Translation;\nuse Doctrine\\Tests\\Models\\DDC2504\\DDC2504ChildClass;\nuse Doctrine\\Tests\\Models\\DDC2504\\DDC2504OtherClass;\nuse Doctrine\\Tests\\Models\\DDC2504\\DDC2504RootClass;\nuse Doctrine\\Tests\\Models\\DDC3346\\DDC3346Article;\nuse Doctrine\\Tests\\Models\\DDC3346\\DDC3346Author;\nuse Doctrine\\Tests\\Models\\DDC3699\\DDC3699Child;\nuse Doctrine\\Tests\\Models\\DDC3699\\DDC3699Parent;\nuse Doctrine\\Tests\\Models\\DDC3699\\DDC3699RelationMany;\nuse Doctrine\\Tests\\Models\\DDC3699\\DDC3699RelationOne;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\AbstractContentItem;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\Directory;\nuse Doctrine\\Tests\\Models\\DirectoryTree\\File;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCategory;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceCustomer;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceFeature;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct;\nuse Doctrine\\Tests\\Models\\ECommerce\\ECommerceShipping;\nuse Doctrine\\Tests\\Models\\Generic\\BooleanModel;\nuse Doctrine\\Tests\\Models\\Generic\\DateTimeModel;\nuse Doctrine\\Tests\\Models\\Generic\\DecimalModel;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1;\nuse Doctrine\\Tests\\Models\\GeoNames\\Admin1AlternateName;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Employee;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Manager;\nuse Doctrine\\Tests\\Models\\Issue5989\\Issue5989Person;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyArticle;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyCar;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUser;\nuse Doctrine\\Tests\\Models\\Legacy\\LegacyUserReference;\nuse Doctrine\\Tests\\Models\\Navigation\\NavCountry;\nuse Doctrine\\Tests\\Models\\Navigation\\NavPhotos;\nuse Doctrine\\Tests\\Models\\Navigation\\NavPointOfInterest;\nuse Doctrine\\Tests\\Models\\Navigation\\NavTour;\nuse Doctrine\\Tests\\Models\\Navigation\\NavUser;\nuse Doctrine\\Tests\\Models\\Pagination\\Company;\nuse Doctrine\\Tests\\Models\\Pagination\\Department;\nuse Doctrine\\Tests\\Models\\Pagination\\Logo;\nuse Doctrine\\Tests\\Models\\Pagination\\User1;\nuse Doctrine\\Tests\\Models\\Quote\\FullAddress;\nuse Doctrine\\Tests\\Models\\Quote\\Group;\nuse Doctrine\\Tests\\Models\\Quote\\NumericEntity;\nuse Doctrine\\Tests\\Models\\Quote\\Phone;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLeg;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingLocation;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingRoute;\nuse Doctrine\\Tests\\Models\\Routing\\RoutingRouteBooking;\nuse Doctrine\\Tests\\Models\\StockExchange\\Bond;\nuse Doctrine\\Tests\\Models\\StockExchange\\Market;\nuse Doctrine\\Tests\\Models\\StockExchange\\Stock;\nuse Doctrine\\Tests\\Models\\Taxi\\Car;\nuse Doctrine\\Tests\\Models\\Taxi\\Driver;\nuse Doctrine\\Tests\\Models\\Taxi\\PaidRide;\nuse Doctrine\\Tests\\Models\\Taxi\\Ride;\nuse Doctrine\\Tests\\Models\\Tweet\\Tweet;\nuse Doctrine\\Tests\\Models\\Tweet\\User;\nuse Doctrine\\Tests\\Models\\Tweet\\UserList;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\AuxiliaryEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\InversedOneToOneEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToManyExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningManyToOneExtraLazyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneCompositeIdEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneCompositeIdForeignKeyEntity;\nuse Doctrine\\Tests\\Models\\ValueConversionType\\OwningOneToOneEntity;\nuse Doctrine\\Tests\\Models\\VersionedManyToOne\\Article;\nuse Doctrine\\Tests\\Models\\VersionedManyToOne\\Category;\nuse Exception;\nuse PHPUnit\\Framework\\AssertionFailedError;\nuse PHPUnit\\Framework\\Constraint\\Count;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse RuntimeException;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\nuse Throwable;\n\nuse function array_map;\nuse function array_pop;\nuse function array_reverse;\nuse function array_slice;\nuse function assert;\nuse function explode;\nuse function get_debug_type;\nuse function getenv;\nuse function implode;\nuse function is_object;\nuse function method_exists;\nuse function sprintf;\nuse function str_contains;\nuse function strtolower;\nuse function var_export;\n\nuse const PHP_EOL;\nuse const PHP_VERSION_ID;\n\n/**\n * Base testcase class for all functional ORM testcases.\n */\nabstract class OrmFunctionalTestCase extends OrmTestCase\n{\n    /**\n     * The metadata cache shared between all functional tests.\n     *\n     * @var CacheItemPoolInterface|null\n     */\n    private static $metadataCache = null;\n\n    /**\n     * The query cache shared between all functional tests.\n     *\n     * @var CacheItemPoolInterface|null\n     */\n    protected static $queryCache = null;\n\n    /**\n     * Shared connection when a TestCase is run alone (outside of its functional suite).\n     *\n     * @var DbalExtensions\\Connection|null\n     */\n    protected static $sharedConn;\n\n    /** @var EntityManagerInterface */\n    protected $_em;\n\n    /** @var SchemaTool */\n    protected $_schemaTool;\n\n    /**\n     * The names of the model sets used in this testcase.\n     *\n     * @var array\n     */\n    protected $_usedModelSets = [];\n\n    /**\n     * To be configured by the test that uses result set cache\n     *\n     * @var CacheItemPoolInterface|null\n     */\n    protected $resultCache;\n\n    /**\n     * Whether the database schema has already been created.\n     *\n     * @var array\n     */\n    protected static $tablesCreated = [];\n\n    /**\n     * Array of entity class name to their tables that were created.\n     *\n     * @var array\n     */\n    protected static $_entityTablesCreated = [];\n\n    /**\n     * List of model sets and their classes.\n     *\n     * @var array\n     */\n    protected static $modelSets = [\n        'cms' => [\n            CmsUser::class,\n            CmsPhonenumber::class,\n            CmsAddress::class,\n            CmsEmail::class,\n            CmsGroup::class,\n            CmsTag::class,\n            CmsArticle::class,\n            CmsComment::class,\n        ],\n        'company' => [\n            CompanyPerson::class,\n            CompanyEmployee::class,\n            CompanyManager::class,\n            CompanyOrganization::class,\n            CompanyEvent::class,\n            CompanyAuction::class,\n            CompanyRaffle::class,\n            CompanyCar::class,\n            CompanyContract::class,\n        ],\n        'ecommerce' => [\n            ECommerceCart::class,\n            ECommerceCustomer::class,\n            ECommerceProduct::class,\n            ECommerceShipping::class,\n            ECommerceFeature::class,\n            ECommerceCategory::class,\n        ],\n        'generic' => [\n            BooleanModel::class,\n            DateTimeModel::class,\n            DecimalModel::class,\n        ],\n        'routing' => [\n            RoutingLeg::class,\n            RoutingLocation::class,\n            RoutingRoute::class,\n            RoutingRouteBooking::class,\n        ],\n        'navigation' => [\n            NavUser::class,\n            NavCountry::class,\n            NavPhotos::class,\n            NavTour::class,\n            NavPointOfInterest::class,\n        ],\n        'directorytree' => [\n            AbstractContentItem::class,\n            File::class,\n            Directory::class,\n        ],\n        'ddc117' => [\n            DDC117Article::class,\n            DDC117Reference::class,\n            DDC117Translation::class,\n            DDC117ArticleDetails::class,\n            DDC117ApproveChanges::class,\n            DDC117Editor::class,\n            DDC117Link::class,\n        ],\n        'ddc3699' => [\n            DDC3699Parent::class,\n            DDC3699RelationOne::class,\n            DDC3699RelationMany::class,\n            DDC3699Child::class,\n        ],\n        'stockexchange' => [\n            Bond::class,\n            Stock::class,\n            Market::class,\n        ],\n        'legacy' => [\n            LegacyUser::class,\n            LegacyUserReference::class,\n            LegacyArticle::class,\n            LegacyCar::class,\n        ],\n        'customtype' => [\n            CustomTypeChild::class,\n            CustomTypeParent::class,\n            CustomTypeUpperCase::class,\n        ],\n        'compositekeyinheritance' => [\n            JoinedRootClass::class,\n            JoinedChildClass::class,\n            SingleRootClass::class,\n            SingleChildClass::class,\n        ],\n        'compositekeyrelations' => [\n            Models\\CompositeKeyRelations\\InvoiceClass::class,\n            Models\\CompositeKeyRelations\\CustomerClass::class,\n        ],\n        'taxi' => [\n            PaidRide::class,\n            Ride::class,\n            Car::class,\n            Driver::class,\n        ],\n        'cache' => [\n            Country::class,\n            State::class,\n            City::class,\n            Traveler::class,\n            TravelerProfileInfo::class,\n            TravelerProfile::class,\n            Travel::class,\n            Attraction::class,\n            Restaurant::class,\n            Beach::class,\n            Bar::class,\n            Flight::class,\n            Token::class,\n            Login::class,\n            Client::class,\n            Person::class,\n            Address::class,\n            Action::class,\n            ComplexAction::class,\n            AttractionInfo::class,\n            AttractionContactInfo::class,\n            AttractionLocationInfo::class,\n        ],\n        'tweet' => [\n            User::class,\n            Tweet::class,\n            UserList::class,\n        ],\n        'ddc2504' => [\n            DDC2504RootClass::class,\n            DDC2504ChildClass::class,\n            DDC2504OtherClass::class,\n        ],\n        'ddc3346' => [\n            DDC3346Author::class,\n            DDC3346Article::class,\n        ],\n        'quote' => [\n            Models\\Quote\\Address::class,\n            Models\\Quote\\City::class,\n            FullAddress::class,\n            Group::class,\n            NumericEntity::class,\n            Phone::class,\n            Models\\Quote\\User::class,\n        ],\n        'vct_onetoone' => [\n            InversedOneToOneEntity::class,\n            OwningOneToOneEntity::class,\n        ],\n        'vct_onetoone_compositeid' => [\n            InversedOneToOneCompositeIdEntity::class,\n            OwningOneToOneCompositeIdEntity::class,\n        ],\n        'vct_onetoone_compositeid_foreignkey' => [\n            AuxiliaryEntity::class,\n            InversedOneToOneCompositeIdForeignKeyEntity::class,\n            OwningOneToOneCompositeIdForeignKeyEntity::class,\n        ],\n        'vct_onetomany' => [\n            InversedOneToManyEntity::class,\n            OwningManyToOneEntity::class,\n        ],\n        'vct_onetomany_compositeid' => [\n            InversedOneToManyCompositeIdEntity::class,\n            OwningManyToOneCompositeIdEntity::class,\n        ],\n        'vct_onetomany_compositeid_foreignkey' => [\n            AuxiliaryEntity::class,\n            InversedOneToManyCompositeIdForeignKeyEntity::class,\n            OwningManyToOneCompositeIdForeignKeyEntity::class,\n        ],\n        'vct_onetomany_extralazy' => [\n            InversedOneToManyExtraLazyEntity::class,\n            OwningManyToOneExtraLazyEntity::class,\n        ],\n        'vct_manytomany' => [\n            InversedManyToManyEntity::class,\n            OwningManyToManyEntity::class,\n        ],\n        'vct_manytomany_compositeid' => [\n            InversedManyToManyCompositeIdEntity::class,\n            OwningManyToManyCompositeIdEntity::class,\n        ],\n        'vct_manytomany_compositeid_foreignkey' => [\n            AuxiliaryEntity::class,\n            InversedManyToManyCompositeIdForeignKeyEntity::class,\n            OwningManyToManyCompositeIdForeignKeyEntity::class,\n        ],\n        'vct_manytomany_extralazy' => [\n            InversedManyToManyExtraLazyEntity::class,\n            OwningManyToManyExtraLazyEntity::class,\n        ],\n        'geonames' => [\n            Models\\GeoNames\\Country::class,\n            Admin1::class,\n            Admin1AlternateName::class,\n            Models\\GeoNames\\City::class,\n        ],\n        'custom_id_object_type' => [\n            CustomIdObjectTypeParent::class,\n            CustomIdObjectTypeChild::class,\n        ],\n        'pagination' => [\n            Company::class,\n            Logo::class,\n            Department::class,\n            Models\\Pagination\\User::class,\n            User1::class,\n        ],\n        'versioned_many_to_one' => [\n            Category::class,\n            Article::class,\n        ],\n        'issue5989' => [\n            Issue5989Person::class,\n            Issue5989Employee::class,\n            Issue5989Manager::class,\n        ],\n        'issue9300' => [\n            Models\\Issue9300\\Issue9300Child::class,\n            Models\\Issue9300\\Issue9300Parent::class,\n        ],\n    ];\n\n    /** @param class-string ...$models */\n    final protected function createSchemaForModels(string ...$models): void\n    {\n        try {\n            $this->_schemaTool->createSchema($this->getMetadataForModels($models));\n        } catch (ToolsException) {\n        }\n    }\n\n    /**\n     * @param class-string ...$models\n     *\n     * @return string[]\n     */\n    final protected function getUpdateSchemaSqlForModels(string ...$models): array\n    {\n        return $this->_schemaTool->getUpdateSchemaSql($this->getMetadataForModels($models));\n    }\n\n    /** @param class-string ...$models */\n    final protected function getSchemaForModels(string ...$models): Schema\n    {\n        return $this->_schemaTool->getSchemaFromMetadata($this->getMetadataForModels($models));\n    }\n\n    /**\n     * @param class-string[] $models\n     *\n     * @return ClassMetadata[]\n     */\n    private function getMetadataForModels(array $models): array\n    {\n        return array_map(\n            function (string $className): ClassMetadata {\n                return $this->_em->getClassMetadata($className);\n            },\n            $models,\n        );\n    }\n\n    protected function useModelSet(string $setName): void\n    {\n        $this->_usedModelSets[$setName] = true;\n    }\n\n    /**\n     * Sweeps the database tables and clears the EntityManager.\n     */\n    protected function tearDown(): void\n    {\n        $conn = static::$sharedConn;\n\n        // In case test is skipped, tearDown is called, but no setup may have run\n        if (! $conn || ! isset($this->_em)) {\n            return;\n        }\n\n        $platform = $conn->getDatabasePlatform();\n\n        if ($this->isQueryLogAvailable()) {\n            $this->getQueryLog()->reset();\n        }\n\n        if (isset($this->_usedModelSets['cms'])) {\n            $conn->executeStatement('DELETE FROM cms_users_groups');\n            $conn->executeStatement('DELETE FROM cms_groups');\n            $conn->executeStatement('DELETE FROM cms_users_tags');\n            $conn->executeStatement('DELETE FROM cms_tags');\n            $conn->executeStatement('DELETE FROM cms_addresses');\n            $conn->executeStatement('DELETE FROM cms_phonenumbers');\n            $conn->executeStatement('DELETE FROM cms_comments');\n            $conn->executeStatement('DELETE FROM cms_articles');\n            $conn->executeStatement('DELETE FROM cms_users');\n            $conn->executeStatement('DELETE FROM cms_emails');\n        }\n\n        if (isset($this->_usedModelSets['ecommerce'])) {\n            $conn->executeStatement('DELETE FROM ecommerce_carts_products');\n            $conn->executeStatement('DELETE FROM ecommerce_products_categories');\n            $conn->executeStatement('DELETE FROM ecommerce_products_related');\n            $conn->executeStatement('DELETE FROM ecommerce_carts');\n            $conn->executeStatement('DELETE FROM ecommerce_customers WHERE mentor_id IS NOT NULL');\n            $conn->executeStatement('DELETE FROM ecommerce_customers');\n            $conn->executeStatement('DELETE FROM ecommerce_features');\n            $conn->executeStatement('DELETE FROM ecommerce_products');\n            $conn->executeStatement('DELETE FROM ecommerce_shippings');\n            $conn->executeStatement('UPDATE ecommerce_categories SET parent_id = NULL');\n            $conn->executeStatement('DELETE FROM ecommerce_categories');\n        }\n\n        if (isset($this->_usedModelSets['company'])) {\n            $conn->executeStatement('DELETE FROM company_contract_employees');\n            $conn->executeStatement('DELETE FROM company_contract_managers');\n            $conn->executeStatement('DELETE FROM company_contracts');\n            $conn->executeStatement('DELETE FROM company_persons_friends');\n            $conn->executeStatement('DELETE FROM company_managers');\n            $conn->executeStatement('DELETE FROM company_employees');\n            $conn->executeStatement('UPDATE company_persons SET spouse_id = NULL');\n            $conn->executeStatement('DELETE FROM company_persons');\n            $conn->executeStatement('DELETE FROM company_raffles');\n            $conn->executeStatement('DELETE FROM company_auctions');\n            $conn->executeStatement('UPDATE company_organizations SET main_event_id = NULL');\n            $conn->executeStatement('DELETE FROM company_events');\n            $conn->executeStatement('DELETE FROM company_organizations');\n        }\n\n        if (isset($this->_usedModelSets['generic'])) {\n            $conn->executeStatement('DELETE FROM boolean_model');\n            $conn->executeStatement('DELETE FROM date_time_model');\n            $conn->executeStatement('DELETE FROM decimal_model');\n        }\n\n        if (isset($this->_usedModelSets['routing'])) {\n            $conn->executeStatement('DELETE FROM RoutingRouteLegs');\n            $conn->executeStatement('DELETE FROM RoutingRouteBooking');\n            $conn->executeStatement('DELETE FROM RoutingRoute');\n            $conn->executeStatement('DELETE FROM RoutingLeg');\n            $conn->executeStatement('DELETE FROM RoutingLocation');\n        }\n\n        if (isset($this->_usedModelSets['navigation'])) {\n            $conn->executeStatement('DELETE FROM navigation_tour_pois');\n            $conn->executeStatement('DELETE FROM navigation_photos');\n            $conn->executeStatement('DELETE FROM navigation_pois');\n            $conn->executeStatement('DELETE FROM navigation_tours');\n            $conn->executeStatement('DELETE FROM navigation_countries');\n        }\n\n        if (isset($this->_usedModelSets['directorytree'])) {\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('file'));\n            // MySQL doesn't know deferred deletions therefore only executing the second query gives errors.\n            $conn->executeStatement('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL');\n            $conn->executeStatement('DELETE FROM Directory');\n        }\n\n        if (isset($this->_usedModelSets['ddc117'])) {\n            $conn->executeStatement('DELETE FROM ddc117editor_ddc117translation');\n            $conn->executeStatement('DELETE FROM DDC117Editor');\n            $conn->executeStatement('DELETE FROM DDC117ApproveChanges');\n            $conn->executeStatement('DELETE FROM DDC117Link');\n            $conn->executeStatement('DELETE FROM DDC117Reference');\n            $conn->executeStatement('DELETE FROM DDC117ArticleDetails');\n            $conn->executeStatement('DELETE FROM DDC117Translation');\n            $conn->executeStatement('DELETE FROM DDC117Article');\n        }\n\n        if (isset($this->_usedModelSets['stockexchange'])) {\n            $conn->executeStatement('DELETE FROM exchange_bonds_stocks');\n            $conn->executeStatement('DELETE FROM exchange_bonds');\n            $conn->executeStatement('DELETE FROM exchange_stocks');\n            $conn->executeStatement('DELETE FROM exchange_markets');\n        }\n\n        if (isset($this->_usedModelSets['legacy'])) {\n            $conn->executeStatement('DELETE FROM legacy_users_cars');\n            $conn->executeStatement('DELETE FROM legacy_users_reference');\n            $conn->executeStatement('DELETE FROM legacy_articles');\n            $conn->executeStatement('DELETE FROM legacy_cars');\n            $conn->executeStatement('DELETE FROM legacy_users');\n        }\n\n        if (isset($this->_usedModelSets['customtype'])) {\n            $conn->executeStatement('DELETE FROM customtype_parent_friends');\n            $conn->executeStatement('DELETE FROM customtype_parents');\n            $conn->executeStatement('DELETE FROM customtype_children');\n            $conn->executeStatement('DELETE FROM customtype_uppercases');\n        }\n\n        if (isset($this->_usedModelSets['compositekeyinheritance'])) {\n            $conn->executeStatement('DELETE FROM JoinedChildClass');\n            $conn->executeStatement('DELETE FROM JoinedRootClass');\n            $conn->executeStatement('DELETE FROM SingleRootClass');\n        }\n\n        if (isset($this->_usedModelSets['taxi'])) {\n            $conn->executeStatement('DELETE FROM taxi_paid_ride');\n            $conn->executeStatement('DELETE FROM taxi_ride');\n            $conn->executeStatement('DELETE FROM taxi_car');\n            $conn->executeStatement('DELETE FROM taxi_driver');\n        }\n\n        if (isset($this->_usedModelSets['tweet'])) {\n            $conn->executeStatement('DELETE FROM tweet_tweet');\n            $conn->executeStatement('DELETE FROM tweet_user_list');\n            $conn->executeStatement('DELETE FROM tweet_user');\n        }\n\n        if (isset($this->_usedModelSets['cache'])) {\n            $conn->executeStatement('DELETE FROM cache_attraction_location_info');\n            $conn->executeStatement('DELETE FROM cache_attraction_contact_info');\n            $conn->executeStatement('DELETE FROM cache_attraction_info');\n            $conn->executeStatement('DELETE FROM cache_visited_cities');\n            $conn->executeStatement('DELETE FROM cache_flight');\n            $conn->executeStatement('DELETE FROM cache_attraction');\n            $conn->executeStatement('DELETE FROM cache_travel');\n            $conn->executeStatement('DELETE FROM cache_traveler');\n            $conn->executeStatement('DELETE FROM cache_traveler_profile_info');\n            $conn->executeStatement('DELETE FROM cache_traveler_profile');\n            $conn->executeStatement('DELETE FROM cache_city');\n            $conn->executeStatement('DELETE FROM cache_state');\n            $conn->executeStatement('DELETE FROM cache_country');\n            $conn->executeStatement('DELETE FROM cache_login');\n            $conn->executeStatement('DELETE FROM cache_token');\n            $conn->executeStatement('DELETE FROM cache_complex_action');\n            $conn->executeStatement('DELETE FROM cache_action');\n            $conn->executeStatement('DELETE FROM cache_client');\n        }\n\n        if (isset($this->_usedModelSets['ddc3346'])) {\n            $conn->executeStatement('DELETE FROM ddc3346_articles');\n            $conn->executeStatement('DELETE FROM ddc3346_users');\n        }\n\n        if (isset($this->_usedModelSets['ornemental_orphan_removal'])) {\n            $conn->executeStatement('DELETE FROM ornemental_orphan_removal_person');\n            $conn->executeStatement('DELETE FROM ornemental_orphan_removal_phone_number');\n        }\n\n        if (isset($this->_usedModelSets['quote'])) {\n            $conn->executeStatement(\n                sprintf(\n                    'UPDATE %s SET %s = NULL',\n                    $platform->quoteSingleIdentifier('quote-address'),\n                    $platform->quoteSingleIdentifier('user-id'),\n                ),\n            );\n\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-users-groups'));\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-group'));\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-phone'));\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-user'));\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-address'));\n            $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-city'));\n        }\n\n        if (isset($this->_usedModelSets['vct_onetoone'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_onetoone');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetoone');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetoone_compositeid'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_onetoone_compositeid');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetoone_compositeid');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetoone_compositeid_foreignkey'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_onetoone_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetoone_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_auxiliary');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetomany'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_manytoone');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetomany');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetomany_compositeid'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_manytoone_compositeid');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetomany_compositeid');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetomany_compositeid_foreignkey'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_manytoone_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetomany_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_auxiliary');\n        }\n\n        if (isset($this->_usedModelSets['vct_onetomany_extralazy'])) {\n            $conn->executeStatement('DELETE FROM vct_owning_manytoone_extralazy');\n            $conn->executeStatement('DELETE FROM vct_inversed_onetomany_extralazy');\n        }\n\n        if (isset($this->_usedModelSets['vct_manytomany'])) {\n            $conn->executeStatement('DELETE FROM vct_xref_manytomany');\n            $conn->executeStatement('DELETE FROM vct_owning_manytomany');\n            $conn->executeStatement('DELETE FROM vct_inversed_manytomany');\n        }\n\n        if (isset($this->_usedModelSets['vct_manytomany_compositeid'])) {\n            $conn->executeStatement('DELETE FROM vct_xref_manytomany_compositeid');\n            $conn->executeStatement('DELETE FROM vct_owning_manytomany_compositeid');\n            $conn->executeStatement('DELETE FROM vct_inversed_manytomany_compositeid');\n        }\n\n        if (isset($this->_usedModelSets['vct_manytomany_compositeid_foreignkey'])) {\n            $conn->executeStatement('DELETE FROM vct_xref_manytomany_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_owning_manytomany_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_inversed_manytomany_compositeid_foreignkey');\n            $conn->executeStatement('DELETE FROM vct_auxiliary');\n        }\n\n        if (isset($this->_usedModelSets['vct_manytomany_extralazy'])) {\n            $conn->executeStatement('DELETE FROM vct_xref_manytomany_extralazy');\n            $conn->executeStatement('DELETE FROM vct_owning_manytomany_extralazy');\n            $conn->executeStatement('DELETE FROM vct_inversed_manytomany_extralazy');\n        }\n\n        if (isset($this->_usedModelSets['geonames'])) {\n            $conn->executeStatement('DELETE FROM geonames_admin1_alternate_name');\n            $conn->executeStatement('DELETE FROM geonames_admin1');\n            $conn->executeStatement('DELETE FROM geonames_city');\n            $conn->executeStatement('DELETE FROM geonames_country');\n        }\n\n        if (isset($this->_usedModelSets['custom_id_object_type'])) {\n            $conn->executeStatement('DELETE FROM custom_id_type_child');\n            $conn->executeStatement('DELETE FROM custom_id_type_parent');\n        }\n\n        if (isset($this->_usedModelSets['pagination'])) {\n            $conn->executeStatement('DELETE FROM pagination_logo');\n            $conn->executeStatement('DELETE FROM pagination_department');\n            $conn->executeStatement('DELETE FROM pagination_company');\n            $conn->executeStatement('DELETE FROM pagination_user');\n        }\n\n        if (isset($this->_usedModelSets['versioned_many_to_one'])) {\n            $conn->executeStatement('DELETE FROM versioned_many_to_one_article');\n            $conn->executeStatement('DELETE FROM versioned_many_to_one_category');\n        }\n\n        if (isset($this->_usedModelSets['issue5989'])) {\n            $conn->executeStatement('DELETE FROM issue5989_persons');\n            $conn->executeStatement('DELETE FROM issue5989_employees');\n            $conn->executeStatement('DELETE FROM issue5989_managers');\n        }\n\n        $this->_em->clear();\n    }\n\n    /** @throws RuntimeException */\n    protected function setUpEntitySchema(array $classNames): void\n    {\n        if ($this->_em === null) {\n            throw new RuntimeException('EntityManager not set, you have to call parent::setUp() before invoking this method.');\n        }\n\n        $classes = [];\n        foreach ($classNames as $className) {\n            if (! isset(static::$_entityTablesCreated[$className])) {\n                static::$_entityTablesCreated[$className] = true;\n                $classes[]                                = $this->_em->getClassMetadata($className);\n            }\n        }\n\n        if ($classes) {\n            $this->_schemaTool->createSchema($classes);\n        }\n    }\n\n    /**\n     * Creates a connection to the test database, if there is none yet, and\n     * creates the necessary tables.\n     */\n    protected function setUp(): void\n    {\n        $this->setUpDBALTypes();\n\n        if (! isset(static::$sharedConn)) {\n            static::$sharedConn = TestUtil::getConnection();\n        }\n\n        if (isset($GLOBALS['DOCTRINE_MARK_SQL_LOGS'])) {\n            $platform = static::$sharedConn->getDatabasePlatform();\n            if (\n                $platform instanceof MySQLPlatform\n                || $platform instanceof PostgreSQLPlatform\n            ) {\n                static::$sharedConn->executeQuery('SELECT 1 /*' . static::class . '*/');\n            } elseif ($platform instanceof OraclePlatform) {\n                static::$sharedConn->executeQuery('SELECT 1 /*' . static::class . '*/ FROM dual');\n            }\n        }\n\n        if (! $this->_em) {\n            $this->_em         = $this->getEntityManager();\n            $this->_schemaTool = new SchemaTool($this->_em);\n        }\n\n        $classes = [];\n\n        foreach ($this->_usedModelSets as $setName => $bool) {\n            if (! isset(static::$tablesCreated[$setName])) {\n                foreach (static::$modelSets[$setName] as $className) {\n                    $classes[] = $this->_em->getClassMetadata($className);\n                }\n\n                static::$tablesCreated[$setName] = true;\n            }\n        }\n\n        if ($classes) {\n            $this->_schemaTool->createSchema($classes);\n        }\n\n        $this->getQueryLog()->enable();\n    }\n\n    /**\n     * Gets an EntityManager for testing purposes.\n     *\n     * @throws ORMException\n     */\n    protected function getEntityManager(\n        Connection|null $connection = null,\n        MappingDriver|null $mappingDriver = null,\n    ): EntityManagerInterface {\n        // NOTE: Functional tests use their own shared metadata cache, because\n        // the actual database platform used during execution has effect on some\n        // metadata mapping behaviors (like the choice of the ID generation).\n        if (self::$metadataCache === null) {\n            self::$metadataCache = new ArrayAdapter();\n        }\n\n        if (self::$queryCache === null) {\n            self::$queryCache = new ArrayAdapter();\n        }\n\n        //FIXME: two different configs! $conn and the created entity manager have\n        // different configs.\n        $config = new Configuration();\n        TestUtil::configureProxies($config);\n        $config->setMetadataCache(self::$metadataCache);\n        $config->setQueryCache(self::$queryCache);\n\n        if ($this->resultCache !== null) {\n            $config->setResultCache($this->resultCache);\n        }\n\n        $enableSecondLevelCache = getenv('ENABLE_SECOND_LEVEL_CACHE');\n\n        if ($this->isSecondLevelCacheEnabled || $enableSecondLevelCache) {\n            $cacheConfig = new CacheConfiguration();\n            $factory     = new DefaultCacheFactory(\n                $cacheConfig->getRegionsConfiguration(),\n                $this->getSharedSecondLevelCache(),\n            );\n\n            $this->secondLevelCacheFactory = $factory;\n\n            if ($this->isSecondLevelCacheLogEnabled) {\n                $this->secondLevelCacheLogger = new StatisticsCacheLogger();\n                $cacheConfig->setCacheLogger($this->secondLevelCacheLogger);\n            }\n\n            $cacheConfig->setCacheFactory($factory);\n            $config->setSecondLevelCacheEnabled(true);\n            $config->setSecondLevelCacheConfiguration($cacheConfig);\n\n            $this->isSecondLevelCacheEnabled = true;\n        }\n\n        $enableNativeLazyObjects = getenv('ENABLE_NATIVE_LAZY_OBJECTS');\n\n        if ($enableNativeLazyObjects === false) {\n            // If the environment variable is not set, we default to true.\n            // This is OK because environment variables are always strings, and\n            // we are comparing it to a boolean.\n            $enableNativeLazyObjects = true;\n        }\n\n        if (PHP_VERSION_ID >= 80400 && $enableNativeLazyObjects) {\n            $config->enableNativeLazyObjects(true);\n        }\n\n        $mappingDriver ??= AttributeDriverFactory::createAttributeDriver([\n            __DIR__ . '/Models/Cache',\n            __DIR__ . '/Models/GeoNames',\n        ]);\n\n        $config->setMetadataDriverImpl($mappingDriver);\n\n        $conn = $connection ?: static::$sharedConn;\n        assert($conn !== null);\n        $conn->queryLog->reset();\n\n        // get rid of more global state\n        if (method_exists($conn, 'getEventManager')) {\n            $evm = $conn->getEventManager();\n            foreach ($evm->getAllListeners() as $event => $listeners) {\n                foreach ($listeners as $listener) {\n                    $evm->removeEventListener([$event], $listener);\n                }\n            }\n        } else {\n            $evm = new EventManager();\n        }\n\n        if ($enableSecondLevelCache) {\n            $evm->addEventListener('loadClassMetadata', new CacheMetadataListener());\n        }\n\n        if (isset($GLOBALS['db_event_subscribers'])) {\n            foreach (explode(',', $GLOBALS['db_event_subscribers']) as $subscriberClass) {\n                $subscriberInstance = new $subscriberClass();\n                $evm->addEventSubscriber($subscriberInstance);\n            }\n        }\n\n        if (isset($GLOBALS['debug_uow_listener'])) {\n            $evm->addEventListener(['onFlush'], new DebugUnitOfWorkListener());\n        }\n\n        return new EntityManager($conn, $config, $evm);\n    }\n\n    final protected function createSchemaManager(): AbstractSchemaManager\n    {\n        return $this->_em->getConnection()->createSchemaManager();\n    }\n\n    /** @throws Throwable */\n    protected function onNotSuccessfulTest(Throwable $e): never\n    {\n        if ($e instanceof AssertionFailedError) {\n            throw $e;\n        }\n\n        if ($this->isQueryLogAvailable() && $this->getQueryLog()->queries !== []) {\n            $queries       = '';\n            $last25queries = array_slice(array_reverse($this->getQueryLog()->queries, true), 0, 25, true);\n            foreach ($last25queries as $i => $query) {\n                $params   = array_map(static fn ($p) => is_object($p) ? get_debug_type($p) : var_export($p, true), $query['params'] ?: []);\n                $queries .= $i . \". SQL: '\" . $query['sql'] . \"' Params: \" . implode(', ', $params) . PHP_EOL;\n            }\n\n            $trace    = $e->getTrace();\n            $traceMsg = '';\n            foreach ($trace as $part) {\n                if (isset($part['file'])) {\n                    if (str_contains($part['file'], 'PHPUnit/')) {\n                        // Beginning with PHPUnit files we don't print the trace anymore.\n                        break;\n                    }\n\n                    $traceMsg .= $part['file'] . ':' . $part['line'] . PHP_EOL;\n                }\n            }\n\n            $message = '[' . get_debug_type($e) . '] ' . $e->getMessage() . PHP_EOL . PHP_EOL . 'With queries:' . PHP_EOL . $queries . PHP_EOL . 'Trace:' . PHP_EOL . $traceMsg;\n\n            throw new Exception($message, (int) $e->getCode(), $e);\n        }\n\n        throw $e;\n    }\n\n    public function assertSQLEquals(string $expectedSql, string $actualSql): void\n    {\n        self::assertEquals(\n            strtolower($expectedSql),\n            strtolower($actualSql),\n            'Lowercase comparison of SQL statements failed.',\n        );\n    }\n\n    /**\n     * Configures DBAL types required in tests\n     */\n    protected function setUpDBALTypes(): void\n    {\n        if (Type::hasType('rot13')) {\n            Type::overrideType('rot13', Rot13Type::class);\n        } else {\n            Type::addType('rot13', Rot13Type::class);\n        }\n    }\n\n    final protected function isQueryLogAvailable(): bool\n    {\n        return $this->_em?->getConnection() instanceof Connection;\n    }\n\n    final protected function getQueryLog(): QueryLog\n    {\n        $connection = $this->_em->getConnection();\n        if (! $connection instanceof Connection) {\n            throw new RuntimeException(sprintf(\n                'The query log is only available if %s is used as wrapper class. Got %s.',\n                Connection::class,\n                get_debug_type($connection),\n            ));\n        }\n\n        return $connection->queryLog;\n    }\n\n    final protected function assertQueryCount(int $expectedCount, string $message = ''): void\n    {\n        self::assertThat($this->getQueryLog()->queries, new Count($expectedCount), $message);\n    }\n\n    /** @phpstan-return array{sql: string, params: array|null, types: array|null} */\n    final protected function getLastLoggedQuery(int $index = 0): array\n    {\n        $queries   = $this->getQueryLog()->queries;\n        $lastQuery = null;\n        for ($i = $index; $i >= 0; $i--) {\n            $lastQuery = array_pop($queries);\n        }\n\n        if ($lastQuery === null) {\n            throw new RuntimeException('The query log was empty.');\n        }\n\n        return $lastQuery;\n    }\n\n    /**\n     * Drops the table with the specified name, if it exists.\n     *\n     * @throws Exception\n     */\n    protected function dropTableIfExists(string $name): void\n    {\n        $schemaManager = $this->createSchemaManager();\n\n        try {\n            $schemaManager->dropTable($name);\n        } catch (DatabaseObjectNotFoundException) {\n        }\n    }\n\n    /**\n     * Drops and creates a new table.\n     *\n     * @throws Exception\n     */\n    protected function dropAndCreateTable(Table $table): void\n    {\n        $schemaManager = $this->createSchemaManager();\n        $platform      = $this->_em->getConnection()->getDatabasePlatform();\n\n        if ($table instanceof NamedObject) {\n            $tableName = $table->getObjectName()->toSQL($platform);\n        } else {\n            $tableName = $table->getQuotedName($platform);\n        }\n\n        $this->dropTableIfExists($tableName);\n        $schemaManager->createTable($table);\n    }\n\n    final protected function isUninitializedObject(object $entity): bool\n    {\n        return $this->_em->getUnitOfWork()->isUninitializedObject($entity);\n    }\n\n    final protected function initializeObject(object $entity): void\n    {\n        $this->_em->getUnitOfWork()->initializeObject($entity);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/OrmTestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests;\n\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver;\nuse Doctrine\\DBAL\\Driver\\Result;\nuse Doctrine\\DBAL\\Platforms\\AbstractPlatform;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Cache\\CacheConfiguration;\nuse Doctrine\\ORM\\Cache\\CacheFactory;\nuse Doctrine\\ORM\\Cache\\DefaultCacheFactory;\nuse Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger;\nuse Doctrine\\ORM\\Configuration;\nuse Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver;\nuse Doctrine\\Tests\\Mocks\\AttributeDriverFactory;\nuse Doctrine\\Tests\\Mocks\\EntityManagerMock;\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\nuse function class_exists;\nuse function method_exists;\nuse function sprintf;\n\n// DBAL 3 compatibility\nclass_exists('Doctrine\\\\DBAL\\\\Platforms\\\\SqlitePlatform');\n\n/**\n * Base testcase class for all ORM testcases.\n */\nabstract class OrmTestCase extends TestCase\n{\n    /**\n     * The metadata cache that is shared between all ORM tests (except functional tests).\n     */\n    private static CacheItemPoolInterface|null $metadataCache = null;\n\n    /**\n     * The query cache that is shared between all ORM tests (except functional tests).\n     */\n    private static CacheItemPoolInterface|null $queryCache = null;\n\n    /** @var bool */\n    protected $isSecondLevelCacheEnabled = false;\n\n    /** @var bool */\n    protected $isSecondLevelCacheLogEnabled = false;\n\n    /** @var CacheFactory */\n    protected $secondLevelCacheFactory;\n\n    /** @var StatisticsCacheLogger */\n    protected $secondLevelCacheLogger;\n\n    private CacheItemPoolInterface|null $secondLevelCache = null;\n\n    /** @param list<string> $paths */\n    protected function createAttributeDriver(array $paths = []): AttributeDriver\n    {\n        return AttributeDriverFactory::createAttributeDriver($paths);\n    }\n\n    /**\n     * Creates an EntityManager for testing purposes.\n     *\n     * NOTE: The created EntityManager will have its dependant DBAL parts completely\n     * mocked out using a DriverMock, ConnectionMock, etc. These mocks can then\n     * be configured in the tests to simulate the DBAL behavior that is desired\n     * for a particular test,\n     */\n    protected function getTestEntityManager(): EntityManagerMock\n    {\n        return $this->createTestEntityManagerWithPlatform(new SQLitePlatform());\n    }\n\n    protected function createTestEntityManagerWithConnection(Connection $connection): EntityManagerMock\n    {\n        return $this->buildTestEntityManagerWithPlatform($connection);\n    }\n\n    protected function createTestEntityManagerWithPlatform(AbstractPlatform $platform): EntityManagerMock\n    {\n        return $this->buildTestEntityManagerWithPlatform(\n            $this->createConnectionMock($platform),\n        );\n    }\n\n    private function buildTestEntityManagerWithPlatform(Connection $connection): EntityManagerMock\n    {\n        $metadataCache = self::getSharedMetadataCacheImpl();\n\n        $config = new Configuration();\n\n        TestUtil::configureProxies($config);\n        $config->setMetadataCache($metadataCache);\n        $config->setQueryCache(self::getSharedQueryCache());\n        $config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver([__DIR__ . '/Models/Cache']));\n\n        if ($this->isSecondLevelCacheEnabled) {\n            $cacheConfig = new CacheConfiguration();\n            $factory     = new DefaultCacheFactory(\n                $cacheConfig->getRegionsConfiguration(),\n                $this->getSharedSecondLevelCache(),\n            );\n\n            $this->secondLevelCacheFactory = $factory;\n\n            $cacheConfig->setCacheFactory($factory);\n            $config->setSecondLevelCacheEnabled();\n            $config->setSecondLevelCacheConfiguration($cacheConfig);\n        }\n\n        return new EntityManagerMock($connection, $config);\n    }\n\n    protected function enableSecondLevelCache(bool $log = true): void\n    {\n        $this->isSecondLevelCacheEnabled    = true;\n        $this->isSecondLevelCacheLogEnabled = $log;\n    }\n\n    private static function getSharedMetadataCacheImpl(): CacheItemPoolInterface\n    {\n        return self::$metadataCache\n            ?? self::$metadataCache = new ArrayAdapter();\n    }\n\n    private static function getSharedQueryCache(): CacheItemPoolInterface\n    {\n        return self::$queryCache\n            ?? self::$queryCache = new ArrayAdapter();\n    }\n\n    protected function getSharedSecondLevelCache(): CacheItemPoolInterface\n    {\n        return $this->secondLevelCache\n            ?? $this->secondLevelCache = new ArrayAdapter();\n    }\n\n    private function createConnectionMock(AbstractPlatform $platform): Connection\n    {\n        $connection = $this->getMockBuilder(Connection::class)\n            ->setConstructorArgs([[], $this->createDriverMock($platform)])\n            ->onlyMethods(['quote'])\n            ->getMock();\n        $connection->method('quote')->willReturnCallback(static fn (string $input) => sprintf(\"'%s'\", $input));\n\n        return $connection;\n    }\n\n    private function createDriverMock(AbstractPlatform $platform): Driver\n    {\n        $result = $this->createMock(Result::class);\n        $result->method('fetchAssociative')\n            ->willReturn(false);\n\n        $connection = $this->createMock(Driver\\Connection::class);\n        $connection->method('query')\n            ->willReturn($result);\n\n        $driver = $this->createMock(Driver::class);\n        $driver->method('connect')\n            ->willReturn($connection);\n        $driver->method('getDatabasePlatform')\n            ->willReturn($platform);\n\n        if (method_exists(Driver::class, 'getSchemaManager')) {\n            $driver->method('getSchemaManager')\n                ->willReturnCallback([$platform, 'createSchemaManager']);\n        }\n\n        return $driver;\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Proxy/AutoloaderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests\\Proxy;\n\nuse Doctrine\\ORM\\Proxy\\Autoloader;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\Attributes\\IgnoreDeprecations;\nuse PHPUnit\\Framework\\TestCase;\n\nuse function class_exists;\nuse function file_exists;\nuse function file_put_contents;\nuse function sys_get_temp_dir;\nuse function unlink;\n\nuse const DIRECTORY_SEPARATOR;\n\nclass AutoloaderTest extends TestCase\n{\n    /** @return iterable<string, array{string, string, class-string, string}> */\n    public static function dataResolveFile(): iterable\n    {\n        return [\n            ['/tmp', 'MyProxy', 'MyProxy\\RealClass', '/tmp' . DIRECTORY_SEPARATOR . 'RealClass.php'],\n            ['/tmp', 'MyProxy', 'MyProxy\\__CG__\\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'],\n            ['/tmp', 'MyProxy\\Subdir', 'MyProxy\\Subdir\\__CG__\\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'],\n            ['/tmp', 'MyProxy', 'MyProxy\\__CG__\\Other\\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__OtherRealClass.php'],\n        ];\n    }\n\n    /** @param class-string $className */\n    #[DataProvider('dataResolveFile')]\n    #[IgnoreDeprecations]\n    public function testResolveFile(\n        string $proxyDir,\n        string $proxyNamespace,\n        string $className,\n        string $expectedProxyFile,\n    ): void {\n        $actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);\n        self::assertEquals($expectedProxyFile, $actualProxyFile);\n    }\n\n    #[IgnoreDeprecations]\n    public function testAutoload(): void\n    {\n        if (file_exists(sys_get_temp_dir() . '/AutoloaderTestClass.php')) {\n            unlink(sys_get_temp_dir() . '/AutoloaderTestClass.php');\n        }\n\n        $autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', static function ($proxyDir, $proxyNamespace, $className): void {\n            file_put_contents(sys_get_temp_dir() . '/AutoloaderTestClass.php', '<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ');\n        });\n\n        self::assertTrue(class_exists('ProxyAutoloaderTest\\AutoloaderTestClass', true));\n        unlink(sys_get_temp_dir() . '/AutoloaderTestClass.php');\n    }\n}\n"
  },
  {
    "path": "tests/Tests/TestInit.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/*\n * This file bootstraps the test environment.\n */\n\nnamespace Doctrine\\Tests;\n\nuse Exception;\n\nuse function date_default_timezone_set;\nuse function error_reporting;\nuse function file_exists;\nuse function mkdir;\n\nuse const E_ALL;\n\nerror_reporting(E_ALL);\ndate_default_timezone_set('UTC');\n\nif (file_exists(__DIR__ . '/../../vendor/autoload.php')) {\n    // dependencies were installed via composer - this is the main project\n    require __DIR__ . '/../../vendor/autoload.php';\n} elseif (file_exists(__DIR__ . '/../../../../autoload.php')) {\n    // installed as a dependency in `vendor`\n    require __DIR__ . '/../../../../autoload.php';\n} else {\n    throw new Exception('Can\\'t find autoload.php. Did you install dependencies via composer?');\n}\n\nif (! file_exists(__DIR__ . '/Proxies') && ! mkdir(__DIR__ . '/Proxies')) {\n    throw new Exception('Could not create ' . __DIR__ . '/Proxies Folder.');\n}\n\nif (! file_exists(__DIR__ . '/ORM/Proxy/generated') && ! mkdir(__DIR__ . '/ORM/Proxy/generated')) {\n    throw new Exception('Could not create ' . __DIR__ . '/ORM/Proxy/generated Folder.');\n}\n"
  },
  {
    "path": "tests/Tests/TestUtil.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Doctrine\\Tests;\n\nuse Doctrine\\Common\\EventSubscriber;\nuse Doctrine\\DBAL\\Configuration as DbalConfiguration;\nuse Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver\\Middleware\\EnableForeignKeys;\nuse Doctrine\\DBAL\\DriverManager;\nuse Doctrine\\DBAL\\Exception\\DatabaseObjectNotFoundException;\nuse Doctrine\\DBAL\\Platforms\\SQLitePlatform;\nuse Doctrine\\ORM\\Configuration;\nuse UnexpectedValueException;\n\nuse function assert;\nuse function class_exists;\nuse function explode;\nuse function fwrite;\nuse function get_debug_type;\nuse function getenv;\nuse function in_array;\nuse function sprintf;\nuse function str_starts_with;\nuse function strlen;\nuse function substr;\n\nuse const PHP_VERSION_ID;\nuse const STDERR;\n\n/**\n * TestUtil is a class with static utility methods used during tests.\n */\nclass TestUtil\n{\n    /** @var bool Whether the database schema is initialized. */\n    private static bool $initialized = false;\n\n    /**\n     * Gets a <b>real</b> database connection using the following parameters\n     * of the $GLOBALS array:\n     *\n     * 'db_driver' : The name of the Doctrine DBAL database driver to use.\n     * 'db_user' : The username to use for connecting.\n     * 'db_password' : The password to use for connecting.\n     * 'db_host' : The hostname of the database to connect to.\n     * 'db_server' : The server name of the database to connect to\n     *               (optional, some vendors allow multiple server instances with different names on the same host).\n     * 'db_dbname' : The name of the database to connect to.\n     * 'db_port' : The port of the database to connect to.\n     *\n     * These variables of the $GLOBALS array are filled by PHPUnit based on an XML configuration file.\n     *\n     * IMPORTANT:\n     * 1) Each invocation of this method returns a NEW database connection.\n     * 2) The database is dropped and recreated to ensure it's clean.\n     */\n    public static function getConnection(DbalConfiguration|null $config = null): DbalExtensions\\Connection\n    {\n        if (! self::$initialized) {\n            self::initializeDatabase();\n            self::$initialized = true;\n        }\n\n        $connectionParameters = self::getTestConnectionParameters();\n\n        if (in_array($connectionParameters['driver'], ['pdo_sqlite', 'sqlite3'], true) && class_exists(EnableForeignKeys::class)) {\n            if ($config === null) {\n                $config = new DbalConfiguration();\n            }\n\n            $config->setMiddlewares([...$config->getMiddlewares(), new EnableForeignKeys()]);\n        }\n\n        $connection = DriverManager::getConnection($connectionParameters, $config);\n        assert($connection instanceof DbalExtensions\\Connection);\n\n        self::addDbEventSubscribers($connection);\n\n        return $connection;\n    }\n\n    public static function getPrivilegedConnection(): DbalExtensions\\Connection\n    {\n        $connection = DriverManager::getConnection(self::getPrivilegedConnectionParameters());\n        assert($connection instanceof DbalExtensions\\Connection);\n\n        return $connection;\n    }\n\n    public static function configureProxies(Configuration $configuration): void\n    {\n        $enableNativeLazyObjects = getenv('ENABLE_NATIVE_LAZY_OBJECTS');\n\n        if ($enableNativeLazyObjects === false) {\n            // If the environment variable is not set, we default to true.\n            // This is OK because environment variables are always strings, and\n            // we are comparing it to a boolean.\n            $enableNativeLazyObjects = true;\n        }\n\n        if (PHP_VERSION_ID >= 80400 && $enableNativeLazyObjects) {\n            $configuration->enableNativeLazyObjects(true);\n\n            return;\n        }\n\n        $configuration->setProxyDir(__DIR__ . '/Proxies');\n        $configuration->setProxyNamespace('Doctrine\\Tests\\Proxies');\n    }\n\n    private static function initializeDatabase(): void\n    {\n        $testConnParams = self::getTestConnectionParameters();\n        $privConnParams = self::getPrivilegedConnectionParameters();\n\n        $testConn = DriverManager::getConnection($testConnParams);\n\n        // Note, writes direct to STDERR to prevent phpunit detecting output - otherwise this would cause either an\n        // \"unexpected output\" warning or a failure on the first test case to call this method.\n        fwrite(STDERR, sprintf(\"\\nUsing DB driver %s\\n\", get_debug_type($testConn->getDriver())));\n\n        // Connect as a privileged user to create and drop the test database.\n        $privConn = DriverManager::getConnection($privConnParams);\n\n        $platform = $privConn->getDatabasePlatform();\n\n        if ($platform instanceof SQLitePlatform) {\n            $schema = $testConn->createSchemaManager()->introspectSchema();\n            $stmts  = $schema->toDropSql($testConn->getDatabasePlatform());\n\n            foreach ($stmts as $stmt) {\n                $testConn->executeStatement($stmt);\n            }\n        } else {\n            $dbname = $testConnParams['dbname'] ?? $testConn->getDatabase();\n            $testConn->close();\n\n            $schemaManager = $privConn->createSchemaManager();\n\n            try {\n                $schemaManager->dropDatabase($dbname);\n            } catch (DatabaseObjectNotFoundException) {\n            }\n\n            $schemaManager->createDatabase($dbname);\n\n            $privConn->close();\n        }\n    }\n\n    private static function addDbEventSubscribers(Connection $conn): void\n    {\n        if (! isset($GLOBALS['db_event_subscribers'])) {\n            return;\n        }\n\n        $evm = $conn->getEventManager();\n        /** @var class-string<EventSubscriber> $subscriberClass */\n        foreach (explode(',', $GLOBALS['db_event_subscribers']) as $subscriberClass) {\n            $subscriberInstance = new $subscriberClass();\n            $evm->addEventSubscriber($subscriberInstance);\n        }\n    }\n\n    /** @return array<string, mixed> */\n    private static function getPrivilegedConnectionParameters(): array\n    {\n        if (isset($GLOBALS['privileged_db_driver'])) {\n            return self::mapConnectionParameters($GLOBALS, 'privileged_db_');\n        }\n\n        $parameters = self::mapConnectionParameters($GLOBALS, 'db_');\n        unset($parameters['dbname']);\n\n        return $parameters;\n    }\n\n    /** @phpstan-return array<string, mixed> */\n    private static function getTestConnectionParameters(): array\n    {\n        if (! isset($GLOBALS['db_driver'])) {\n            throw new UnexpectedValueException(\n                'You must provide database connection params including a db_driver value. See phpunit.xml.dist for details',\n            );\n        }\n\n        return self::mapConnectionParameters($GLOBALS, 'db_');\n    }\n\n    /**\n     * @param array<string,mixed> $configuration\n     *\n     * @return array<string,mixed>\n     */\n    private static function mapConnectionParameters(array $configuration, string $prefix): array\n    {\n        $parameters = [];\n\n        foreach (\n            [\n                'driver',\n                'user',\n                'password',\n                'host',\n                'dbname',\n                'port',\n                'server',\n                'memory',\n                'path',\n                'ssl_key',\n                'ssl_cert',\n                'ssl_ca',\n                'ssl_capath',\n                'ssl_cipher',\n                'unix_socket',\n            ] as $parameter\n        ) {\n            if (! isset($configuration[$prefix . $parameter])) {\n                continue;\n            }\n\n            $parameters[$parameter] = $configuration[$prefix . $parameter];\n        }\n\n        if (isset($parameters['port'])) {\n            $parameters['port'] = (int) $parameters['port'];\n        }\n\n        foreach ($configuration as $param => $value) {\n            if (str_starts_with($param, $prefix . 'driver_option_')) {\n                $parameters['driverOptions'][substr($param, strlen($prefix . 'driver_option_'))] = $value;\n            }\n\n            if (! str_starts_with($param, $prefix . 'default_table_option_')) {\n                continue;\n            }\n\n            $parameters['defaultTableOptions'][substr($param, strlen($prefix . 'default_table_option_'))] = $value;\n        }\n\n        $parameters['wrapperClass'] = DbalExtensions\\Connection::class;\n\n        return $parameters;\n    }\n}\n"
  },
  {
    "path": "tests/dbproperties.xml.dev",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Use this configuration file as a template to run the tests against any dbms.\n    Procedure:\n        1) Save a copy of this file with a name of your choosing. It doesn't matter\n           where you place it as long as you know where it is.\n           i.e. \"mysqlconf.xml\" (It needs the ending .xml).\n        2) Edit the file and fill in your settings (database name, type, username, etc.)\n           Just change the \"value\"s, not the names of the var elements.\n        3) To run the tests against the database type the following from within the\n           tests/ folder: phpunit --configuration <filename> ...\n           Example: phpunit --configuration mysqlconf.xml AllTests\n-->\n<phpunit>\n  <php>\n    <!-- \"Real\" test database -->\n    <var name=\"db_driver\" value=\"pdo_mysql\"/>\n    <var name=\"db_host\" value=\"localhost\" />\n    <var name=\"db_user\" value=\"root\" />\n    <var name=\"db_password\" value=\"\" />\n    <var name=\"db_dbname\" value=\"doctrine_tests\" />\n    <var name=\"db_port\" value=\"3306\"/>\n    <!--<var name=\"db_event_subscribers\" value=\"Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit\">-->\n    \n    <!--\n    At the start of each test run, we will drop and recreate the test database.\n\n    By default we assume that the `db_` config above has unrestricted access to the provided database\n    platform.\n\n    If you prefer, you can provide a restricted user above and a separate `privileged_db` config\n    block to provide details of a privileged connection to use for the setup / teardown actions.\n\n    Note that these configurations are not merged - if you specify a `privileged_db_driver` then\n    you must also specify all the other options that your driver requires.\n    <var name=\"privileged_db_driver\" value=\"pdo_mysql\"/>\n    <var name=\"privileged_db_host\" value=\"localhost\" />\n    <var name=\"privileged_db_username\" value=\"root\" />\n    <var name=\"privileged_db_password\" value=\"\" />\n    <var name=\"privileged_db_dbname\" value=\"doctrine_tests_tmp\" />\n    <var name=\"privileged_db_port\" value=\"3306\"/>\n    -->\n  </php>\n</phpunit>\n"
  }
]