Compare commits

...

83 Commits

Author SHA1 Message Date
Grégoire Paris
8ed6c2234a Merge pull request #11661 from doctrine/2.19.x
Merge 2.19.x up into 2.20.x
2024-10-11 13:47:24 +02:00
Grégoire Paris
ff612b9678 Merge pull request #11660 from simPod/test-method
test: cover all transactional methods in `EntityManagerTest::testItPreservesTheOriginalExceptionOnRollbackFailure()`
2024-10-11 13:11:31 +02:00
Simon Podlipsky
ee0d7197dd test: cover all transactional methods in EntityManagerTest::testItPreservesTheOriginalExceptionOnRollbackFailure() 2024-10-11 13:00:52 +02:00
Matthias Pigulla
39d2136f46 Fix different first/max result values taking up query cache space (#11188)
* Add a test covering the #11112 issue

* Add new OutputWalker and SqlFinalizer interfaces

* Add a SingleSelectSqlFinalizer that can take care of adding offset/limit as well as locking mode statements to a given SQL query.

Add a FinalizedSelectExecutor that executes given, finalized SQL statements.

* In SqlWalker, split SQL query generation into the two parts that shall happen before and after the finalization phase.

Move the part that generates "pre-finalization" SQL into a dedicated method. Use a side channel in SingleSelectSqlFinalizer to access the "finalization" logic and avoid duplication.

* Fix CS violations

* Skip the GH11112 test while applying refactorings

* Avoid a Psalm complaint due to invalid (?) docblock syntax

* Establish alternate code path - queries can obtain the sql executor through the finalizer, parser knows about output walkers yielding finalizers

* Remove a possibly premature comment

* Re-enable the #11112 test

* Fix CS

* Make RootTypeWalker inherit from SqlOutputWalker so it becomes finalizer-aware

* Update QueryCacheTest, since first/max results no longer need extra cache entries

* Fix ParserResultSerializationTest by forcing the parser to produce a ParserResult of the old kind (with the executor already constructed)

* Fix WhereInWalkerTest

* Update lib/Doctrine/ORM/Query/Exec/PreparedExecutorFinalizer.php

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

* Fix tests

* Fix a Psalm complaint

* Fix a test

* Fix CS

* Make the NullSqlWalker an instance of SqlOutputWalker

* Avoid multiple cache entries caused by LimitSubqueryOutputWalker

* Fix Psalm complaints

* Fix static analysis complaints

* Remove experimental code that I committed accidentally

* Remove unnecessary baseline entry

* Make AddUnknownQueryComponentWalker subclass SqlOutputWalker

That way, we have no remaining classes in the codebase subclassing SqlWalker but not SqlOutputWalker

* Use more expressive exception classes

* Add a deprecation message

* Move SqlExecutor creation to ParserResult, to minimize public methods available on it

* Avoid keeping the SqlExecutor in the Query, since it must be generated just in time (e. g. in case Query parameters change)

* Address PHPStan complaints

* Fix tests

* Small refactorings

* Add an upgrade notice

* Small refactorings

* Update the Psalm baseline

* Add a missing namespace import

* Update Psalm baseline

* Fix CS

* Fix Psalm baseline

---------

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2024-10-10 15:15:08 +02:00
Benjamin Eberlei
bea454eefc [GH-8471] undeprecate partials completly (#11647)
* [GH-8471] Undeprecate all PARTIAL object usage.
2024-10-10 13:54:34 +02:00
Grégoire Paris
14f2572e4e Merge pull request #11656 from doctrine/2.19.x
Merge 2.19.x up into 2.20.x
2024-10-10 13:53:05 +02:00
Grégoire Paris
c2c500077b Merge pull request #11646 from greg0ire/finally-fix-bug
Run risky code in finally block
2024-10-10 11:46:49 +02:00
Grégoire Paris
6281c2b79f Merge pull request #11655 from greg0ire/submodule-cleanup
Submodule cleanup
2024-10-10 11:12:12 +02:00
Grégoire Paris
bac1c17eab Remove submodule remnant
This should make a warning we have in the CI go away.

>  fatal: No url found for submodule path 'docs/en/_theme' in .gitmodules
2024-10-10 11:07:38 +02:00
Grégoire Paris
b6137c8911 Add guard clause
It maybe happen that the SQL COMMIT statement is successful, but then
something goes wrong. In that kind of case, you do not want to attempt a
rollback.

This was implemented in UnitOfWork::commit(), but for some reason not in
the similar EntityManager methods.
2024-10-10 10:58:24 +02:00
Grégoire Paris
51be1b1d52 Run risky code in finally block
catch blocks are not supposed to fail. If you want to do something
despite an exception happening, you should do it in a finally block.

Closes #7545
2024-10-10 10:06:12 +02:00
Alexander M. Turek
488a5dd3bf Remove vendor prefix of PHPDoc referencing class-string (#11643) 2024-10-09 21:58:37 +02:00
Matthias Pigulla
896c65504d Deprecate the \Doctrine\ORM\Query\Parser::setCustomOutputTreeWalker() method (#11641)
We use this method only from within one of our own test cases, and I don't see how it would be useful to anybody else outside – it has to be called on the `Parser` instance which exists internally in the `Query` only.

Deprecating and removing it in 3.x allows for a slight simplification in the `Parser` there, since we do no longer need the field (it can be a local variable).
2024-10-09 16:12:41 +02:00
Matthias Pigulla
16a8f10fd2 Remove a misleading comment (#11644) 2024-10-09 15:37:04 +02:00
Alexander M. Turek
d80a831157 Stop recommending vendor-prefixed PHPDoc (#11640) 2024-10-09 14:48:42 +02:00
Alexander M. Turek
52660297ab Let PHPStan detect deprecated usages (#11639) 2024-10-09 14:47:57 +02:00
Alexander M. Turek
58287bb731 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  PHPStan 1.12.6 (#11635)
2024-10-09 11:08:42 +02:00
Alexander M. Turek
bc37f75b41 PHPStan 1.12.6 (#11635) 2024-10-09 11:08:02 +02:00
Grégoire Paris
8a25b264f7 Add upgrade note about property hooks (#11636)
People that might have experimented with property hooks while still
using ORM < 2.20.0 need to know that they need to remove their
experiment or upgrade to a version that explicitly supports them.
2024-10-09 11:05:58 +02:00
Benjamin Eberlei
0e48b19cd3 Prepare PHP 8.4 support: Prevent property hooks from being used (#11628)
Prevent property hooks from being used as they currently would work on external non-raw values without explicit code.
2024-10-09 10:36:21 +02:00
Grégoire Paris
109042e5af Merge pull request #11631 from greg0ire/php84-ci
Add CI job for PHP 8.4
2024-10-09 09:42:08 +02:00
Grégoire Paris
08328adc6c Use E_ALL instead of E_ALL | E_STRICT
E_STRICT is deprecated as of PHP 8.4
2024-10-09 09:19:32 +02:00
Grégoire Paris
65806884b0 Add CI job for PHP 8.4
For now doctrine/common generates proxies that trigger deprecation, so
let us only test with lazy ghosts only.
2024-10-08 17:56:38 +02:00
Alexander M. Turek
ad80e8281a Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Replace custom directives with native option
2024-10-08 15:54:57 +02:00
Grégoire Paris
0c0c61c51b Merge pull request #11627 from greg0ire/no-custom-directives
Replace custom directives with native option
2024-10-08 15:26:44 +02:00
Grégoire Paris
cc28fed9f5 Replace custom directives with native option 2024-10-08 14:43:18 +02:00
Alexander M. Turek
2245149588 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Make nullable parameters explicit in generated entities (#11625)
  Update attributes-reference.rst
  Bump doctrine/.github from 5.0.1 to 5.1.0 (#11616)
  Move orphan metadata to where it belongs
  PHPStan 1.12 (#11585)
2024-10-08 12:26:50 +02:00
Alexander M. Turek
b13564c6c0 Make nullable parameters explicit in generated entities (#11625) 2024-10-08 12:25:31 +02:00
Max Mustermann
91709c1275 fix generating duplicate method stubs
When adding the same lifecycle event callback to two or more lifecycle events, the generator will create a stub for each event resulting in fatal 'Cannot redeclare' errors. That is, only if the callback name contains uppercase letters.
2024-10-05 13:40:04 +02:00
Grégoire Paris
d18126aac5 Merge pull request #11618 from n0099/patch-1
unclosed `]` in attributes-reference.rst
2024-10-01 17:27:04 +02:00
n0099
b7fd8241cf Update attributes-reference.rst 2024-10-01 21:19:44 +08:00
dependabot[bot]
2432939e4f Bump doctrine/.github from 5.0.1 to 5.1.0 (#11616)
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/5.0.1...5.1.0)

---
updated-dependencies:
- dependency-name: doctrine/.github
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 09:04:06 +02:00
Grégoire Paris
1bf4603422 Merge pull request #11615 from greg0ire/move-orphan
Move orphan metadata to where it belongs
2024-09-29 09:22:54 +02:00
Grégoire Paris
25d5bc5b46 Move orphan metadata to where it belongs
The goal here was to retain compatibility with doctrine/rst-parser,
which is no longer in use in the website.
2024-09-27 19:42:13 +02:00
Alexander M. Turek
6cde337777 PHPStan 1.12 (#11585) 2024-08-27 12:10:07 +02:00
Grégoire Paris
74ef28295a Merge pull request #11582 from doctrine/2.19.x-merge-up-into-2.20.x_0oKsBvVN
Merge release 2.19.7 into 2.20.x
2024-08-23 12:29:43 +02:00
Grégoire Paris
168ac31084 Merge pull request #11109 from mcurland/Fix11108
Original entity data resolves inverse 1-1 joins
2024-08-23 08:54:57 +02:00
Matthew Curland
fe4a2e83cf Original entity data resolves inverse 1-1 joins
If the source entity for an inverse (non-owning) 1-1 relationship is
identified by an association then the identifying association may not
be set when an inverse one-to-one association is resolved. This means
that no data is available in the entity to resolve the needed column
value for the join query.

The original entity data can be retrieved from the unit of work and
is used as a fallback to populate the query condition.

Fixes #11108
2024-08-17 11:50:56 +02:00
Grégoire Paris
8ac6a13ca0 Merge pull request #11564 from gitbugr/GH11501_fix_o2m_persister_single_inheritence_parent_relation_bugfix
GH11551 - fix OneToManyPersister::deleteEntityCollection case where single-inheritence table parent entity is targetEntity.
2024-08-05 07:47:46 +02:00
gitbugr
2707b09a07 fix spacing
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2024-08-03 21:38:49 +01:00
Kyron Taylor
121158f92c GH11551 - fix OneToManyPersister::deleteEntityCollection when using
single-inheritence entity parent as targetEntity.

When using the parent entity for a single-inheritence table as the
targetEntity for a property, the discriminator value should be all
of the values in the discriminator map.
OneToManyPersister::deleteEntityCollection has been amended to
reflect this.
2024-08-03 16:55:14 +01:00
Grégoire Paris
51ad860a25 Merge pull request #11543 from stof/fix_native_query_parameter_type
Fix the support for custom parameter types in native queries
2024-07-04 20:12:59 +02:00
Christophe Coevoet
9bd51aaeb6 Fix the support for custom parameter types in native queries
The Query class (used for DQL queries) takes care of using the value and
type as is when a type was specified for a parameter instead of going
through the default processing of values.
The NativeQuery class was missing the equivalent check, making the
custom type work only if the default processing of values does not
convert the value to a different one.
2024-07-04 16:25:34 +02:00
Grégoire Paris
c37b115450 Merge pull request #11534 from k00ni/patch-1
working-with-objects.rst: added missing white space
2024-06-28 09:03:54 +02:00
Konrad Abicht
19129e9f8a working-with-objects.rst: added missing white space 2024-06-28 09:00:12 +02:00
Grégoire Paris
7d01f19667 Merge pull request #11531 from doctrine/2.19.x-merge-up-into-2.20.x_QMtlHSin
Merge release 2.19.6 into 2.20.x
2024-06-27 17:50:50 +02:00
Grégoire Paris
c1bb2ccf4b Merge pull request #11526 from GromNaN/patch-1
doc: Use modern array syntax in getting started
2024-06-26 19:24:40 +02:00
Jérôme Tamarelle
e3d7c6076c Use modern array syntax in the doc 2024-06-26 19:18:32 +02:00
Grégoire Paris
40f299f1eb Merge pull request #11506 from michalbundyra/composite-key-relations-3
[2.19.x] Fetching entities with Composite Key Relations and null values
2024-06-21 08:12:27 +02:00
Grégoire Paris
d0e9177121 Merge pull request #11514 from doctrine/2.19.x
Merge 2.19.x up into 2.20.x
2024-06-20 22:51:33 +02:00
Grégoire Paris
68af854f46 Merge pull request #11513 from greg0ire/address-persistence-3.3.3-release
Address doctrine/persistence 3.3.3 release
2024-06-20 22:14:52 +02:00
Grégoire Paris
77467cd824 Address doctrine/persistence 3.3.3 release
FileDriver became templatable, and some very wrong phpdoc has been
fixed, causing Psalm to better understand the 2 FileDriver classes in
this project.
2024-06-20 22:00:33 +02:00
Grégoire Paris
802f20b8e7 Merge pull request #11509 from greg0ire/remove-unneeded-rule
Remove unneeded CS rule
2024-06-19 23:49:15 +02:00
Michał Bundyra
96d13ac62a Fetching entities with Composite Key Relations and null values
Remove redundant condition to check if target class contains foreign
identifier in order to allow fetching a null for relations with
composite keys, when part of the key value is null.
2024-06-19 21:54:02 +01:00
Grégoire Paris
2ea6a1a5fb Remove unneeded CS rule 2024-06-19 21:47:55 +02:00
Alexander M. Turek
83851a9716 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Fix OneToManyPersister::deleteEntityCollection missing discriminator column/value. (GH-11500)
  Skip joined entity creation for empty relation (#10889)
  ci: maintained and stable mariadb version (11.4 current lts) (#11490)
  fix(docs): use string value in `addAttribute`
  Replace assertion with exception (#11489)
  Use ramsey/composer-install in PHPBench workflow
  update EntityManager#transactional to EntityManager#wrapInTransaction
  Fix cloning entities
  Consider usage of setFetchMode when checking for simultaneous usage of fetch-mode EAGER and WITH condition.
2024-06-18 14:19:19 +02:00
Alexander M. Turek
066ec1ac81 Fix upgrade guide for 2.20 (#11504) 2024-06-18 14:18:37 +02:00
Benjamin Eberlei
68744489f0 Undeprecate PARTIAL for array hydration. (#11366)
* Undeprecate PARTIAL for array hydration.

* note about undeprecate partial in UPGRADE.md
2024-06-18 14:15:31 +02:00
Grégoire Paris
cc2ad1993c Merge pull request #11501 from gitbugr/2.19.x
Fix OneToManyPersister::deleteEntityCollection missing discriminator column/value. (GH-11500)
2024-06-17 21:40:07 +02:00
Kyron Taylor
e4d46c4276 Fix OneToManyPersister::deleteEntityCollection missing discriminator column/value. (GH-11500) 2024-06-15 21:58:08 +01:00
Grégoire Paris
858a1adc3b Merge pull request #11194 from noemi-salaun/fix/gh10889
Skip joined entity creation for empty relation (#10889)
2024-06-14 20:06:59 +02:00
Noemi Salaun
3b499132d9 Skip joined entity creation for empty relation (#10889) 2024-06-14 14:34:04 +02:00
Daniel Black
39153fd88a ci: maintained and stable mariadb version (11.4 current lts) (#11490)
Also use MARIADB env names and the healthcheck.sh included in the container.
2024-06-13 19:34:46 +02:00
Grégoire Paris
bdc9679e37 Merge pull request #11493 from SamMousa/fix-docs-11492
fix(docs): use string value in `addAttribute`
2024-06-11 15:26:45 +01:00
Sam Mousa
87a8ee21c9 fix(docs): use string value in addAttribute 2024-06-11 16:21:28 +02:00
Grégoire Paris
59c8bc09ab Replace assertion with exception (#11489) 2024-06-03 23:08:27 +02:00
Grégoire Paris
3a7d7c9f57 Merge pull request #11484 from greg0ire/backport-ramsey
Use ramsey/composer-install in PHPBench workflow
2024-06-02 15:26:00 +02:00
Grégoire Paris
06eca40134 Use ramsey/composer-install in PHPBench workflow
It will handle caching for us.
2024-06-02 15:22:59 +02:00
Grégoire Paris
23b35e9554 Merge pull request #11475 from nicolas-grekas/fix-clone
Fix cloning entities
2024-06-01 22:47:57 +02:00
Grégoire Paris
e063926cbd Merge pull request #11445 from aprat84/gh-11128
Consider usage of setFetchMode when checking for simultaneous usage of fetch-mode EAGER and WITH condition
2024-05-30 17:24:11 +02:00
Grégoire Paris
4a01a76a17 Merge pull request #11460 from IndraGunawan/update-transactional-doc
docs: update EntityManager#transactional to EntityManager#wrapInTransaction
2024-05-28 14:07:06 +02:00
Indra Gunawan
93c2dd9d4b update EntityManager#transactional to EntityManager#wrapInTransaction
One has been deprecated in favor of the other.
2024-05-28 13:59:17 +02:00
Nicolas Grekas
75bc22980e Fix cloning entities 2024-05-27 14:53:58 +02:00
Alix Mauro
9696c3434d Consider usage of setFetchMode when checking for simultaneous usage of fetch-mode EAGER and WITH condition.
This fixes a bug that arises when an entity relation is mapped with
fetch-mode EAGER but setFetchMode LAZY (or anything that is not EAGER)
has been used on the query. If the query use WITH condition, an
exception is incorrectly raised (Associations with fetch-mode=EAGER may
not be using WITH conditions).

Fixes #11128

Co-Authored-By: Albert Prat <albert.prat@interactiu.cat>
2024-05-25 14:22:20 +02:00
Alexander M. Turek
bf3e082c00 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Psalm 5.24.0 (#11467)
  PHPStan 1.11.1 (#11466)
  Test with actual lock modes (#11465)
  Backport test for Query::setLockMode() (#11463)
2024-05-21 14:22:18 +02:00
Alexander M. Turek
eb49f66926 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Bump ramsey/composer-install from 2 to 3 (#11442)
  Bump doctrine/.github from 3.0.0 to 5.0.1
  Upgrade codecov/codecov-action
2024-05-21 08:40:37 +02:00
Grégoire Paris
8b6a58fa0e Merge pull request #11432 from doctrine/2.19.x-merge-up-into-2.20.x_IfraK93L
Merge release 2.19.5 into 2.20.x
2024-04-30 09:04:52 +02:00
Alexander M. Turek
b725908c83 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Fix BIGINT validation (#11414)
  Fix templated phpdoc return type (#11407)
  [Documentation] Merging "Query Result Formats" with "Hydration Modes"
  Fix psalm errors: remove override of template type
  Update dql-doctrine-query-language.rst
  Adding `NonUniqueResultException`
  [Documentation] Query Result Formats
2024-04-15 16:26:53 +02:00
Alexander M. Turek
be307edba8 Merge release 2.19.3 into 2.20.x (#11398) 2024-03-22 12:11:39 +01:00
Alexander M. Turek
083f642cfa Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  Remove unused variable (#11391)
2024-03-21 10:33:34 +01:00
Alexander M. Turek
716da7e538 Merge branch '2.19.x' into 2.20.x
* 2.19.x:
  [Documentation] Removing "Doctrine Mapping Types" ... (#11384)
  [GH-11185] Bugfix: do not use collection batch loading for indexBy assocations. (#11380)
  Improve lazy ghost performance by avoiding self-referencing closure. (#11376)
2024-03-21 10:12:37 +01:00
Grégoire Paris
bcdc5bdaf4 Merge pull request #11378 from doctrine/2.19.x-merge-up-into-2.20.x_eyF2lMAL
Merge release 2.19.2 into 2.20.x
2024-03-18 20:22:04 +01:00
Grégoire Paris
a3e3a3bbf3 Merge pull request #11360 from doctrine/2.19.x-merge-up-into-2.20.x_aXnS7Xw9
Merge release 2.19.1 into 2.20.x
2024-03-16 21:32:18 +01:00
148 changed files with 2224 additions and 642 deletions

View File

@@ -24,4 +24,4 @@ on:
jobs:
coding-standards:
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.1.0"

View File

@@ -40,6 +40,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
dbal-version:
- "default"
extension:
@@ -113,6 +114,7 @@ jobs:
php-version:
- "8.2"
- "8.3"
- "8.4"
dbal-version:
- "default"
- "3@dev"
@@ -186,11 +188,12 @@ jobs:
php-version:
- "8.2"
- "8.3"
- "8.4"
dbal-version:
- "default"
- "3@dev"
mariadb-version:
- "10.9"
- "11.4"
extension:
- "mysqli"
- "pdo_mysql"
@@ -204,11 +207,11 @@ jobs:
mariadb:
image: "mariadb:${{ matrix.mariadb-version }}"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: "doctrine_tests"
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes
MARIADB_DATABASE: "doctrine_tests"
options: >-
--health-cmd "mysqladmin ping --silent"
--health-cmd "healthcheck.sh --connect --innodb_initialized"
ports:
- "3306:3306"
@@ -256,6 +259,7 @@ jobs:
php-version:
- "8.2"
- "8.3"
- "8.4"
dbal-version:
- "default"
- "3@dev"

View File

@@ -5,45 +5,16 @@ on:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
- ".github/workflows/documentation.yml"
- "docs/**"
push:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
- ".github/workflows/documentation.yml"
- "docs/**"
jobs:
validate-with-guides:
name: "Validate documentation with phpDocumentor/guides"
runs-on: "ubuntu-22.04"
steps:
- name: "Checkout code"
uses: "actions/checkout@v4"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.3"
- name: "Remove existing composer file"
run: "rm composer.json"
- name: "Require phpdocumentor/guides-cli"
run: "composer require --dev phpdocumentor/guides-cli --no-update"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"
- name: "Add orphan metadata where needed"
run: |
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/reference/installation.rst)" > docs/en/reference/installation.rst
- name: "Run guides-cli"
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"
documentation:
name: "Documentation"
uses: "doctrine/.github/.github/workflows/documentation.yml@5.1.0"

View File

@@ -47,15 +47,8 @@ jobs:
coverage: "pcov"
ini-values: "zend.assertions=1, apc.enable_cli=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v3"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
- name: "Run PHPBench"
run: "vendor/bin/phpbench run --report=default"

View File

@@ -7,7 +7,7 @@ on:
jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.1.0"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}

View File

@@ -1,3 +1,39 @@
# Upgrade to 2.20
## Add `Doctrine\ORM\Query\OutputWalker` interface, deprecate `Doctrine\ORM\Query\SqlWalker::getExecutor()`
Output walkers should implement the new `\Doctrine\ORM\Query\OutputWalker` interface and create
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances instead of `Doctrine\ORM\Query\Exec\AbstractSqlExecutor`s.
The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the
`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.
Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`
method. Details can be found at https://github.com/doctrine/orm/pull/11188.
## Explictly forbid property hooks
Property hooks are not supported yet by Doctrine ORM. Until support is added,
they are explicitly forbidden because the support would result in a breaking
change in behavior.
Progress on this is tracked at https://github.com/doctrine/orm/issues/11624 .
## PARTIAL DQL syntax is undeprecated
Use of the PARTIAL keyword is not deprecated anymore in DQL, because we will be
able to support PARTIAL objects with PHP 8.4 Lazy Objects and
Symfony/VarExporter in a better way. When we decided to remove this feature
these two abstractions did not exist yet.
WARNING: If you want to upgrade to 3.x and still use PARTIAL keyword in DQL
with array or object hydrators, then you have to directly migrate to ORM 3.3.x or higher.
PARTIAL keyword in DQL is not available in 3.0, 3.1 and 3.2 of ORM.
## Deprecate `\Doctrine\ORM\Query\Parser::setCustomOutputTreeWalker()`
Use the `\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER` query hint to set the output walker
class instead of setting it through the `\Doctrine\ORM\Query\Parser::setCustomOutputTreeWalker()` method
on the parser instance.
# Upgrade to 2.19
## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association

View File

@@ -15,7 +15,8 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
"dealerdirect/phpcodesniffer-composer-installer": true
"dealerdirect/phpcodesniffer-composer-installer": true,
"phpstan/extension-installer": true
},
"sort-packages": true
},
@@ -42,7 +43,9 @@
"doctrine/annotations": "^1.13 || ^2",
"doctrine/coding-standard": "^9.0.2 || ^12.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "~1.4.10 || 1.11.1",
"phpstan/extension-installer": "~1.1.0 || ^1.4",
"phpstan/phpstan": "~1.4.10 || 1.12.6",
"phpstan/phpstan-deprecation-rules": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",

Submodule docs/en/_theme deleted from 6f1bc8bead

View File

@@ -14,7 +14,7 @@ Index
- :ref:`#[AttributeOverride] <attrref_attributeoverride>`
- :ref:`#[Column] <attrref_column>`
- :ref:`#[Cache] <attrref_cache>`
- :ref:`#[ChangeTrackingPolicy <attrref_changetrackingpolicy>`
- :ref:`#[ChangeTrackingPolicy] <attrref_changetrackingpolicy>`
- :ref:`#[CustomIdGenerator] <attrref_customidgenerator>`
- :ref:`#[DiscriminatorColumn] <attrref_discriminatorcolumn>`
- :ref:`#[DiscriminatorMap] <attrref_discriminatormap>`

View File

@@ -1,3 +1,5 @@
:orphan:
Installation
============

View File

@@ -81,8 +81,8 @@ implements the ``MappingDriver`` interface:
/**
* Loads the metadata for the specified class into the provided container.
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @return void
*
@@ -93,8 +93,7 @@ implements the ``MappingDriver`` interface:
/**
* Gets the names of all mapped classes known to this driver.
*
* @return array<int, string> The names of all mapped classes known to this driver.
* @psalm-return list<class-string>
* @return list<class-string> The names of all mapped classes known to this driver.
*/
public function getAllClassNames();
@@ -102,7 +101,7 @@ implements the ``MappingDriver`` interface:
* Returns whether the class with the specified name should have its metadata loaded.
* This is only the case if it is either mapped as an Entity or a MappedSuperclass.
*
* @psalm-param class-string $className
* @param class-string $className
*
* @return bool
*/

View File

@@ -88,7 +88,7 @@ requirement.
A more convenient alternative for explicit transaction demarcation is the use
of provided control abstractions in the form of
``Connection#transactional($func)`` and ``EntityManager#transactional($func)``.
``Connection#transactional($func)`` and ``EntityManager#wrapInTransaction($func)``.
When used, these control abstractions ensure that you never forget to rollback
the transaction, in addition to the obvious code reduction. An example that is
functionally equivalent to the previously shown code looks as follows:
@@ -96,21 +96,23 @@ functionally equivalent to the previously shown code looks as follows:
.. code-block:: php
<?php
// transactional with Connection instance
// $conn instanceof Connection
$conn->transactional(function($conn) {
// ... do some work
$user = new User;
$user->setName('George');
});
// transactional with EntityManager instance
// $em instanceof EntityManager
$em->transactional(function($em) {
$em->wrapInTransaction(function($em) {
// ... do some work
$user = new User;
$user->setName('George');
$em->persist($user);
});
.. warning::
For historical reasons, ``EntityManager#transactional($func)`` will return
``true`` whenever the return value of ``$func`` is loosely false.
Some examples of this include ``array()``, ``"0"``, ``""``, ``0``, and
``null``.
The difference between ``Connection#transactional($func)`` and
``EntityManager#transactional($func)`` is that the latter
abstraction flushes the ``EntityManager`` prior to transaction

View File

@@ -338,10 +338,11 @@ Performance of different deletion strategies
Deleting an object with all its associated objects can be achieved
in multiple ways with very different performance impacts.
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
will fetch this association. If its a Single association it will
pass this entity to
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM will
fetch this association. If it's a Single association it will pass
this entity to ``EntityManager#remove()``. If the association is a
collection, Doctrine will loop over all its elements and pass them to
``EntityManager#remove()``.
In both cases the cascade remove semantics are applied recursively.
For large object graphs this removal strategy can be very costly.
2. Using a DQL ``DELETE`` statement allows you to delete multiple

View File

@@ -1,84 +1,77 @@
.. toc::
:orphan:
.. tocheader:: Tutorials
.. toctree::
:caption: Tutorials
:depth: 3
.. toctree::
:depth: 3
tutorials/getting-started
tutorials/getting-started-database
tutorials/getting-started-models
tutorials/working-with-indexed-associations
tutorials/extra-lazy-associations
tutorials/composite-primary-keys
tutorials/ordered-associations
tutorials/override-field-association-mappings-in-subclasses
tutorials/pagination
tutorials/embeddables
tutorials/getting-started
tutorials/getting-started-database
tutorials/getting-started-models
tutorials/working-with-indexed-associations
tutorials/extra-lazy-associations
tutorials/composite-primary-keys
tutorials/ordered-associations
tutorials/override-field-association-mappings-in-subclasses
tutorials/pagination
tutorials/embeddables
.. toctree::
:caption: Reference
:depth: 3
.. toc::
reference/architecture
reference/configuration
reference/faq
reference/basic-mapping
reference/association-mapping
reference/inheritance-mapping
reference/working-with-objects
reference/working-with-associations
reference/typedfieldmapper
reference/events
reference/unitofwork
reference/unitofwork-associations
reference/transactions-and-concurrency
reference/batch-processing
reference/dql-doctrine-query-language
reference/query-builder
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/php-mapping
reference/caching
reference/improving-performance
reference/tools
reference/metadata-drivers
reference/best-practices
reference/limitations-and-known-issues
tutorials/pagination
reference/filters
reference/namingstrategy
reference/advanced-configuration
reference/second-level-cache
reference/security
.. tocheader:: Reference
.. toctree::
:caption: Cookbook
:depth: 3
.. toctree::
:depth: 3
reference/architecture
reference/configuration
reference/faq
reference/basic-mapping
reference/association-mapping
reference/inheritance-mapping
reference/working-with-objects
reference/working-with-associations
reference/typedfieldmapper
reference/events
reference/unitofwork
reference/unitofwork-associations
reference/transactions-and-concurrency
reference/batch-processing
reference/dql-doctrine-query-language
reference/query-builder
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/php-mapping
reference/caching
reference/improving-performance
reference/tools
reference/metadata-drivers
reference/best-practices
reference/limitations-and-known-issues
tutorials/pagination
reference/filters
reference/namingstrategy
reference/advanced-configuration
reference/second-level-cache
reference/security
.. toc::
.. tocheader:: Cookbook
.. toctree::
:depth: 3
cookbook/aggregate-fields
cookbook/custom-mapping-types
cookbook/decorator-pattern
cookbook/dql-custom-walkers
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
cookbook/validation-of-entities
cookbook/working-with-datetime
cookbook/mysql-enums
cookbook/advanced-field-value-conversion-using-custom-mapping-types
cookbook/entities-in-session
cookbook/aggregate-fields
cookbook/custom-mapping-types
cookbook/decorator-pattern
cookbook/dql-custom-walkers
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
cookbook/validation-of-entities
cookbook/working-with-datetime
cookbook/mysql-enums
cookbook/advanced-field-value-conversion-using-custom-mapping-types
cookbook/entities-in-session

View File

@@ -188,7 +188,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
#[OneToMany(targetEntity: ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')]
private Collection $attributes;
public function addAttribute(string $name, ArticleAttribute $value): void
public function addAttribute(string $name, string $value): void
{
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
}

View File

@@ -144,7 +144,7 @@ step:
// Create a simple "default" Doctrine ORM configuration for Attributes
$config = ORMSetup::createAttributeMetadataConfiguration(
paths: array(__DIR__."/src"),
paths: [__DIR__ . '/src'],
isDevMode: true,
);
// or if you prefer annotation, YAML or XML
@@ -153,7 +153,7 @@ step:
// isDevMode: true,
// );
// $config = ORMSetup::createXMLMetadataConfiguration(
// paths: array(__DIR__."/config/xml"),
// paths: [__DIR__ . '/config/xml'],
// isDevMode: true,
//);
// $config = ORMSetup::createYAMLMetadataConfiguration(

View File

@@ -14,7 +14,6 @@
<file>src</file>
<file>tests</file>
<exclude-pattern>*/src/Mapping/InverseJoinColumn.php</exclude-pattern>
<exclude-pattern>*/tests/Tests/Proxies/__CG__*</exclude-pattern>
<exclude-pattern>*/tests/Tests/ORM/Tools/Export/export/*</exclude-pattern>

View File

@@ -165,6 +165,11 @@ parameters:
count: 2
path: src/Mapping/ClassMetadataFactory.php
-
message: "#^Call to an undefined method ReflectionProperty\\:\\:getHooks\\(\\)\\.$#"
count: 1
path: src/Mapping/ClassMetadataInfo.php
-
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#"
count: 2

View File

@@ -61,6 +61,11 @@ parameters:
count: 2
path: src/Mapping/ClassMetadataFactory.php
- '~^Call to deprecated method getSQLResultCasing\(\) of class Doctrine\\DBAL\\Platforms\\AbstractPlatform\.$~'
-
message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ImportCommand\:~'
path: src/Tools/Console/ConsoleRunner.php
# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
@@ -74,3 +79,9 @@ parameters:
-
message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#'
path: src/UnitOfWork.php
-
message: '#contains generic type.*but class.*is not generic#'
paths:
- src/Mapping/Driver/XmlDriver.php
- src/Mapping/Driver/YamlDriver.php

View File

@@ -3,6 +3,8 @@ includes:
- phpstan-params.neon
parameters:
reportUnmatchedIgnoredErrors: false
ignoreErrors:
# deprecations from doctrine/dbal:3.x
- '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getGuidExpression\(\).$/'
@@ -64,3 +66,19 @@ parameters:
# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
-
message: '#contains generic type.*but class.*is not generic#'
paths:
- src/Mapping/Driver/XmlDriver.php
- src/Mapping/Driver/YamlDriver.php
# Extending a deprecated class conditionally to maintain BC
-
message: '~deprecated class Doctrine\\Persistence\\Mapping\\Driver\\AnnotationDriver\:~'
path: src/Mapping/Driver/CompatibilityAnnotationDriver.php
# We're sniffing for this deprecated class in order to detect Persistence 2
-
message: '~deprecated class Doctrine\\Common\\Persistence\\PersistentObject\:~'
path: src/EntityManager.php

View File

@@ -305,7 +305,7 @@
<code><![CDATA[$persister->loadById($sortedId)]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[?T]]></code>
<code><![CDATA[T|null]]></code>
</InvalidReturnType>
<MissingReturnType>
<code><![CDATA[wrapInTransaction]]></code>
@@ -713,6 +713,9 @@
<code><![CDATA[joinColumnName]]></code>
<code><![CDATA[joinColumnName]]></code>
</TooManyArguments>
<UndefinedMethod>
<code><![CDATA[getHooks]]></code>
</UndefinedMethod>
</file>
<file src="src/Mapping/ColumnResult.php">
<MissingConstructor>
@@ -932,13 +935,8 @@
<InvalidPropertyAssignmentValue>
<code><![CDATA[$metadata->table]]></code>
</InvalidPropertyAssignmentValue>
<InvalidPropertyFetch>
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
</InvalidPropertyFetch>
<InvalidReturnStatement>
<code><![CDATA[$mapping]]></code>
<code><![CDATA[$result]]></code>
<code><![CDATA[[
'usage' => $usage,
'region' => $region,
@@ -962,7 +960,6 @@
* options?: array
* }]]></code>
<code><![CDATA[array{usage: int|null, region?: string}]]></code>
<code><![CDATA[loadMappingFile]]></code>
</InvalidReturnType>
<MissingParamType>
<code><![CDATA[$fileExtension]]></code>
@@ -971,15 +968,6 @@
<MoreSpecificImplementedParamType>
<code><![CDATA[$metadata]]></code>
</MoreSpecificImplementedParamType>
<NoInterfaceProperties>
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
</NoInterfaceProperties>
<TypeDoesNotContainType>
<code><![CDATA[$xmlRoot->getName() === 'embeddable']]></code>
<code><![CDATA[$xmlRoot->getName() === 'entity']]></code>
<code><![CDATA[$xmlRoot->getName() === 'mapped-superclass']]></code>
</TypeDoesNotContainType>
</file>
<file src="src/Mapping/Driver/YamlDriver.php">
<ArgumentTypeCoercion>
@@ -1011,38 +999,6 @@
<MoreSpecificReturnType>
<code><![CDATA[array{usage: int|null, region: string|null}]]></code>
</MoreSpecificReturnType>
<PossiblyUndefinedMethod>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
</PossiblyUndefinedMethod>
<UndefinedInterfaceMethod>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
<code><![CDATA[$element]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="src/Mapping/Embedded.php">
<MissingParamType>
@@ -1494,7 +1450,9 @@
<code><![CDATA[__wakeup]]></code>
</UndefinedInterfaceMethod>
<UndefinedMethod>
<code><![CDATA[self::createLazyGhost($initializer, $skippedProperties)]]></code>
<code><![CDATA[self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
$initializer($object, $identifier);
}, $skippedProperties)]]></code>
</UndefinedMethod>
<UnresolvableInclude>
<code><![CDATA[require $fileName]]></code>
@@ -1910,6 +1868,12 @@
<code><![CDATA[$this->_sqlStatements = &$this->sqlStatements]]></code>
</UnsupportedPropertyReferenceUsage>
</file>
<file src="src/Query/Exec/FinalizedSelectExecutor.php">
<PropertyNotSetInConstructor>
<code><![CDATA[FinalizedSelectExecutor]]></code>
<code><![CDATA[FinalizedSelectExecutor]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="src/Query/Exec/MultiTableDeleteExecutor.php">
<InvalidReturnStatement>
<code><![CDATA[$numDeleted]]></code>
@@ -2038,6 +2002,9 @@
<ArgumentTypeCoercion>
<code><![CDATA[$stringPattern]]></code>
</ArgumentTypeCoercion>
<DeprecatedMethod>
<code><![CDATA[setSqlExecutor]]></code>
</DeprecatedMethod>
<InvalidNullableReturnType>
<code><![CDATA[AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement]]></code>
</InvalidNullableReturnType>
@@ -2099,11 +2066,6 @@
<code><![CDATA[$token === TokenType::T_IDENTIFIER]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="src/Query/ParserResult.php">
<PropertyNotSetInConstructor>
<code><![CDATA[$sqlExecutor]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="src/Query/QueryExpressionVisitor.php">
<InvalidReturnStatement>
<code><![CDATA[new ArrayCollection($this->parameters)]]></code>

View File

@@ -558,9 +558,11 @@ abstract class AbstractQuery
// DBAL 2
if (! method_exists(QueryCacheProfile::class, 'setResultCache')) {
// @phpstan-ignore method.deprecated
if (! $profile->getResultCacheDriver()) {
$defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache();
if ($defaultHydrationCacheImpl) {
// @phpstan-ignore method.deprecated
$profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultHydrationCacheImpl));
}
}
@@ -609,9 +611,11 @@ abstract class AbstractQuery
// DBAL 2
if (! method_exists(QueryCacheProfile::class, 'setResultCache')) {
// @phpstan-ignore method.deprecated
if (! $profile->getResultCacheDriver()) {
$defaultResultCacheDriver = $this->_em->getConfiguration()->getResultCache();
if ($defaultResultCacheDriver) {
// @phpstan-ignore method.deprecated
$profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultResultCacheDriver));
}
}
@@ -677,6 +681,7 @@ abstract class AbstractQuery
$resultCacheDriver = DoctrineProvider::wrap($resultCache);
$this->_queryCacheProfile = $this->_queryCacheProfile
// @phpstan-ignore method.deprecated
? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
: new QueryCacheProfile(0, null, $resultCacheDriver);
@@ -780,6 +785,7 @@ abstract class AbstractQuery
// Compatibility for DBAL 2
if (! method_exists($this->_queryCacheProfile, 'setResultCache')) {
// @phpstan-ignore method.deprecated
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver(DoctrineProvider::wrap($cache));
return $this;
@@ -1235,6 +1241,7 @@ abstract class AbstractQuery
// Support for DBAL 2
if (! method_exists($this->_hydrationCacheProfile, 'getResultCache')) {
// @phpstan-ignore method.deprecated
$cacheDriver = $this->_hydrationCacheProfile->getResultCacheDriver();
assert($cacheDriver !== null);

View File

@@ -21,15 +21,13 @@ class AssociationCacheEntry implements CacheEntry
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string
* @psalm-var class-string
* @var class-string
*/
public $class;
/**
* @param string $class The entity class.
* @param class-string $class The entity class.
* @param array<string, mixed> $identifier The entity identifier.
* @psalm-param class-string $class
*/
public function __construct($class, array $identifier)
{

View File

@@ -26,8 +26,7 @@ class CollectionCacheKey extends CacheKey
* The owner entity class
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string
* @psalm-var class-string
* @var class-string
*/
public $entityClass;
@@ -40,10 +39,9 @@ class CollectionCacheKey extends CacheKey
public $association;
/**
* @param string $entityClass The entity class.
* @param class-string $entityClass The entity class.
* @param string $association The field name that represents the association.
* @param array<string, mixed> $ownerIdentifier The identifier of the owning entity.
* @psalm-param class-string $entityClass
*/
public function __construct($entityClass, $association, array $ownerIdentifier)
{

View File

@@ -25,15 +25,13 @@ class EntityCacheEntry implements CacheEntry
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string
* @psalm-var class-string
* @var class-string
*/
public $class;
/**
* @param string $class The entity class.
* @param class-string $class The entity class.
* @param array<string,mixed> $data The entity data.
* @psalm-param class-string $class
*/
public function __construct($class, array $data)
{

View File

@@ -26,15 +26,13 @@ class EntityCacheKey extends CacheKey
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string
* @psalm-var class-string
* @var class-string
*/
public $entityClass;
/**
* @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param class-string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param array<string, mixed> $identifier The entity identifier
* @psalm-param class-string $entityClass
*/
public function __construct($entityClass, array $identifier)
{

View File

@@ -254,9 +254,8 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/**
* @deprecated This method is not used anymore.
*
* @param string $targetEntity
* @param object $element
* @psalm-param class-string $targetEntity
* @param class-string $targetEntity
* @param object $element
*
* @return void
*/

View File

@@ -8,6 +8,8 @@ use Doctrine\ORM\Cache\Exception\CacheException;
/**
* Defines a contract for accessing a particular named region.
*
* @phpstan-ignore interface.extendsDeprecatedInterface
*/
interface Region extends MultiGetRegion
{

View File

@@ -72,6 +72,7 @@ class DefaultRegion implements Region
CacheItemPoolInterface::class
);
// @phpstan-ignore property.deprecated
$this->cache = $cacheItemPool;
$this->cacheItemPool = CacheAdapter::wrap($cacheItemPool);
} elseif (! $cacheItemPool instanceof CacheItemPoolInterface) {
@@ -82,6 +83,7 @@ class DefaultRegion implements Region
get_debug_type($cacheItemPool)
));
} else {
// @phpstan-ignore property.deprecated
$this->cache = DoctrineProvider::wrap($cacheItemPool);
$this->cacheItemPool = $cacheItemPool;
}

View File

@@ -269,6 +269,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
);
if (! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) {
// @phpstan-ignore staticMethod.deprecatedClass
throw UnknownEntityNamespace::fromNamespaceAlias($entityNamespaceAlias);
}
@@ -314,6 +315,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
// Compatibility with DBAL 2
if (! method_exists(parent::class, 'getResultCache')) {
// @phpstan-ignore method.deprecated
$cacheImpl = $this->getResultCacheImpl();
return $cacheImpl ? CacheAdapter::wrap($cacheImpl) : null;
@@ -329,6 +331,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
// Compatibility with DBAL 2
if (! method_exists(parent::class, 'setResultCache')) {
// @phpstan-ignore method.deprecated
$this->setResultCacheImpl(DoctrineProvider::wrap($cache));
return;
@@ -684,8 +687,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name
*
* @return string|callable|null
* @psalm-return class-string|callable|null
* @return class-string|callable|null
*/
public function getCustomNumericFunction($name)
{
@@ -702,7 +704,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* Any previously added numeric functions are discarded.
*
* @psalm-param array<string, class-string> $functions The map of custom
* @param array<string, class-string> $functions The map of custom
* DQL numeric functions.
*
* @return void
@@ -737,8 +739,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name
*
* @return string|callable|null
* @psalm-return class-string|callable|null
* @return class-string|callable|null
*/
public function getCustomDatetimeFunction($name)
{
@@ -804,8 +805,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $modeName The hydration mode name.
*
* @return string|null The hydrator class name.
* @psalm-return class-string<AbstractHydrator>|null
* @return class-string<AbstractHydrator>|null The hydrator class name.
*/
public function getCustomHydrationMode($modeName)
{
@@ -815,9 +815,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Adds a custom hydration mode.
*
* @param string $modeName The hydration mode name.
* @param string $hydrator The hydrator class name.
* @psalm-param class-string<AbstractHydrator> $hydrator
* @param string $modeName The hydration mode name.
* @param class-string<AbstractHydrator> $hydrator The hydrator class name.
*
* @return void
*/
@@ -829,8 +828,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets a class metadata factory.
*
* @param string $cmfName
* @psalm-param class-string $cmfName
* @param class-string $cmfName
*
* @return void
*/
@@ -839,10 +837,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['classMetadataFactoryName'] = $cmfName;
}
/**
* @return string
* @psalm-return class-string
*/
/** @return class-string */
public function getClassMetadataFactoryName()
{
if (! isset($this->_attributes['classMetadataFactoryName'])) {
@@ -855,9 +850,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Adds a filter to the list of possible filters.
*
* @param string $name The name of the filter.
* @param string $className The class name of the filter.
* @psalm-param class-string<SQLFilter> $className
* @param string $name The name of the filter.
* @param class-string<SQLFilter> $className The class name of the filter.
*
* @return void
*/
@@ -871,9 +865,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name The name of the filter.
*
* @return string|null The class name of the filter, or null if it is not
* defined.
* @psalm-return class-string<SQLFilter>|null
* @return class-string<SQLFilter>|null The class name of the filter,
* or null if it is not defined.
*/
public function getFilterClassName($name)
{
@@ -883,8 +876,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets default repository class.
*
* @param string $className
* @psalm-param class-string<EntityRepository> $className
* @param class-string<EntityRepository> $className
*
* @return void
*
@@ -912,8 +904,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Get default repository class.
*
* @return string
* @psalm-return class-string<EntityRepository>
* @return class-string<EntityRepository>
*/
public function getDefaultRepositoryClassName()
{

View File

@@ -50,7 +50,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @param class-string<T> $className
*
* @psalm-return EntityRepository<T>
*
@@ -96,6 +96,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
E_USER_NOTICE
);
// @phpstan-ignore method.deprecated
return $this->wrapped->transactional($func);
}

View File

@@ -30,9 +30,7 @@ use Doctrine\ORM\Query\FilterCollection;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\Mapping\MappingException;
use Doctrine\Persistence\ObjectRepository;
use InvalidArgumentException;
use Throwable;
use function array_keys;
use function class_exists;
@@ -162,8 +160,9 @@ class EntityManager implements EntityManagerInterface
throw MissingMappingDriverImplementation::create();
}
$this->conn = $conn;
$this->config = $config;
$this->conn = $conn;
$this->config = $config;
// @phpstan-ignore method.deprecated
$this->eventManager = $eventManager ?? $conn->getEventManager();
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
@@ -246,18 +245,24 @@ class EntityManager implements EntityManagerInterface
$this->conn->beginTransaction();
$successful = false;
try {
$return = $func($this);
$this->flush();
$this->conn->commit();
return $return ?: true;
} catch (Throwable $e) {
$this->close();
$this->conn->rollBack();
$successful = true;
throw $e;
return $return ?: true;
} finally {
if (! $successful) {
$this->close();
if ($this->conn->isTransactionActive()) {
$this->conn->rollBack();
}
}
}
}
@@ -268,18 +273,24 @@ class EntityManager implements EntityManagerInterface
{
$this->conn->beginTransaction();
$successful = false;
try {
$return = $func($this);
$this->flush();
$this->conn->commit();
return $return;
} catch (Throwable $e) {
$this->close();
$this->conn->rollBack();
$successful = true;
throw $e;
return $return;
} finally {
if (! $successful) {
$this->close();
if ($this->conn->isTransactionActive()) {
$this->conn->rollBack();
}
}
}
}
@@ -406,25 +417,23 @@ class EntityManager implements EntityManagerInterface
/**
* Finds an Entity by its identifier.
*
* @param string $className The class name of the entity to find.
* @param mixed $id The identity of the entity to find.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* during the search.
* @param int|null $lockVersion The version of the entity to find when using
* optimistic locking.
* @psalm-param class-string<T> $className
* @param class-string<T> $className The class name of the entity to find.
* @param mixed $id The identity of the entity to find.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* during the search.
* @param int|null $lockVersion The version of the entity to find when using
* optimistic locking.
* @psalm-param LockMode::*|null $lockMode
*
* @return object|null The entity instance or NULL if the entity can not be found.
* @psalm-return ?T
* @return T|null The entity instance or NULL if the entity can not be found.
*
* @throws OptimisticLockException
* @throws ORMInvalidArgumentException
* @throws TransactionRequiredException
* @throws ORMException
*
* @template T
* @template T of object
*/
public function find($className, $id, $lockMode = null, $lockVersion = null)
{
@@ -615,6 +624,7 @@ class EntityManager implements EntityManagerInterface
public function clear($entityName = null)
{
if ($entityName !== null && ! is_string($entityName)) {
// @phpstan-ignore staticMethod.deprecated
throw ORMInvalidArgumentException::invalidEntityName($entityName);
}
@@ -801,11 +811,9 @@ class EntityManager implements EntityManagerInterface
/**
* Gets the repository for an entity class.
*
* @param string $entityName The name of the entity.
* @psalm-param class-string<T> $entityName
* @param class-string<T> $entityName The name of the entity.
*
* @return ObjectRepository|EntityRepository The repository class.
* @psalm-return EntityRepository<T>
* @return EntityRepository<T> The repository class.
*
* @template T of object
*/
@@ -1109,6 +1117,7 @@ class EntityManager implements EntityManagerInterface
private function configureLegacyMetadataCache(): void
{
// @phpstan-ignore method.deprecated
$metadataCache = $this->config->getMetadataCacheImpl();
if (! $metadataCache) {
return;

View File

@@ -29,9 +29,9 @@ interface EntityManagerInterface extends ObjectManager
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @param class-string<T> $className
*
* @psalm-return EntityRepository<T>
* @return EntityRepository<T>
*
* @template T of object
*/
@@ -172,12 +172,10 @@ interface EntityManagerInterface extends ObjectManager
* Gets a reference to the entity identified by the given type and identifier
* without actually loading it, if the entity is not yet loaded.
*
* @param string $entityName The name of the entity type.
* @param mixed $id The entity identifier.
* @psalm-param class-string<T> $entityName
* @param class-string<T> $entityName The name of the entity type.
* @param mixed $id The entity identifier.
*
* @return object|null The entity reference.
* @psalm-return T|null
* @return T|null The entity reference.
*
* @throws ORMException
*
@@ -202,12 +200,10 @@ interface EntityManagerInterface extends ObjectManager
*
* @deprecated 2.7 This method is being removed from the ORM and won't have any replacement
*
* @param string $entityName The name of the entity type.
* @param mixed $identifier The entity identifier.
* @psalm-param class-string<T> $entityName
* @param class-string<T> $entityName The name of the entity type.
* @param mixed $identifier The entity identifier.
*
* @return object|null The (partial) entity reference
* @psalm-return T|null
* @return T|null The (partial) entity reference
*
* @template T
*/
@@ -337,7 +333,7 @@ interface EntityManagerInterface extends ObjectManager
/**
* {@inheritDoc}
*
* @psalm-param string|class-string<T> $className
* @param string|class-string<T> $className
*
* @return Mapping\ClassMetadata
* @psalm-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)

View File

@@ -42,8 +42,7 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* @internal This property will be private in 3.0, call {@see getEntityName()} instead.
*
* @var string
* @psalm-var class-string<T>
* @var class-string<T>
*/
protected $_entityName;
@@ -287,10 +286,7 @@ class EntityRepository implements ObjectRepository, Selectable
));
}
/**
* @return string
* @psalm-return class-string<T>
*/
/** @return class-string<T> */
protected function getEntityName()
{
return $this->_entityName;

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PostLoadEventArgs extends LifecycleEventArgs
{
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PostPersistEventArgs extends LifecycleEventArgs
{
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PostRemoveEventArgs extends LifecycleEventArgs
{
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PostUpdateEventArgs extends LifecycleEventArgs
{
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PrePersistEventArgs extends LifecycleEventArgs
{
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
/** @phpstan-ignore class.extendsDeprecatedClass */
final class PreRemoveEventArgs extends LifecycleEventArgs
{
}

View File

@@ -13,6 +13,8 @@ use function sprintf;
/**
* Class that holds event arguments for a preUpdate event.
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class PreUpdateEventArgs extends LifecycleEventArgs
{
@@ -26,6 +28,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
*/
public function __construct($entity, EntityManagerInterface $em, array &$changeSet)
{
// @phpstan-ignore staticMethod.deprecatedClass
parent::__construct($entity, $em);
$this->entityChangeSet = &$changeSet;

View File

@@ -8,6 +8,8 @@ use Doctrine\ORM\ORMException as BaseORMException;
/**
* Should become an interface in 3.0
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class ORMException extends BaseORMException
{

View File

@@ -73,6 +73,7 @@ abstract class AbstractIdGenerator
throw new InvalidArgumentException('Unsupported entity manager implementation.');
}
// @phpstan-ignore method.deprecated
return $this->generate($em, $entity);
}

View File

@@ -22,6 +22,7 @@ trait CriteriaOrderings
private static function getCriteriaOrderings(Criteria $criteria): array
{
if (! method_exists(Criteria::class, 'orderings')) {
// @phpstan-ignore method.deprecated
return $criteria->getOrderings();
}

View File

@@ -273,7 +273,7 @@ class ObjectHydrator extends AbstractHydrator
}
/**
* @psalm-param class-string $className
* @param class-string $className
* @psalm-param array<string, mixed> $data
*
* @return mixed
@@ -367,11 +367,15 @@ class ObjectHydrator extends AbstractHydrator
$parentObject = $this->resultPointers[$parentAlias];
} else {
// Parent object of relation not found, mark as not-fetched again
$element = $this->getEntity($data, $dqlAlias);
if (isset($nonemptyComponents[$dqlAlias])) {
$element = $this->getEntity($data, $dqlAlias);
// Update result pointer and provide initial fetch data for parent
$this->resultPointers[$dqlAlias] = $element;
$rowData['data'][$parentAlias][$relationField] = $element;
// Update result pointer and provide initial fetch data for parent
$this->resultPointers[$dqlAlias] = $element;
$rowData['data'][$parentAlias][$relationField] = $element;
} else {
$element = null;
}
// Mark as not-fetched again
unset($this->_hints['fetched'][$parentAlias][$relationField]);

View File

@@ -172,6 +172,8 @@ class ClassMetadataBuilder
/**
* Adds named query.
*
* @deprecated
*
* @param string $name
* @param string $dqlQuery
*
@@ -216,11 +218,11 @@ class ClassMetadataBuilder
/**
* Sets the discriminator column details.
*
* @param string $name
* @param string $type
* @param int $length
* @psalm-param class-string<BackedEnum>|null $enumType
* @psalm-param array<string, mixed> $options
* @param string $name
* @param string $type
* @param int $length
* @param class-string<BackedEnum>|null $enumType
* @param array<string, mixed> $options
*
* @return $this
*/

View File

@@ -420,7 +420,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
/**
* Gets the lower-case short name of a class.
*
* @psalm-param class-string $className
* @param class-string $className
*/
private function getShortName(string $className): string
{
@@ -558,6 +558,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
{
foreach ($parentClass->namedQueries as $name => $query) {
if (! isset($subClass->namedQueries[$name])) {
// @phpstan-ignore method.deprecated
$subClass->addNamedQuery(
[
'name' => $query['name'],
@@ -575,6 +576,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
{
foreach ($parentClass->namedNativeQueries as $name => $query) {
if (! isset($subClass->namedNativeQueries[$name])) {
// @phpstan-ignore method.deprecated
$subClass->addNamedNativeQuery(
[
'name' => $query['name'],
@@ -637,7 +639,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
$platform = $this->getTargetPlatform();
// Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
/** @psalm-suppress UndefinedClass, InvalidClass */
/** @psalm-suppress UndefinedClass, InvalidClass */ // @phpstan-ignore method.deprecated
if (! $platform instanceof MySQLPlatform && ! $platform instanceof SqlitePlatform && ! $platform instanceof SQLServerPlatform && $platform->usesSequenceEmulatedIdentityColumns()) {
Deprecation::trigger(
'doctrine/orm',
@@ -654,8 +656,9 @@ DEPRECATION
$columnName = $class->getSingleIdentifierColumnName();
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
$sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform());
$sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName);
$definition = [
// @phpstan-ignore method.deprecated
$sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName);
$definition = [
'sequenceName' => $this->truncateSequenceName($sequenceName),
];
@@ -711,6 +714,7 @@ DEPRECATION
$class->setIdGenerator(new AssignedGenerator());
break;
// @phpstan-ignore classConstant.deprecated
case ClassMetadata::GENERATOR_TYPE_UUID:
Deprecation::trigger(
'doctrine/orm',
@@ -718,6 +722,7 @@ DEPRECATION
'Mapping for %s: the "UUID" id generator strategy is deprecated with no replacement',
$class->name
);
// @phpstan-ignore new.deprecated
$class->setIdGenerator(new UuidGenerator());
break;
@@ -845,8 +850,10 @@ DEPRECATION
*/
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
{
/** @psalm-var class-string */
return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
/** @var class-string $classString */
$classString = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
return $classString;
}
/**

View File

@@ -257,8 +257,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The name of the entity class.
*
* @var string
* @psalm-var class-string<T>
* @var class-string<T>
*/
public $name;
@@ -275,8 +274,7 @@ class ClassMetadataInfo implements ClassMetadata
* hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
* as {@link $name}.
*
* @var string
* @psalm-var class-string
* @var class-string
*/
public $rootEntityName;
@@ -300,8 +298,7 @@ class ClassMetadataInfo implements ClassMetadata
* The name of the custom repository class used for the entity class.
* (Optional).
*
* @var string|null
* @psalm-var ?class-string<EntityRepository>
* @var class-string<EntityRepository>|null
*/
public $customRepositoryClassName;
@@ -323,7 +320,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The names of the parent <em>entity</em> classes (ancestors), starting with the
* nearest one and ending with the root entity class.
*
* @psalm-var list<class-string>
* @var list<class-string>
*/
public $parentClasses = [];
@@ -350,7 +347,7 @@ class ClassMetadataInfo implements ClassMetadata
* For subclasses of such root entities, the list can be reused/passed downwards, it only needs to
* be filtered accordingly (only keep remaining subclasses)
*
* @psalm-var list<class-string>
* @var list<class-string>
*/
public $subClasses = [];
@@ -548,9 +545,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @see discriminatorColumn
*
* @var array<int|string, string>
*
* @psalm-var array<int|string, class-string>
* @var array<int|string, class-string>
*/
public $discriminatorMap = [];
@@ -811,8 +806,7 @@ class ClassMetadataInfo implements ClassMetadata
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
*
* @param string $entityName The name of the entity class the new instance is used for.
* @psalm-param class-string<T> $entityName
* @param class-string<T> $entityName The name of the entity class the new instance is used for.
*/
public function __construct($entityName, ?NamingStrategy $namingStrategy = null, ?TypedFieldMapper $typedFieldMapper = null)
{
@@ -1405,6 +1399,7 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getColumnName($fieldName)
{
// @phpstan-ignore property.deprecated
return $this->columnNames[$fieldName] ?? $fieldName;
}
@@ -1659,6 +1654,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['quoted'] = true;
}
// @phpstan-ignore property.deprecated
$this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn && $this->discriminatorColumn['name'] === $mapping['columnName'])) {
@@ -1683,6 +1679,7 @@ class ClassMetadataInfo implements ClassMetadata
}
}
// @phpstan-ignore method.deprecated
if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
if (isset($mapping['id']) && $mapping['id'] === true) {
throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
@@ -2423,7 +2420,7 @@ class ClassMetadataInfo implements ClassMetadata
* Assumes that the class names in the passed array are in the order:
* directParent -> directParentParent -> directParentParentParent ... -> root.
*
* @psalm-param list<class-string> $classNames
* @param list<class-string> $classNames
*
* @return void
*/
@@ -2576,6 +2573,7 @@ class ClassMetadataInfo implements ClassMetadata
unset($this->fieldMappings[$fieldName]);
unset($this->fieldNames[$mapping['columnName']]);
// @phpstan-ignore property.deprecated
unset($this->columnNames[$mapping['fieldName']]);
$overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping);
@@ -2699,6 +2697,7 @@ class ClassMetadataInfo implements ClassMetadata
*/
private function isInheritanceType(int $type): bool
{
// @phpstan-ignore classConstant.deprecated
if ($type === self::INHERITANCE_TYPE_TABLE_PER_CLASS) {
Deprecation::trigger(
'doctrine/orm',
@@ -2710,6 +2709,7 @@ class ClassMetadataInfo implements ClassMetadata
return $type === self::INHERITANCE_TYPE_NONE ||
$type === self::INHERITANCE_TYPE_SINGLE_TABLE ||
$type === self::INHERITANCE_TYPE_JOINED ||
// @phpstan-ignore classConstant.deprecated
$type === self::INHERITANCE_TYPE_TABLE_PER_CLASS;
}
@@ -2766,8 +2766,9 @@ class ClassMetadataInfo implements ClassMetadata
public function addInheritedFieldMapping(array $fieldMapping)
{
$this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
$this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
// @phpstan-ignore property.deprecated
$this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
if (isset($fieldMapping['generated'])) {
$this->requiresFetchAfterChange = true;
@@ -3017,8 +3018,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Registers a custom repository class for the entity class.
*
* @param string|null $repositoryClassName The class name of the custom mapper.
* @psalm-param class-string<EntityRepository>|null $repositoryClassName
* @param class-string<EntityRepository>|null $repositoryClassName The class name of the custom mapper.
*
* @return void
*/
@@ -3555,8 +3555,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $assocName
*
* @return string
* @psalm-return class-string
* @return class-string
*
* @throws InvalidArgumentException
*/
@@ -3859,6 +3858,7 @@ class ClassMetadataInfo implements ClassMetadata
if ($schemaName) {
$sequencePrefix = $schemaName . '.' . $tableName;
// @phpstan-ignore method.deprecated
if (! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
$sequencePrefix = $schemaName . '__' . $tableName;
}
@@ -3875,7 +3875,7 @@ class ClassMetadataInfo implements ClassMetadata
}
}
/** @psalm-param class-string $class */
/** @param class-string $class */
private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ?ReflectionProperty
{
$reflectionProperty = $reflService->getAccessibleProperty($class, $field);
@@ -3890,6 +3890,10 @@ class ClassMetadataInfo implements ClassMetadata
}
}
if (PHP_VERSION_ID >= 80400 && $reflectionProperty !== null && count($reflectionProperty->getHooks()) > 0) {
throw new LogicException('Doctrine ORM does not support property hooks in this version. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.');
}
return $reflectionProperty;
}
}

View File

@@ -17,7 +17,7 @@ use function trim;
*/
class DefaultEntityListenerResolver implements EntityListenerResolver
{
/** @psalm-var array<class-string, object> Map to store entity listener instances. */
/** @var array<class-string, object> Map to store entity listener instances. */
private $instances = [];
/**

View File

@@ -42,6 +42,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
if (! empty($class->table['schema'])) {
$tableName = $class->table['schema'] . '.' . $class->table['name'];
// @phpstan-ignore method.deprecated
if (! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
$tableName = $class->table['schema'] . '__' . $class->table['name'];
}
@@ -90,7 +91,8 @@ class DefaultQuoteStrategy implements QuoteStrategy
$schema = '';
if (isset($association['joinTable']['schema'])) {
$schema = $association['joinTable']['schema'];
$schema = $association['joinTable']['schema'];
// @phpstan-ignore method.deprecated
$schema .= ! $platform->supportsSchemas() && $platform->canEmulateSchemas() ? '__' : '.';
}

View File

@@ -47,10 +47,7 @@ class AnnotationDriver extends CompatibilityAnnotationDriver
*/
protected $reader;
/**
* @var int[]
* @psalm-var array<class-string, int>
*/
/** @var array<class-string, int> */
protected $entityAnnotationClasses = [
Mapping\Entity::class => 1,
Mapping\MappedSuperclass::class => 2,
@@ -89,8 +86,8 @@ class AnnotationDriver extends CompatibilityAnnotationDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @template T of object
*/

View File

@@ -65,6 +65,7 @@ class AttributeDriver extends CompatibilityAnnotationDriver
$this->reader = new AttributeReader();
$this->addPaths($paths);
// @phpstan-ignore property.deprecated
if ($this->entityAnnotationClasses !== self::ENTITY_ATTRIBUTE_CLASSES) {
Deprecation::trigger(
'doctrine/orm',
@@ -114,6 +115,7 @@ class AttributeDriver extends CompatibilityAnnotationDriver
foreach ($classAttributes as $a) {
$attr = $a instanceof RepeatableAttributeCollection ? $a[0] : $a;
// @phpstan-ignore property.deprecated
if (isset($this->entityAnnotationClasses[get_class($attr)])) {
return false;
}
@@ -125,8 +127,8 @@ class AttributeDriver extends CompatibilityAnnotationDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @template T of object
*/

View File

@@ -185,8 +185,8 @@ class DatabaseDriver implements MappingDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @template T of object
*/
@@ -533,7 +533,7 @@ class DatabaseDriver implements MappingDriver
/**
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
*
* @psalm-return class-string
* @return class-string
*/
private function getClassNameForTable(string $tableName): string
{

View File

@@ -10,6 +10,8 @@ use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator;
* YamlDriver that additionally looks for mapping information in a global file.
*
* @deprecated This class is being removed from the ORM and won't have any replacement
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class SimplifiedYamlDriver extends YamlDriver
{

View File

@@ -37,6 +37,8 @@ use function strtoupper;
* XmlDriver is a metadata driver that enables mapping through XML files.
*
* @link www.doctrine-project.org
*
* @template-extends FileDriver<SimpleXMLElement>
*/
class XmlDriver extends FileDriver
{
@@ -71,15 +73,14 @@ class XmlDriver extends FileDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$xmlRoot = $this->getElement($className);
assert($xmlRoot instanceof SimpleXMLElement);
if ($xmlRoot->getName() === 'entity') {
if (isset($xmlRoot['repository-class'])) {
@@ -121,6 +122,7 @@ class XmlDriver extends FileDriver
// Evaluate named queries
if (isset($xmlRoot->{'named-queries'})) {
foreach ($xmlRoot->{'named-queries'}->{'named-query'} ?? [] as $namedQueryElement) {
// @phpstan-ignore method.deprecated
$metadata->addNamedQuery(
[
'name' => (string) $namedQueryElement['name'],
@@ -133,6 +135,7 @@ class XmlDriver extends FileDriver
// Evaluate native named queries
if (isset($xmlRoot->{'named-native-queries'})) {
foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} ?? [] as $nativeQueryElement) {
// @phpstan-ignore method.deprecated
$metadata->addNamedNativeQuery(
[
'name' => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
@@ -203,6 +206,7 @@ class XmlDriver extends FileDriver
];
if (isset($discrColumn['options'])) {
assert($discrColumn['options'] instanceof SimpleXMLElement);
$columnDef['options'] = $this->parseOptions($discrColumn['options']->children());
}
@@ -214,6 +218,7 @@ class XmlDriver extends FileDriver
// Evaluate <discriminator-map...>
if (isset($xmlRoot->{'discriminator-map'})) {
$map = [];
assert($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} instanceof SimpleXMLElement);
foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
$map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
}
@@ -486,6 +491,7 @@ class XmlDriver extends FileDriver
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
// @phpstan-ignore classConstant.deprecated
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
}
@@ -615,6 +621,7 @@ class XmlDriver extends FileDriver
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
// @phpstan-ignore classConstant.deprecated
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
}
@@ -964,19 +971,19 @@ class XmlDriver extends FileDriver
if (isset($xmlElement->entity)) {
foreach ($xmlElement->entity as $entityElement) {
/** @psalm-var class-string $entityName */
/** @var class-string $entityName */
$entityName = (string) $entityElement['name'];
$result[$entityName] = $entityElement;
}
} elseif (isset($xmlElement->{'mapped-superclass'})) {
foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
/** @psalm-var class-string $className */
/** @var class-string $className */
$className = (string) $mappedSuperClass['name'];
$result[$className] = $mappedSuperClass;
}
} elseif (isset($xmlElement->embeddable)) {
foreach ($xmlElement->embeddable as $embeddableElement) {
/** @psalm-var class-string $embeddableName */
/** @var class-string $embeddableName */
$embeddableName = (string) $embeddableElement['name'];
$result[$embeddableName] = $embeddableElement;
}

View File

@@ -31,6 +31,8 @@ use function substr;
* The YamlDriver reads the mapping metadata from yaml schema files.
*
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
*
* @template-extends FileDriver<array<string, mixed>>
*/
class YamlDriver extends FileDriver
{
@@ -61,8 +63,8 @@ class YamlDriver extends FileDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
* @param class-string<T> $className
* @param ClassMetadata<T> $metadata
*
* @template T of object
*/

View File

@@ -18,8 +18,7 @@ use Doctrine\ORM\EntityRepository;
final class Entity implements MappingAttribute
{
/**
* @var string|null
* @psalm-var class-string<EntityRepository<T>>|null
* @var class-string<EntityRepository<T>>|null
* @readonly
*/
public $repositoryClass;

View File

@@ -2,7 +2,6 @@
declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use Attribute;

View File

@@ -17,13 +17,12 @@ use Doctrine\ORM\EntityRepository;
final class MappedSuperclass implements MappingAttribute
{
/**
* @var string|null
* @psalm-var class-string<EntityRepository>|null
* @var class-string<EntityRepository>|null
* @readonly
*/
public $repositoryClass;
/** @psalm-param class-string<EntityRepository>|null $repositoryClass */
/** @param class-string<EntityRepository>|null $repositoryClass */
public function __construct(?string $repositoryClass = null)
{
$this->repositoryClass = $repositoryClass;

View File

@@ -4,7 +4,11 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
/** A marker interface for mapping attributes. */
/**
* A marker interface for mapping attributes.
*
* @phpstan-ignore interface.extendsDeprecatedInterface
*/
interface MappingAttribute extends Annotation
{
}

View File

@@ -33,8 +33,7 @@ final class ReflectionPropertiesGetter
}
/**
* @param string $className
* @psalm-param class-string $className
* @param class-string $className
*
* @return ReflectionProperty[] indexed by property internal name
*/
@@ -57,7 +56,7 @@ final class ReflectionPropertiesGetter
}
/**
* @psalm-param class-string $className
* @param class-string $className
*
* @return ReflectionClass[]
* @psalm-return list<ReflectionClass<object>>

View File

@@ -50,7 +50,15 @@ class NativeQuery extends AbstractQuery
$types = [];
foreach ($this->getParameters() as $parameter) {
$name = $parameter->getName();
$name = $parameter->getName();
if ($parameter->typeWasSpecified()) {
$parameters[$name] = $parameter->getValue();
$types[$name] = $parameter->getType();
continue;
}
$value = $this->processParameterValue($parameter->getValue());
$type = $parameter->getValue() === $value
? $parameter->getType()

View File

@@ -8,13 +8,18 @@ use BadMethodCallException;
use Doctrine\Common\Collections\Criteria;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityNotFoundException;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Utility\PersisterHelper;
use function array_fill;
use function array_keys;
use function array_merge;
use function array_reverse;
use function array_values;
use function assert;
use function count;
use function implode;
use function is_int;
use function is_string;
@@ -166,7 +171,11 @@ class OneToManyPersister extends AbstractCollectionPersister
throw new BadMethodCallException('Filtering a collection by Criteria is not supported by this CollectionPersister.');
}
/** @throws DBALException */
/**
* @throws DBALException
* @throws EntityNotFoundException
* @throws MappingException
*/
private function deleteEntityCollection(PersistentCollection $collection): int
{
$mapping = $collection->getMapping();
@@ -186,6 +195,16 @@ class OneToManyPersister extends AbstractCollectionPersister
$statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform)
. ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
if ($targetClass->isInheritanceTypeSingleTable()) {
$discriminatorColumn = $targetClass->getDiscriminatorColumn();
$discriminatorValues = $targetClass->discriminatorValue ? [$targetClass->discriminatorValue] : array_keys($targetClass->discriminatorMap);
$statement .= ' AND ' . $discriminatorColumn['name'] . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')';
foreach ($discriminatorValues as $discriminatorValue) {
$parameters[] = $discriminatorValue;
$types[] = $discriminatorColumn['type'];
}
}
$numAffected = $this->conn->executeStatement($statement, $parameters, $types);
assert(is_int($numAffected));

View File

@@ -832,17 +832,42 @@ class BasicEntityPersister implements EntityPersister
$computedIdentifier = [];
/** @var array<string,mixed>|null $sourceEntityData */
$sourceEntityData = null;
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if (! isset($sourceClass->fieldNames[$sourceKeyColumn])) {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name,
$sourceKeyColumn
);
}
// The likely case here is that the column is a join column
// in an association mapping. However, there is no guarantee
// at this point that a corresponding (generally identifying)
// association has been mapped in the source entity. To handle
// this case we directly reference the column-keyed data used
// to initialize the source entity before throwing an exception.
$resolvedSourceData = false;
if (! isset($sourceEntityData)) {
$sourceEntityData = $this->em->getUnitOfWork()->getOriginalEntityData($sourceEntity);
}
$computedIdentifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
if (isset($sourceEntityData[$sourceKeyColumn])) {
$dataValue = $sourceEntityData[$sourceKeyColumn];
if ($dataValue !== null) {
$resolvedSourceData = true;
$computedIdentifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
$dataValue;
}
}
if (! $resolvedSourceData) {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name,
$sourceKeyColumn
);
}
} else {
$computedIdentifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
}
}
$targetEntity = $this->load($computedIdentifier, null, $assoc);

View File

@@ -250,6 +250,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest.
// @phpstan-ignore method.deprecated
if ($this->platform->supportsForeignKeyConstraints()) {
$rootClass = $this->em->getClassMetadata($this->class->rootEntityName);
$rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform);

View File

@@ -173,6 +173,7 @@ EOPHP;
$this->isLazyGhostObjectEnabled = false;
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
// @phpstan-ignore classConstant.deprecatedInterface
$proxyGenerator->setPlaceholder('baseProxyInterface', LegacyProxy::class);
parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
@@ -354,15 +355,14 @@ EOPHP;
/**
* Creates a closure capable of initializing a proxy
*
* @return Closure(InternalProxy, InternalProxy):void
* @return Closure(InternalProxy, array):void
*
* @throws EntityNotFoundException
*/
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure
{
return static function (InternalProxy $proxy) use ($entityPersister, $classMetadata, $identifierFlattener): void {
$identifier = $classMetadata->getIdentifierValues($proxy);
$original = $entityPersister->loadById($identifier);
return static function (InternalProxy $proxy, array $identifier) use ($entityPersister, $classMetadata, $identifierFlattener): void {
$original = $entityPersister->loadById($identifier);
if ($original === null) {
throw EntityNotFoundException::fromClassNameAndIdentifier(
@@ -378,7 +378,7 @@ EOPHP;
$class = $entityPersister->getClassMetadata();
foreach ($class->getReflectionProperties() as $property) {
if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
if (isset($identifier[$property->name]) || ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
continue;
}
@@ -468,7 +468,9 @@ EOPHP;
$identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers);
$proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy {
$proxy = self::createLazyGhost($initializer, $skippedProperties);
$proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
$initializer($object, $identifier);
}, $skippedProperties);
foreach ($identifierFields as $idField => $reflector) {
if (! isset($identifier[$idField])) {

View File

@@ -18,6 +18,8 @@ use Doctrine\ORM\Query\AST\DeleteStatement;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\AST\UpdateStatement;
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
use Doctrine\ORM\Query\Exec\SqlFinalizer;
use Doctrine\ORM\Query\OutputWalker;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Query\ParameterTypeInferer;
use Doctrine\ORM\Query\Parser;
@@ -33,6 +35,7 @@ use function assert;
use function count;
use function get_debug_type;
use function in_array;
use function is_a;
use function is_int;
use function ksort;
use function md5;
@@ -196,7 +199,7 @@ class Query extends AbstractQuery
*/
public function getSQL()
{
return $this->parse()->getSqlExecutor()->getSqlStatements();
return $this->getSqlExecutor()->getSqlStatements();
}
/**
@@ -285,7 +288,7 @@ class Query extends AbstractQuery
*/
protected function _doExecute()
{
$executor = $this->parse()->getSqlExecutor();
$executor = $this->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
@@ -344,6 +347,7 @@ class Query extends AbstractQuery
$cache = method_exists(QueryCacheProfile::class, 'getResultCache')
? $this->_queryCacheProfile->getResultCache()
// @phpstan-ignore method.deprecated
: $this->_queryCacheProfile->getResultCacheDriver();
assert($cache !== null);
@@ -812,11 +816,31 @@ class Query extends AbstractQuery
{
ksort($this->_hints);
if (! $this->hasHint(self::HINT_CUSTOM_OUTPUT_WALKER)) {
// Assume Parser will create the SqlOutputWalker; save is_a call, which might trigger a class load
$firstAndMaxResult = '';
} else {
$outputWalkerClass = $this->getHint(self::HINT_CUSTOM_OUTPUT_WALKER);
if (is_a($outputWalkerClass, OutputWalker::class, true)) {
$firstAndMaxResult = '';
} else {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11188/',
'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.',
$outputWalkerClass,
OutputWalker::class,
SqlFinalizer::class
);
$firstAndMaxResult = '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults;
}
}
return md5(
$this->getDQL() . serialize($this->_hints) .
'&platform=' . get_debug_type($this->getEntityManager()->getConnection()->getDatabasePlatform()) .
($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
'&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults .
$firstAndMaxResult .
'&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
);
}
@@ -835,4 +859,9 @@ class Query extends AbstractQuery
$this->state = self::STATE_DIRTY;
}
private function getSqlExecutor(): AbstractSqlExecutor
{
return $this->parse()->prepareSqlExecutor($this);
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/** @phpstan-ignore class.extendsDeprecatedClass */
class InListExpression extends InExpression
{
/** @var non-empty-list<mixed> */
@@ -15,6 +16,7 @@ class InListExpression extends InExpression
$this->literals = $literals;
$this->not = $not;
// @phpstan-ignore staticMethod.deprecatedClass
parent::__construct($expression);
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/** @phpstan-ignore class.extendsDeprecatedClass */
class InSubselectExpression extends InExpression
{
/** @var Subselect */
@@ -14,6 +15,7 @@ class InSubselectExpression extends InExpression
$this->subselect = $subselect;
$this->not = $not;
// @phpstan-ignore staticMethod.deprecatedClass
parent::__construct($expression);
}
}

View File

@@ -23,6 +23,7 @@ class IndexBy extends Node
public function __construct(PathExpression $singleValuedPathExpression)
{
// @phpstan-ignore property.deprecated
$this->singleValuedPathExpression = $this->simpleStateFieldPathExpression = $singleValuedPathExpression;
}

View File

@@ -39,6 +39,7 @@ abstract class AbstractSqlExecutor
public function __construct()
{
// @phpstan-ignore property.deprecated
$this->_sqlStatements = &$this->sqlStatements;
}
@@ -93,10 +94,13 @@ abstract class AbstractSqlExecutor
public function __wakeup(): void
{
// @phpstan-ignore property.deprecated
if ($this->_sqlStatements !== null && $this->sqlStatements === null) {
// @phpstan-ignore property.deprecated
$this->sqlStatements = $this->_sqlStatements;
}
// @phpstan-ignore property.deprecated
$this->_sqlStatements = &$this->sqlStatements;
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Types\Type;
/**
* SQL executor for a given, final, single SELECT SQL query
*
* @method string getSqlStatements()
*/
class FinalizedSelectExecutor extends AbstractSqlExecutor
{
public function __construct(string $sql)
{
parent::__construct();
$this->sqlStatements = $sql;
}
/**
* @param list<mixed>|array<string, mixed> $params
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
*/
public function execute(Connection $conn, array $params, array $types): Result
{
return $conn->executeQuery($this->getSqlStatements(), $params, $types, $this->queryCacheProfile);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\ORM\Query;
/**
* PreparedExecutorFinalizer is a wrapper for the SQL finalization
* phase that does nothing - it is constructed with the sql executor
* already.
*/
final class PreparedExecutorFinalizer implements SqlFinalizer
{
/** @var AbstractSqlExecutor */
private $executor;
public function __construct(AbstractSqlExecutor $exeutor)
{
$this->executor = $exeutor;
}
public function createExecutor(Query $query): AbstractSqlExecutor
{
return $this->executor;
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Utility\LockSqlHelper;
/**
* SingleSelectSqlFinalizer finalizes a given SQL query by applying
* the query's firstResult/maxResult values as well as extra read lock/write lock
* statements, both through the platform-specific methods.
*
* The resulting, "finalized" SQL is passed to a FinalizedSelectExecutor.
*/
class SingleSelectSqlFinalizer implements SqlFinalizer
{
use LockSqlHelper;
/** @var string */
private $sql;
public function __construct(string $sql)
{
$this->sql = $sql;
}
/**
* This method exists temporarily to support old SqlWalker interfaces.
*
* @internal
*
* @psalm-internal Doctrine\ORM
*/
public function finalizeSql(Query $query): string
{
$platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
$sql = $platform->modifyLimitQuery($this->sql, $query->getMaxResults(), $query->getFirstResult());
$lockMode = $query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE;
if ($lockMode !== LockMode::NONE && $lockMode !== LockMode::OPTIMISTIC && $lockMode !== LockMode::PESSIMISTIC_READ && $lockMode !== LockMode::PESSIMISTIC_WRITE) {
throw QueryException::invalidLockMode();
}
if ($lockMode === LockMode::PESSIMISTIC_READ) {
$sql .= ' ' . $this->getReadLockSQL($platform);
} elseif ($lockMode === LockMode::PESSIMISTIC_WRITE) {
$sql .= ' ' . $this->getWriteLockSQL($platform);
}
return $sql;
}
/** @return FinalizedSelectExecutor */
public function createExecutor(Query $query): AbstractSqlExecutor
{
return new FinalizedSelectExecutor($this->finalizeSql($query));
}
}

View File

@@ -14,8 +14,6 @@ use Doctrine\ORM\Query\SqlWalker;
* that are mapped to a single table.
*
* @link www.doctrine-project.org
*
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
*/
class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
{

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\ORM\Query;
/**
* SqlFinalizers are created by OutputWalkers that traversed the DQL AST.
* The SqlFinalizer instance can be kept in the query cache and re-used
* at a later time.
*
* Once the SqlFinalizer has been created or retrieved from the query cache,
* it receives the Query object again in order to yield the AbstractSqlExecutor
* that will then be used to execute the query.
*
* The SqlFinalizer may assume that the DQL that was used to build the AST
* and run the OutputWalker (which created the SqlFinalizer) is equivalent to
* the query that will be passed to the createExecutor() method. Potential differences
* are the parameter values or firstResult/maxResult settings.
*/
interface SqlFinalizer
{
public function createExecutor(Query $query): AbstractSqlExecutor;
}

View File

@@ -84,7 +84,11 @@ class Lexer extends AbstractLexer
public const T_CLOSE_CURLY_BRACE = TokenType::T_CLOSE_CURLY_BRACE;
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
/** @deprecated No Replacement planned. */
/**
* @deprecated No Replacement planned.
*
* @phpstan-ignore classConstant.deprecated
*/
public const T_ALIASED_NAME = TokenType::T_ALIASED_NAME;
/** @deprecated use {@see TokenType::T_FULLY_QUALIFIED_NAME} */
@@ -341,6 +345,7 @@ class Lexer extends AbstractLexer
$value
);
// @phpstan-ignore classConstant.deprecated
return TokenType::T_ALIASED_NAME;
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query\Exec\SqlFinalizer;
/**
* Interface for output walkers
*
* Output walkers, like tree walkers, can traverse the DQL AST to perform
* their purpose.
*
* The goal of an OutputWalker is to ultimately provide the SqlFinalizer
* which produces the final, executable SQL statement in a "finalization" phase.
*
* It must be possible to use the same SqlFinalizer for Queries with different
* firstResult/maxResult values. In other words, SQL produced by the
* output walker should not depend on those values, and any SQL generation/modification
* specific to them should happen in the finalizer's `\Doctrine\ORM\Query\Exec\SqlFinalizer::createExecutor()`
* method instead.
*/
interface OutputWalker
{
/** @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST */
public function getFinalizer($AST): SqlFinalizer;
}

View File

@@ -8,10 +8,12 @@ use BackedEnum;
use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Types\Types;
use function class_exists;
use function current;
use function is_array;
use function is_bool;
@@ -67,9 +69,17 @@ class ParameterTypeInferer
$firstValue = $firstValue->value;
}
if (! class_exists(ArrayParameterType::class)) {
return is_int($firstValue)
// @phpstan-ignore classConstant.deprecated
? Connection::PARAM_INT_ARRAY
// @phpstan-ignore classConstant.deprecated
: Connection::PARAM_STR_ARRAY;
}
return is_int($firstValue)
? Connection::PARAM_INT_ARRAY
: Connection::PARAM_STR_ARRAY;
? ArrayParameterType::INTEGER
: ArrayParameterType::STRING;
}
return ParameterType::STRING;

View File

@@ -10,6 +10,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Exec\SqlFinalizer;
use LogicException;
use ReflectionClass;
@@ -51,7 +52,7 @@ class Parser
{
/**
* @readonly Maps BUILT-IN string function names to AST class names.
* @psalm-var array<string, class-string<Functions\FunctionNode>>
* @var array<string, class-string<Functions\FunctionNode>>
*/
private static $stringFunctions = [
'concat' => Functions\ConcatFunction::class,
@@ -64,7 +65,7 @@ class Parser
/**
* @readonly Maps BUILT-IN numeric function names to AST class names.
* @psalm-var array<string, class-string<Functions\FunctionNode>>
* @var array<string, class-string<Functions\FunctionNode>>
*/
private static $numericFunctions = [
'length' => Functions\LengthFunction::class,
@@ -87,7 +88,7 @@ class Parser
/**
* @readonly Maps BUILT-IN datetime function names to AST class names.
* @psalm-var array<string, class-string<Functions\FunctionNode>>
* @var array<string, class-string<Functions\FunctionNode>>
*/
private static $datetimeFunctions = [
'current_date' => Functions\CurrentDateFunction::class,
@@ -162,7 +163,7 @@ class Parser
/**
* Any additional custom tree walkers that modify the AST.
*
* @psalm-var list<class-string<TreeWalker>>
* @var list<class-string<TreeWalker>>
*/
private $customTreeWalkers = [];
@@ -193,21 +194,26 @@ class Parser
* Sets a custom tree walker that produces output.
* This tree walker will be run last over the AST, after any other walkers.
*
* @param string $className
* @psalm-param class-string<SqlWalker> $className
* @param class-string<SqlWalker> $className
*
* @return void
*/
public function setCustomOutputTreeWalker($className)
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11641',
'%s is deprecated, set the output walker class with the \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER query hint instead',
__METHOD__
);
$this->customOutputWalker = $className;
}
/**
* Adds a custom tree walker for modifying the AST.
*
* @param string $className
* @psalm-param class-string<TreeWalker> $className
* @param class-string<TreeWalker> $className
*
* @return void
*/
@@ -391,11 +397,26 @@ class Parser
$this->queryComponents = $treeWalkerChain->getQueryComponents();
}
$outputWalkerClass = $this->customOutputWalker ?: SqlWalker::class;
$outputWalkerClass = $this->customOutputWalker ?: SqlOutputWalker::class;
$outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents);
// Assign an SQL executor to the parser result
$this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
if ($outputWalker instanceof OutputWalker) {
$finalizer = $outputWalker->getFinalizer($AST);
$this->parserResult->setSqlFinalizer($finalizer);
} else {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11188/',
'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.',
$outputWalkerClass,
OutputWalker::class,
SqlFinalizer::class
);
// @phpstan-ignore method.deprecated
$executor = $outputWalker->getExecutor($AST);
// @phpstan-ignore method.deprecated
$this->parserResult->setSqlExecutor($executor);
}
return $this->parserResult;
}
@@ -981,6 +1002,7 @@ class Parser
return $this->lexer->token->value;
}
// @phpstan-ignore classConstant.deprecated
$this->match(TokenType::T_ALIASED_NAME);
assert($this->lexer->token !== null);
@@ -1844,12 +1866,6 @@ class Parser
*/
public function PartialObjectExpression()
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/8471',
'PARTIAL syntax in DQL is deprecated.'
);
$this->match(TokenType::T_PARTIAL);
$partialFieldSet = [];
@@ -2575,6 +2591,8 @@ class Parser
* AST\InstanceOfExpression|
* AST\LikeExpression|
* AST\NullComparisonExpression)
*
* @phpstan-ignore return.deprecatedClass
*/
public function SimpleConditionalExpression()
{
@@ -2924,7 +2942,10 @@ class Parser
return new AST\ParenthesisExpression($expr);
}
assert($this->lexer->lookahead !== null);
if ($this->lexer->lookahead === null) {
$this->syntaxError('ArithmeticPrimary');
}
switch ($this->lexer->lookahead->type) {
case TokenType::T_COALESCE:
case TokenType::T_NULLIF:

View File

@@ -4,7 +4,10 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
use Doctrine\ORM\Query\Exec\SqlFinalizer;
use LogicException;
use function sprintf;
@@ -20,15 +23,23 @@ class ParserResult
'sqlExecutor' => '_sqlExecutor',
'resultSetMapping' => '_resultSetMapping',
'parameterMappings' => '_parameterMappings',
'sqlFinalizer' => 'sqlFinalizer',
];
/**
* The SQL executor used for executing the SQL.
*
* @var AbstractSqlExecutor
* @var ?AbstractSqlExecutor
*/
private $sqlExecutor;
/**
* The SQL executor used for executing the SQL.
*
* @var ?SqlFinalizer
*/
private $sqlFinalizer;
/**
* The ResultSetMapping that describes how to map the SQL result set.
*
@@ -75,6 +86,8 @@ class ParserResult
/**
* Sets the SQL executor that should be used for this ParserResult.
*
* @deprecated
*
* @param AbstractSqlExecutor $executor
*
* @return void
@@ -87,13 +100,33 @@ class ParserResult
/**
* Gets the SQL executor used by this ParserResult.
*
* @return AbstractSqlExecutor
* @deprecated
*
* @return ?AbstractSqlExecutor
*/
public function getSqlExecutor()
{
return $this->sqlExecutor;
}
public function setSqlFinalizer(SqlFinalizer $finalizer): void
{
$this->sqlFinalizer = $finalizer;
}
public function prepareSqlExecutor(Query $query): AbstractSqlExecutor
{
if ($this->sqlFinalizer !== null) {
return $this->sqlFinalizer->createExecutor($query);
}
if ($this->sqlExecutor !== null) {
return $this->sqlExecutor;
}
throw new LogicException('This ParserResult lacks both the SqlFinalizer as well as the (legacy) SqlExecutor');
}
/**
* Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to
* several SQL parameter positions.

View File

@@ -41,7 +41,7 @@ class ResultSetMapping
* Maps alias names to class names.
*
* @ignore
* @psalm-var array<string, class-string>
* @var array<string, class-string>
*/
public $aliasMap = [];
@@ -137,7 +137,7 @@ class ResultSetMapping
* Map from column names to class names that declare the field the column is mapped to.
*
* @ignore
* @psalm-var array<string, class-string>
* @var array<string, class-string>
*/
public $declaringClasses = [];
@@ -172,12 +172,11 @@ class ResultSetMapping
/**
* Adds an entity result to this ResultSetMapping.
*
* @param string $class The class name of the entity.
* @param string $alias The alias for the class. The alias must be unique among all entity
* results or joined entity results within this ResultSetMapping.
* @param string|null $resultAlias The result alias with which the entity result should be
* placed in the result structure.
* @psalm-param class-string $class
* @param class-string $class The class name of the entity.
* @param string $alias The alias for the class. The alias must be unique among all entity
* results or joined entity results within this ResultSetMapping.
* @param string|null $resultAlias The result alias with which the entity result should be
* placed in the result structure.
*
* @return $this
*
@@ -316,15 +315,14 @@ class ResultSetMapping
/**
* Adds a field to the result that belongs to an entity or joined entity.
*
* @param string $alias The alias of the root entity or joined entity to which the field belongs.
* @param string $columnName The name of the column in the SQL result set.
* @param string $fieldName The name of the field on the declaring class.
* @param string|null $declaringClass The name of the class that declares/owns the specified field.
* When $alias refers to a superclass in a mapped hierarchy but
* the field $fieldName is defined on a subclass, specify that here.
* If not specified, the field is assumed to belong to the class
* designated by $alias.
* @psalm-param class-string|null $declaringClass
* @param string $alias The alias of the root entity or joined entity to which the field belongs.
* @param string $columnName The name of the column in the SQL result set.
* @param string $fieldName The name of the field on the declaring class.
* @param class-string|null $declaringClass The name of the class that declares/owns the specified field.
* When $alias refers to a superclass in a mapped hierarchy but
* the field $fieldName is defined on a subclass, specify that here.
* If not specified, the field is assumed to belong to the class
* designated by $alias.
*
* @return $this
*
@@ -349,12 +347,11 @@ class ResultSetMapping
/**
* Adds a joined entity result.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @psalm-param class-string $class
* @param class-string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
*
* @return $this
*
@@ -539,7 +536,7 @@ class ResultSetMapping
return $this->fieldMappings[$columnName];
}
/** @psalm-return array<string, class-string> */
/** @return array<string, class-string> */
public function getAliasMap()
{
return $this->aliasMap;

View File

@@ -77,12 +77,10 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param class-string $class
* @psalm-param array<string, string> $renamedColumns
* @param class-string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param self::COLUMN_RENAMING_*|null $renameMode
*
* @return void
@@ -99,15 +97,13 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param class-string $class
* @psalm-param array<string, string> $renamedColumns
* @param class-string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param self::COLUMN_RENAMING_*|null $renameMode
*
* @return void
@@ -228,12 +224,11 @@ class ResultSetMappingBuilder extends ResultSetMapping
*
* This depends on the renaming mode selected by the user.
*
* @psalm-param class-string $className
* @param class-string $className
* @psalm-param self::COLUMN_RENAMING_* $mode
* @psalm-param array<string, string> $customRenameColumns
*
* @return string[]
* @psalm-return array<array-key, string>
*/
private function getColumnAliasMap(
string $className,

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query\Exec\PreparedExecutorFinalizer;
use Doctrine\ORM\Query\Exec\SingleSelectSqlFinalizer;
use Doctrine\ORM\Query\Exec\SqlFinalizer;
use LogicException;
class SqlOutputWalker extends SqlWalker implements OutputWalker
{
public function getFinalizer($AST): SqlFinalizer
{
switch (true) {
case $AST instanceof AST\SelectStatement:
return new SingleSelectSqlFinalizer($this->createSqlForFinalizer($AST));
case $AST instanceof AST\UpdateStatement:
return new PreparedExecutorFinalizer($this->createUpdateStatementExecutor($AST));
case $AST instanceof AST\DeleteStatement:
return new PreparedExecutorFinalizer($this->createDeleteStatementExecutor($AST));
}
throw new LogicException('Unexpected AST node type');
}
}

View File

@@ -16,7 +16,6 @@ use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
use Doctrine\ORM\Utility\LockSqlHelper;
use Doctrine\ORM\Utility\PersisterHelper;
use InvalidArgumentException;
use LogicException;
@@ -49,8 +48,6 @@ use function trim;
*/
class SqlWalker implements TreeWalker
{
use LockSqlHelper;
public const HINT_DISTINCT = 'doctrine.distinct';
/**
@@ -278,34 +275,48 @@ class SqlWalker implements TreeWalker
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @deprecated Output walkers should no longer create the executor directly, but instead provide
* a SqlFinalizer by implementing the `OutputWalker` interface. Thus, this method is
* no longer needed and will be removed in 4.0.
*
* @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST
*
* @return Exec\AbstractSqlExecutor
*
* @not-deprecated
*/
public function getExecutor($AST)
{
switch (true) {
case $AST instanceof AST\DeleteStatement:
$primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
return $primaryClass->isInheritanceTypeJoined()
? new Exec\MultiTableDeleteExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
return $this->createDeleteStatementExecutor($AST);
case $AST instanceof AST\UpdateStatement:
$primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
return $primaryClass->isInheritanceTypeJoined()
? new Exec\MultiTableUpdateExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
return $this->createUpdateStatementExecutor($AST);
default:
return new Exec\SingleSelectExecutor($AST, $this);
}
}
/** @psalm-internal Doctrine\ORM */
protected function createUpdateStatementExecutor(AST\UpdateStatement $AST): Exec\AbstractSqlExecutor
{
$primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
return $primaryClass->isInheritanceTypeJoined()
? new Exec\MultiTableUpdateExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
/** @psalm-internal Doctrine\ORM */
protected function createDeleteStatementExecutor(AST\DeleteStatement $AST): Exec\AbstractSqlExecutor
{
$primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
return $primaryClass->isInheritanceTypeJoined()
? new Exec\MultiTableDeleteExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
/**
* Generates a unique, short SQL table alias.
*
@@ -561,10 +572,15 @@ class SqlWalker implements TreeWalker
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
$limit = $this->query->getMaxResults();
$offset = $this->query->getFirstResult();
$lockMode = $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE;
$sql = $this->walkSelectClause($AST->selectClause)
$sql = $this->createSqlForFinalizer($AST);
$finalizer = new Exec\SingleSelectSqlFinalizer($sql);
return $finalizer->finalizeSql($this->query);
}
protected function createSqlForFinalizer(AST\SelectStatement $AST): string
{
$sql = $this->walkSelectClause($AST->selectClause)
. $this->walkFromClause($AST->fromClause)
. $this->walkWhereClause($AST->whereClause);
@@ -585,33 +601,24 @@ class SqlWalker implements TreeWalker
$sql .= ' ORDER BY ' . $orderBySql;
}
$sql = $this->platform->modifyLimitQuery($sql, $limit, $offset);
if ($lockMode === LockMode::NONE) {
return $sql;
}
if ($lockMode === LockMode::PESSIMISTIC_READ) {
return $sql . ' ' . $this->getReadLockSQL($this->platform);
}
if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
return $sql . ' ' . $this->getWriteLockSQL($this->platform);
}
if ($lockMode !== LockMode::OPTIMISTIC) {
throw QueryException::invalidLockMode();
}
foreach ($this->selectedClasses as $selectedClass) {
if (! $selectedClass['class']->isVersioned) {
throw OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
$this->assertOptimisticLockingHasAllClassesVersioned();
return $sql;
}
private function assertOptimisticLockingHasAllClassesVersioned(): void
{
$lockMode = $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE;
if ($lockMode === LockMode::OPTIMISTIC) {
foreach ($this->selectedClasses as $selectedClass) {
if (! $selectedClass['class']->isVersioned) {
throw OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
}
}
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
@@ -1062,7 +1069,9 @@ class SqlWalker implements TreeWalker
}
}
if ($relation['fetch'] === ClassMetadata::FETCH_EAGER && $condExpr !== null) {
$fetchMode = $this->query->getHint('fetchMode')[$assoc['sourceEntity']][$assoc['fieldName']] ?? $relation['fetch'];
if ($fetchMode === ClassMetadata::FETCH_EAGER && $condExpr !== null) {
throw QueryException::eagerFetchJoinWithNotAllowed($assoc['sourceEntity'], $assoc['fieldName']);
}

View File

@@ -806,6 +806,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
final protected function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata
{
// @phpstan-ignore method.deprecated
$metadata = $this->_getQueryComponents()[$dqlAlias]['metadata'] ?? null;
if ($metadata === null) {

View File

@@ -23,8 +23,7 @@ class TreeWalkerChain implements TreeWalker
/**
* The tree walkers.
*
* @var string[]
* @psalm-var list<class-string<TreeWalker>>
* @var list<class-string<TreeWalker>>
*/
private $walkers = [];
@@ -81,8 +80,7 @@ class TreeWalkerChain implements TreeWalker
/**
* Adds a tree walker to the chain.
*
* @param string $walkerClass The class of the walker to instantiate.
* @psalm-param class-string<TreeWalker> $walkerClass
* @param class-string<TreeWalker> $walkerClass The class of the walker to instantiate.
*
* @return void
*/

View File

@@ -49,10 +49,7 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
$this->parserResult = $parserResult;
}
/**
* @return string|false
* @psalm-return class-string<TreeWalker>|false
*/
/** @return class-string<TreeWalker>|false */
#[ReturnTypeWillChange]
public function rewind()
{

View File

@@ -86,6 +86,7 @@ class QueryBuilder
*
* @var int
* @psalm-var self::SELECT|self::DELETE|self::UPDATE
* @phpstan-ignore classConstant.deprecated
*/
private $type = self::SELECT;
@@ -94,6 +95,7 @@ class QueryBuilder
*
* @var int
* @psalm-var self::STATE_*
* @phpstan-ignore classConstant.deprecated
*/
private $state = self::STATE_CLEAN;
@@ -342,25 +344,30 @@ class QueryBuilder
*/
public function getDQL()
{
// @phpstan-ignore classConstant.deprecated
if ($this->dql !== null && $this->state === self::STATE_CLEAN) {
return $this->dql;
}
switch ($this->type) {
// @phpstan-ignore classConstant.deprecated
case self::DELETE:
$dql = $this->getDQLForDelete();
break;
// @phpstan-ignore classConstant.deprecated
case self::UPDATE:
$dql = $this->getDQLForUpdate();
break;
// @phpstan-ignore classConstant.deprecated
case self::SELECT:
default:
$dql = $this->getDQLForSelect();
break;
}
// @phpstan-ignore classConstant.deprecated
$this->state = self::STATE_CLEAN;
$this->dql = $dql;
@@ -422,6 +429,7 @@ class QueryBuilder
} else {
// Should never happen with correct joining order. Might be
// thoughtful to throw exception instead.
// @phpstan-ignore method.deprecated
$rootAlias = $this->getRootAlias();
}
@@ -743,6 +751,7 @@ class QueryBuilder
$newDqlPart = [];
foreach ($dqlPart as $k => $v) {
// @phpstan-ignore method.deprecated
$k = is_numeric($k) ? $this->getRootAlias() : $k;
$newDqlPart[$k] = $v;
@@ -763,6 +772,7 @@ class QueryBuilder
$this->dqlParts[$dqlPartName] = $isMultiple ? [$dqlPart] : $dqlPart;
}
// @phpstan-ignore classConstant.deprecated
$this->state = self::STATE_DIRTY;
return $this;
@@ -785,6 +795,7 @@ class QueryBuilder
*/
public function select($select = null)
{
// @phpstan-ignore classConstant.deprecated
$this->type = self::SELECT;
if (empty($select)) {
@@ -816,7 +827,8 @@ class QueryBuilder
if ($this->dqlParts['distinct'] !== $flag) {
$this->dqlParts['distinct'] = $flag;
$this->state = self::STATE_DIRTY;
// @phpstan-ignore classConstant.deprecated
$this->state = self::STATE_DIRTY;
}
return $this;
@@ -839,6 +851,7 @@ class QueryBuilder
*/
public function addSelect($select = null)
{
// @phpstan-ignore classConstant.deprecated
$this->type = self::SELECT;
if (empty($select)) {
@@ -868,6 +881,7 @@ class QueryBuilder
*/
public function delete($delete = null, $alias = null)
{
// @phpstan-ignore classConstant.deprecated
$this->type = self::DELETE;
if (! $delete) {
@@ -903,6 +917,7 @@ class QueryBuilder
*/
public function update($update = null, $alias = null)
{
// @phpstan-ignore classConstant.deprecated
$this->type = self::UPDATE;
if (! $update) {
@@ -1528,7 +1543,8 @@ class QueryBuilder
public function resetDQLPart($part)
{
$this->dqlParts[$part] = is_array($this->dqlParts[$part]) ? [] : null;
$this->state = self::STATE_DIRTY;
// @phpstan-ignore classConstant.deprecated
$this->state = self::STATE_DIRTY;
return $this;
}

View File

@@ -71,6 +71,7 @@ EOT
$cacheDriver = null;
if (! $cache) {
// @phpstan-ignore method.deprecated
$cacheDriver = $em->getConfiguration()->getQueryCacheImpl();
if (! $cacheDriver) {

View File

@@ -63,8 +63,9 @@ EOT
{
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
$em = $this->getEntityManager($input);
$cache = $em->getConfiguration()->getResultCache();
$em = $this->getEntityManager($input);
$cache = $em->getConfiguration()->getResultCache();
// @phpstan-ignore method.deprecated
$cacheDriver = method_exists(Configuration::class, 'getResultCacheImpl') ? $em->getConfiguration()->getResultCacheImpl() : null;
if (! $cacheDriver && ! $cache) {

View File

@@ -131,8 +131,7 @@ EOT
/**
* Return all mapped entity class names
*
* @return string[]
* @psalm-return class-string[]
* @return class-string[]
*/
private function getMappedEntities(EntityManagerInterface $entityManager): array
{

View File

@@ -71,6 +71,7 @@ final class ConsoleRunner
if ($helperSetOrProvider instanceof HelperSet) {
$cli->setHelperSet($helperSetOrProvider);
// @phpstan-ignore new.deprecated
$helperSetOrProvider = new HelperSetManagerProvider($helperSetOrProvider);
}
@@ -83,6 +84,7 @@ final class ConsoleRunner
public static function addCommands(Application $cli, ?EntityManagerProvider $entityManagerProvider = null): void
{
if ($entityManagerProvider === null) {
// @phpstan-ignore new.deprecated
$entityManagerProvider = new HelperSetManagerProvider($cli->getHelperSet());
}
@@ -95,6 +97,7 @@ final class ConsoleRunner
$cli->addCommands(
[
// DBAL Commands
// @phpstan-ignore new.deprecated
new DBALConsole\Command\ReservedWordsCommand($connectionProvider),
new DBALConsole\Command\RunSqlCommand($connectionProvider),
@@ -108,11 +111,16 @@ final class ConsoleRunner
new Command\SchemaTool\CreateCommand($entityManagerProvider),
new Command\SchemaTool\UpdateCommand($entityManagerProvider),
new Command\SchemaTool\DropCommand($entityManagerProvider),
// @phpstan-ignore new.deprecated
new Command\EnsureProductionSettingsCommand($entityManagerProvider),
// @phpstan-ignore new.deprecated
new Command\ConvertDoctrine1SchemaCommand(),
// @phpstan-ignore new.deprecated
new Command\GenerateRepositoriesCommand($entityManagerProvider),
// @phpstan-ignore new.deprecated
new Command\GenerateEntitiesCommand($entityManagerProvider),
new Command\GenerateProxiesCommand($entityManagerProvider),
// @phpstan-ignore new.deprecated
new Command\ConvertMappingCommand($entityManagerProvider),
new Command\RunDqlCommand($entityManagerProvider),
new Command\ValidateSchemaCommand($entityManagerProvider),

View File

@@ -91,8 +91,8 @@ class ConvertDoctrine1Schema
}
/**
* @param mixed[] $mappingInformation
* @psalm-param class-string $className
* @param class-string $className
* @param mixed[] $mappingInformation
*/
private function convertToClassMetadataInfo(
string $className,

View File

@@ -767,6 +767,9 @@ public function __construct(<params>)
if ($fieldMapping['type'] === 'datetime') {
$param = $this->getType($fieldMapping['type']) . ' ' . $param;
if (! empty($fieldMapping['nullable'])) {
$param = '?' . $param;
}
}
if (! empty($fieldMapping['nullable'])) {
@@ -1275,12 +1278,25 @@ public function __construct(<params>)
$methods = [];
foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
$lifecycleEventsByCallback = [];
foreach ($metadata->lifecycleCallbacks as $event => $callbacks) {
foreach ($callbacks as $callback) {
$methods[] = $this->generateLifecycleCallbackMethod($name, $callback, $metadata);
$callbackCaseInsensitive = $callback;
foreach (array_keys($lifecycleEventsByCallback) as $key) {
if (strtolower($key) === strtolower($callback)) {
$callbackCaseInsensitive = $key;
break;
}
}
$lifecycleEventsByCallback[$callbackCaseInsensitive][] = $event;
}
}
foreach ($lifecycleEventsByCallback as $callback => $events) {
$methods[] = $this->generateLifecycleCallbackMethod($events, $callback, $metadata);
}
return implode("\n\n", array_filter($methods));
}
@@ -1385,6 +1401,9 @@ public function __construct(<params>)
if ($typeHint && ! isset($types[$typeHint])) {
$variableType = '\\' . ltrim($variableType, '\\');
$methodTypeHint = '\\' . $typeHint . ' ';
if ($defaultValue === 'null') {
$methodTypeHint = '?' . $methodTypeHint;
}
}
$replacements = [
@@ -1408,8 +1427,8 @@ public function __construct(<params>)
}
/**
* @param string $name
* @param string $methodName
* @param string|string[] $name
* @param string $methodName
*
* @return string
*/
@@ -1419,10 +1438,16 @@ public function __construct(<params>)
return '';
}
$this->staticReflection[$metadata->name]['methods'][] = $methodName;
$this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName);
$replacements = [
'<name>' => $this->annotationsPrefix . ucfirst($name),
$eventAnnotations = array_map(
function ($event) {
return $this->annotationsPrefix . ucfirst($event);
},
is_array($name) ? $name : [$name]
);
$replacements = [
'<name>' => implode("\n * @", $eventAnnotations),
'<methodName>' => $methodName,
];

View File

@@ -31,7 +31,7 @@ use const DIRECTORY_SEPARATOR;
*/
class EntityRepositoryGenerator
{
/** @psalm-var class-string|null */
/** @var class-string|null */
private $repositoryName;
/** @var string */
@@ -80,7 +80,7 @@ class <className> extends <repositoryName>
/**
* Generates the namespace, if class do not have namespace, return empty string instead.
*
* @psalm-param class-string $fullClassName
* @param class-string $fullClassName
*/
private function getClassNamespace(string $fullClassName): string
{
@@ -90,7 +90,7 @@ class <className> extends <repositoryName>
/**
* Generates the class name
*
* @psalm-param class-string $fullClassName
* @param class-string $fullClassName
*/
private function generateClassName(string $fullClassName): string
{
@@ -108,7 +108,7 @@ class <className> extends <repositoryName>
/**
* Generates the namespace statement, if class do not have namespace, return empty string instead.
*
* @psalm-param class-string $fullClassName The full repository class name.
* @param class-string $fullClassName The full repository class name.
*/
private function generateEntityRepositoryNamespace(string $fullClassName): string
{

View File

@@ -16,6 +16,8 @@ use function str_replace;
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
*
* @link www.doctrine-project.org
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class AnnotationExporter extends AbstractExporter
{

View File

@@ -23,6 +23,8 @@ use const PHP_EOL;
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
*
* @link www.doctrine-project.org
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class PhpExporter extends AbstractExporter
{

View File

@@ -21,6 +21,8 @@ use function uasort;
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
*
* @link www.doctrine-project.org
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class XmlExporter extends AbstractExporter
{

View File

@@ -16,6 +16,8 @@ use function count;
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
*
* @link www.doctrine-project.org
*
* @phpstan-ignore class.extendsDeprecatedClass
*/
class YamlExporter extends AbstractExporter
{

Some files were not shown because too many files have changed in this diff Show More