Compare commits

..

118 Commits
3.3.0 ... 3.3.2

Author SHA1 Message Date
Grégoire Paris
c9557c588b Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2025-02-04 20:43:15 +01:00
Grégoire Paris
19912de927 Merge pull request #11820 from doctrine/dependabot/github_actions/2.20.x/doctrine/dot-github-7.2.1
Bump doctrine/.github from 7.1.0 to 7.2.1
2025-02-04 20:17:01 +01:00
dependabot[bot]
737cca5b78 Bump doctrine/.github from 7.1.0 to 7.2.1
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 7.1.0 to 7.2.1.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/7.1.0...7.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-04 18:23:54 +00:00
Grégoire Paris
aff82af7de Merge pull request #11811 from greg0ire/quoteIdentifier-depr
Address quoteIdentifier() deprecation
2025-01-28 23:17:29 +01:00
Grégoire Paris
9e999ea1ff Merge pull request #11792 from dbannik/11783-failure-with-indexed-relation
11783 failure with indexed relation
2025-01-28 15:32:25 +01:00
Bob van de Vijver
6755bb0c7b Fix Hydration when use ManyToMany[indexBy]
The bug related (#11694) and fixed mapping of sql column alias to field in entity (#11783) and
invalidate cache [cache/persisted/entity|cache/persisted/collection] when sql filter changes
2025-01-27 15:35:59 +03:00
Grégoire Paris
aa141bf001 Address quoteIdentifier() deprecation
We should be using quoteSingleIdentifier(), assuming we only ever pass
single identifiers here.

See https://github.com/doctrine/dbal/pull/6590
2025-01-26 12:01:14 +01:00
Grégoire Paris
cf39e00553 Merge pull request #11807 from greg0ire/display-deprecations
Display Doctrine deprecations when running PHPUnit
2025-01-25 11:09:40 +01:00
Grégoire Paris
27b47841be Display Doctrine deprecations when running PHPUnit
This will give a signal that there is work to be done without blocking
other contributions by failing the build.
2025-01-25 10:53:36 +01:00
Grégoire Paris
c2a49327a7 Merge pull request #11799 from HypeMC/enum-array-error
Fix invalid enum value in array of enums
2025-01-22 11:54:59 +01:00
Maxime COLIN
9bd7242376 Introduce testNotListedValueInEnumArray 2025-01-22 02:25:34 +01:00
pawel-slowik
fff085b63f Fix documentation for JoinColumn nullable (#11798)
Nullability is not inherited from the PHP type. The change that enabled
this feature was reversed in https://github.com/doctrine/orm/pull/8732.
2025-01-22 00:12:00 +01:00
Grégoire Paris
5ad5b11ae1 Merge pull request #11769 from HypeMC/fix-reportfieldswheredeclared
Fix fields of transient classes being considered duplicate with `reportFieldsWhereDeclared`
2025-01-20 23:48:32 +01:00
Grégoire Paris
c12fd2cb94 Merge pull request #11793 from greg0ire/doctrine-common-support
Ignore deprecations from doctrine/common
2025-01-18 22:29:13 +01:00
Grégoire Paris
44d5d4a779 Ignore deprecations from doctrine/common
These new issues are caused by doctrine/common 3.5.0, released 2 weeks
ago.
2025-01-17 08:33:24 +01:00
Grégoire Paris
5a599233c9 Merge pull request #11791 from jonnyeom/patch-2
UPGRADE: Document QueryBuilder::setParameters() Type enforcement
2025-01-17 07:55:07 +01:00
Jonny Eom
596da353c2 UPGRADE: Document QueryBuilder::setParameters() Type enforcement 2025-01-16 17:14:20 +01:00
Jamie Purchase
68c87740aa Update working-with-objects.rst (#7553)
Spelling.
2025-01-02 00:25:25 +01:00
Florian Sylvain
55dc02c39f changed confusing negative wording (#11775) 2024-12-31 00:09:28 +01:00
Grégoire Paris
b1f8253105 Merge pull request #11772 from doctrine/2.20.x
Merge 2.20.x up into 3.3.x
2024-12-19 08:08:14 +01:00
Grégoire Paris
e3cabade99 Merge pull request #11768 from pbreteche/HINT_READ_ONLY-use-its-boolean-value
Check hint value before considering instance read-only
2024-12-19 07:48:36 +01:00
HypeMC
9402f9e0f7 Fix docs examples for mappings overrides (#11770) 2024-12-18 20:41:44 +01:00
HypeMC
4feaa470af Fix fields of transient classes being considered duplicate with reportFieldsWhereDeclared 2024-12-18 15:42:12 +01:00
Pierre Bretéché
4a9101f383 Check hint value before considering instance read-only
This fixes a bug that occurs when calling setHint(Query::HINT_READ_ONLY, false) from a query object.
UnitOfWork checks if this hint exists without considering the value passed as second argument.
Handling the second parameter improves consistency with documentation.
https://www.doctrine-project.org/projects/doctrine-orm/en/2.20/reference/improving-performance.html#read-only-entities
2024-12-18 14:55:22 +01:00
Grégoire Paris
f91da5b950 Merge pull request #11767 from doctrine/dependabot/github_actions/2.20.x/doctrine/dot-github-7.1.0
Bump doctrine/.github from 6.0.0 to 7.1.0
2024-12-16 08:30:26 +01:00
dependabot[bot]
66f654d4e2 Bump doctrine/.github from 6.0.0 to 7.1.0
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 6.0.0 to 7.1.0.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/6.0.0...7.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 06:51:45 +00:00
Claudio Zizza
7b9c0d91f6 Create website schema validation workflow (#11763) 2024-12-12 23:43:08 +01:00
Grégoire Paris
53b51ae40e Merge pull request #11613 from alexander-schranz/patch-2
Add missing generated option to documentation
2024-12-11 19:51:14 +01:00
Alexander Schranz
95b0f5c328 Add missing generated option 2024-12-11 11:55:34 +01:00
Grégoire Paris
46c94e3729 Merge pull request #11760 from greg0ire/fix-merge-up
Run SA checks with PHP 8.4
2024-12-08 12:55:58 +01:00
Grégoire Paris
4e01567816 Run tests with PHP 8.4
This change was accidentally dropped during a merge up.
2024-12-08 12:47:41 +01:00
Grégoire Paris
f7f3104451 Merge pull request #11755 from greg0ire/3.3.x
Merge 2.20.x up into 3.3.x
2024-12-08 12:16:15 +01:00
Grégoire Paris
a5c80a4c75 Provide XSD for phpcs file (#11752)
It unlocks autocompletion and validation in some IDEs.
2024-12-07 23:21:04 +01:00
Grégoire Paris
417444d4b5 Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2024-12-07 22:31:38 +01:00
Grégoire Paris
6fd26a3933 Order result (#11757)
This avoids a test flakyness observed when using PostgreSQL in the CI.
2024-12-07 22:07:23 +01:00
Grégoire Paris
8ef9253999 Upgrade to PHPStan 2 (#11756)
Some calls to assert() are no longer necessary.
2024-12-07 20:42:54 +01:00
Grégoire Paris
360b80afab Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2024-12-07 16:17:12 +01:00
Grégoire Paris
0ed0be089c Run static analysis checks on PHP 8.4 (#11753)
* Run static analysis checks on PHP 8.4

* Remove forgotten references to Psalm

* Remove invalid annotation

I do not think it achieves anything.
2024-12-07 13:36:37 +01:00
Grégoire Paris
8fb1043e96 Merge pull request #11704 from beberlei/DropPsalm-2.20
Drop Psalm
2024-12-07 12:43:18 +01:00
Benjamin Eberlei
fd041fbe80 Drop Psalm 2024-12-07 12:29:58 +01:00
Grégoire Paris
5d73458f0b Merge pull request #11745 from greg0ire/3.4.x
Merge 3.3.x up into 3.4.x
2024-12-03 23:43:43 +01:00
Grégoire Paris
6c70d11f4e Merge remote-tracking branch 'origin/3.3.x' into 3.4.x 2024-12-03 23:04:46 +01:00
Grégoire Paris
eadf96c879 Merge pull request #11743 from doctrine/dependabot/github_actions/2.20.x/doctrine/dot-github-6.0.0
Bump doctrine/.github from 5.3.0 to 6.0.0
2024-12-02 08:07:33 +01:00
dependabot[bot]
0d770c89d6 Bump doctrine/.github from 5.3.0 to 6.0.0
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 5.3.0 to 6.0.0.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/5.3.0...6.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 06:07:53 +00:00
Grégoire Paris
0a635c1ece Merge pull request #11740 from doctrine/2.20.x
Merge 2.20.x up into 3.3.x
2024-11-28 08:53:49 +01:00
Grégoire Paris
69a4199434 Merge pull request #11738 from greg0ire/3.3.x
Merge 2.20.x up into 3.3.x
2024-11-28 07:54:42 +01:00
Grégoire Paris
4bda5147f3 Merge pull request #11736 from greg0ire/avoid-coverage-upload
Avoid coverage upload for merge up pull requests
2024-11-28 07:54:05 +01:00
Grégoire Paris
cbda7e2322 Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2024-11-27 22:27:07 +01:00
Grégoire Paris
38c6569645 Avoid coverage upload for merge up pull requests
When there are no conflicts between branches, we create pull requests
where the head branch is a branch on the origin repository. That branch
points to a commit that should already have coverage information
provided by the build that happens after merging a regular pull request.

The thing is, coverage information provided by builds that happen before
merging a pull request are associated with the commit of the head
repository. This means that when merging up 1.2 into 1.3, the build
produces coverage information that is the result of a merge between 1.2
and 1.3, and associates it with 1.2, although it is run on with a
codebase that is much closer to 1.3 (and is in fact supposed to become
1.3 after the merge).

This means that when we create a merge up PR from 1.2 to anything else,
the coverage information is going to be wrong until a PR targeting 1.2
gets merged.

I do not think we need coverage about conflictless merge up PRs more
than we need accurate numbers, so I propose we disable the upload for
those instead of, say, trying to associate them with the temporary merge
commit.
2024-11-27 21:39:44 +01:00
Grégoire Paris
7c0eebe90a Merge pull request #11733 from doctrine/dependabot/github_actions/2.20.x/doctrine/dot-github-5.3.0
Bump doctrine/.github from 5.2.0 to 5.3.0
2024-11-25 09:11:24 +01:00
dependabot[bot]
8784f2bce9 Bump doctrine/.github from 5.2.0 to 5.3.0
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/5.2.0...5.3.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>
2024-11-25 06:38:50 +00:00
Grégoire Paris
fbcac42ebd Merge pull request #11732 from greg0ire/phpstan-lvl-7
Raise PHPStan level to 7
2024-11-24 23:44:33 +01:00
Grégoire Paris
619302dc9a Raise PHPStan level to 7
We have a plan to drop Psalm. Before we do that, let us ensure we run
PHPStan at the level we agreed upon during the Hackathon.
2024-11-24 21:50:03 +01:00
Grégoire Paris
50d7a0f95e Merge pull request #11727 from doctrine/3.3.x
Merge 3.3.x up into 3.4.x
2024-11-23 22:01:13 +01:00
Grégoire Paris
d7f13a82ef Merge pull request #11726 from greg0ire/use-properties-minor
Use properties over array keys
2024-11-23 21:41:29 +01:00
Grégoire Paris
9e1038075e Merge pull request #11725 from greg0ire/use-properties
Use properties over array keys
2024-11-23 21:28:02 +01:00
Grégoire Paris
dd3604f523 Use properties over array keys
Using array access is deprecated.
2024-11-23 21:06:56 +01:00
Grégoire Paris
65e9f607e5 Use properties over array keys
Using array access is deprecated.
2024-11-23 21:05:10 +01:00
Grégoire Paris
a42134ccee Merge pull request #11724 from doctrine/3.3.x
Merge 3.3.x up into 3.4.x
2024-11-23 20:43:44 +01:00
Grégoire Paris
71e038c81d Merge pull request #11723 from greg0ire/3.3.x
Merge 2.20.x up into 3.3.x
2024-11-23 19:56:58 +01:00
Grégoire Paris
2ff998da0e Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2024-11-23 19:31:41 +01:00
Grégoire Paris
82e2c981da Merge pull request #11556 from k00ni/patch-1
[Docs] unitofwork.rst: php => PHP
2024-11-19 13:07:44 +01:00
Konrad Abicht
8422a41423 unitofwork.rst: php => PHP 2024-11-19 13:06:17 +01:00
Grégoire Paris
58ad1d9678 Merge pull request #11709 from lyrixx/fix-event-doc
Fix `Events::onFlush` and `PostFlush()` documentation: events are always raised
2024-11-18 22:06:18 +01:00
Grégoire Pineau
346c49832c Fix Events::onFlush and PostFlush() documentation: events are always raised
see 9e2bfa8169/src/UnitOfWork.php (L399-L413)
2024-11-18 21:51:27 +01:00
Grégoire Paris
f140651ff0 Merge pull request #11719 from greg0ire/ignore-deprecation
Ignore deprecation about StaticReflectionService
2024-11-18 21:50:12 +01:00
Grégoire Paris
bb5b2a3300 Ignore deprecation about StaticReflectionService
It is from a class that is deprecated and removed in later branches.
2024-11-18 21:46:52 +01:00
Grégoire Paris
5013d5dbef Merge pull request #9760 from beberlei/GH-3519-UniqueDiscriminatorValues
[GH-3519] Deprecate passing the same class with different discriminator values.
2024-11-18 12:18:06 +01:00
Grégoire Paris
ba11851ac4 Merge pull request #11718 from doctrine/dependabot/github_actions/2.20.x/codecov/codecov-action-5
Bump codecov/codecov-action from 4 to 5
2024-11-18 08:42:38 +01:00
dependabot[bot]
4fbce94999 Bump codecov/codecov-action from 4 to 5
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 06:30:54 +00:00
Grégoire Paris
486e406236 Merge pull request #10065 from goetas/lazy-eager-collection-refresh
Lazy and eager collection refresh inconsistency
2024-11-14 23:43:20 +01:00
Asmir Mustafic
7d1b24f3b1 attempt a fix 2024-11-14 20:15:56 +01:00
Asmir Mustafic
43ce0bef78 lazy and eager collection refresh 2024-11-14 20:06:32 +01:00
Benjamin Eberlei
da51234d5a [GH-3519] Deprecate passing the same class with different discriminator values. 2024-11-14 08:34:48 +01:00
Grégoire Paris
d9aa6ef6dc Merge pull request #11716 from acasademont/patch-1
Add `isEmpty()` method to the Extra Lazy Associations tutorial
2024-11-14 08:03:31 +01:00
Albert Casademont
ff3ccff36a Add isEmpty() method to the Extra Lazy Associations tutorial
Extra lazy support for it was added a long time ago
(see https://github.com/doctrine/orm/pull/912) but was never properly
documented.
2024-11-13 23:59:12 +01:00
Mohamed Attia
4b03ec7789 Refine Explanations of Doctrine orm Package Structure (#11710)
* Update architecture.rst

* Update architecture.rst
2024-11-12 00:02:38 +01:00
Grégoire Paris
f41dc4a503 Merge pull request #11533 from greg0ire/persistence-4-compat
Implement compatibility with Persistence 4
2024-11-04 12:30:26 +01:00
Grégoire Paris
81c0d599c9 Implement compatibility with Persistence 4 2024-11-04 12:15:30 +01:00
Indra Gunawan
9e2bfa8169 Run tests against PostgreSQL 17 (#11697)
* Run tests against PostgreSQL 17

* remove pgsql 15
2024-10-25 14:57:39 +02:00
Grégoire Paris
44fa8bbde8 Merge pull request #11344 from d-ph/patch-2
Change the lock mode in the "Aggregate Fields" cookbook (aggregate-fields.rst) article.
2024-10-24 08:55:49 +02:00
Grégoire Paris
3ca9529c32 Merge pull request #11694 from dbannik/Bug-join-sql-when-change-sqlFilter-parameters
BUG: When changing SQLFilter parameter, resulting SQL query is not generated correctly
2024-10-23 21:23:17 +02:00
Dzmitry Bannik
439b4dacf4 Is not correctly generated sql when changed/switched sqlFilter parameters
CachedPersisterContext::$selectJoinSql should be clear or regenerated when sqlFilter changed
The problem reproduce when in use fetch=EAGER and use additional sql filter on this property
2024-10-23 12:02:03 +03:00
Grégoire Paris
05f54860f7 Merge pull request #11549 from doctrine/feature/allow-overriding-cascade
Allow overriding association's cascade
2024-10-22 19:51:34 +02:00
Jan Nedbal
021a9cce3d UPGRADE: mention SqlWalker deprecations in 3.3 (#11693) 2024-10-22 15:54:18 +02:00
Grégoire Paris
0f11a97c8e Merge pull request #11035 from kbond/feat/lazy-first
Make `PersistentCollection::first()` "extra" lazy
2024-10-21 11:28:22 +02:00
Kevin Bond
c9253ef64b feat: make PersistentCollection::first() "extra" lazy 2024-10-20 19:24:21 -04:00
Grégoire Paris
af54a1696c Merge pull request #11692 from greg0ire/inherit-issue-templates
Remove config.yml
2024-10-20 12:16:44 +02:00
Grégoire Paris
4d821cb139 Remove config.yml
It causes the template inheritance to fail.
2024-10-20 12:15:32 +02:00
Grégoire Paris
85fc95060f Merge pull request #11691 from greg0ire/inherit-issue-templates
Inherit issue templates
2024-10-20 12:15:05 +02:00
Grégoire Paris
039b03255a Inherit issue templates 2024-10-20 11:58:56 +02:00
Benjamin Eberlei
e7efdede15 Update README.md 2024-10-18 09:13:20 +02:00
Grégoire Paris
74155c8672 Merge pull request #11688 from doctrine/3.3.x
Merge 3.3.x up into 3.4.x
2024-10-16 23:44:13 +02:00
Grégoire Paris
9c0e62ad44 Merge pull request #11686 from greg0ire/3.3.x
Merge 2.20.x up into 3.3.x
2024-10-16 23:16:09 +02:00
Grégoire Paris
94702d14b9 Merge remote-tracking branch 'origin/2.20.x' into 3.3.x 2024-10-16 23:02:36 +02:00
Grégoire Paris
5aad44cff6 Merge pull request #11685 from greg0ire/fix-merge-up
Remove YAML doc example
2024-10-16 23:00:52 +02:00
Grégoire Paris
182469b346 Fix copy/paste/adapt mistake (#11684)
The last step was missing.
2024-10-16 22:59:19 +02:00
Grégoire Paris
0893d8511e Remove YAML doc example
It was wrongly added during a merge up
2024-10-16 22:58:27 +02:00
Grégoire Paris
e47398ecc5 Remove leftovers from Sphinx (#11683)
We use phpDocumentor/guides now, no need for this.
2024-10-16 22:08:16 +02:00
Grégoire Paris
9e884ccf1f Merge pull request #11453 from MatteoFeltrin/allow-fqcn-in-value-attribute-of-discriminator-mapping
Allow classname in 'value' attribute of xml discriminator-mapping field
2024-10-16 08:06:49 +02:00
Alexander M. Turek
fac0899ef7 Tell dependabot to target 2.20.x (#11681) 2024-10-14 09:52:23 +02:00
Alexander M. Turek
d5c400e8d1 Remove annotations example 2024-10-14 09:32:55 +02:00
Alexander M. Turek
6648d68ddf Merge branch '2.20.x' into 3.3.x
* 2.20.x:
  Bump doctrine/.github from 5.1.0 to 5.2.0 (#11680)
  Psalm 5.26.1 (#11677)
  Update README (#11673)
  Fix PHPUnit deprecations
  Bump doctrine/.github from 5.1.0 to 5.2.0 (#11671)
  Experiment with literalinclude
2024-10-14 09:31:18 +02:00
dependabot[bot]
982d6060a3 Bump doctrine/.github from 5.1.0 to 5.2.0 (#11680) 2024-10-14 08:56:04 +02:00
Grégoire Paris
013f850c76 Merge pull request #11676 from derrabus/chore/phpunit-deprecations
Fix PHPUnit deprecations
2024-10-14 08:17:49 +02:00
Grégoire Paris
ef4508e52f Merge pull request #11667 from greg0ire/literalinclude
Experiment with literalinclude
2024-10-14 08:15:17 +02:00
Grégoire Paris
6dae89ce0d Merge pull request #11678 from derrabus/improvement/stale
Close stale pull requests
2024-10-14 08:14:36 +02:00
Alexander M. Turek
f53350934f Psalm 5.26.1 (#11677) 2024-10-13 22:04:07 +02:00
Alexander M. Turek
efaba02ef5 CI: Close stale pull requests 2024-10-13 21:55:29 +02:00
Grégoire Paris
4ff909044e Update README (#11673) 2024-10-13 21:45:35 +02:00
Alexander M. Turek
32682aa14d Fix PHPUnit deprecations 2024-10-13 21:17:33 +02:00
Alexander M. Turek
e9f3ca2a45 Drop DBAL 2 compat code (#11674) 2024-10-13 20:48:11 +02:00
Grégoire Paris
737aee7d98 Update branch metadata (#11672)
3.3.0 was just released.
2024-10-13 20:47:57 +02:00
dependabot[bot]
bd20df1043 Bump doctrine/.github from 5.1.0 to 5.2.0 (#11671) 2024-10-13 12:47:29 +02:00
Grégoire Paris
2ec2030ab2 Experiment with literalinclude
I think it would be great to use literalinclude for big code snippets,
because our IDEs could warn us about issues, and it would be easily to
showcase our coding standard. Before we do that though, let us validate
that it renders as expected. I have picked a complex example where we
have a configuration block.
2024-10-12 15:46:50 +02:00
Maciej Malarz
129553da90 Allow overriding association's cascade 2024-07-11 11:50:04 +02:00
MatteoFeltrin
73e30df52b allow classname in 'value' attribute of xml discriminator-mapping field 2024-05-20 11:00:23 +02:00
d-ph
0a49274f9b Update aggregate-fields.rst
Change `PESSIMISTIC_READ` to `PESSIMISTIC_WRITE`. Otherwise, the solution to the race condition at the bottom of the article would allow concurrent reads, which would not solve the presented race condition problem.
2024-03-08 09:36:42 +00:00
335 changed files with 5270 additions and 2926 deletions

View File

@@ -11,17 +11,23 @@
"slug": "latest",
"upcoming": true
},
{
"name": "3.4",
"branchName": "3.4.x",
"slug": "3.4",
"upcoming": true
},
{
"name": "3.3",
"branchName": "3.3.x",
"slug": "3.3",
"upcoming": true
"current": true
},
{
"name": "3.2",
"branchName": "3.2.x",
"slug": "3.2",
"current": true
"maintained": false
},
{
"name": "3.1",

2
.gitattributes vendored
View File

@@ -19,5 +19,3 @@ phpstan-baseline.neon export-ignore
phpstan-dbal2.neon export-ignore
phpstan-params.neon export-ignore
phpstan-persistence2.neon export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore

View File

@@ -1,37 +0,0 @@
---
name: 💥 BC Break
about: Have you encountered an issue during upgrade? 💣
---
<!--
Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/2.9.x/UPGRADE.md
-->
### BC Break Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Previous behavior
<!-- What was the previous (working) behavior? -->
#### Current behavior
<!-- What is the current (broken) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the BC break.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit it in a Pull Request separately, referencing this bug report.
-->

View File

@@ -1,34 +0,0 @@
---
name: 🐞 Bug Report
about: Something is broken? 🔨
---
### Bug Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes/no
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Current behavior
<!-- What is the current (buggy) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the bug.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
-->
#### Expected behavior
<!-- What was the expected (correct) behavior? -->

View File

@@ -1,18 +0,0 @@
---
name: 🎉 Feature Request
about: You have a neat idea that should be implemented? 🎩
---
### Feature Request
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the feature you would like to see implemented. -->

View File

@@ -1,6 +0,0 @@
---
name: ❓ Support Question
about: Have a problem that you can't figure out? 🤔
---
Please use https://github.com/doctrine/orm/discussions instead.

View File

@@ -6,4 +6,4 @@ updates:
interval: "weekly"
labels:
- "CI"
target-branch: "2.19.x"
target-branch: "2.20.x"

View File

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

View File

@@ -113,7 +113,7 @@ jobs:
- "default"
- "3.7"
postgres-version:
- "15"
- "17"
extension:
- pdo_pgsql
- pgsql
@@ -323,6 +323,8 @@ jobs:
upload_coverage:
name: "Upload coverage to Codecov"
runs-on: "ubuntu-22.04"
# Only run on PRs from forks
if: "github.event.pull_request.head.repo.full_name != github.repository"
needs:
- "phpunit-smoke-check"
- "phpunit-postgres"
@@ -341,7 +343,7 @@ jobs:
path: "reports"
- name: "Upload to Codecov"
uses: "codecov/codecov-action@v4"
uses: "codecov/codecov-action@v5"
with:
directory: reports
env:

View File

@@ -17,4 +17,4 @@ on:
jobs:
documentation:
name: "Documentation"
uses: "doctrine/.github/.github/workflows/documentation.yml@5.1.0"
uses: "doctrine/.github/.github/workflows/documentation.yml@7.2.1"

View File

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

24
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: 'Close stale pull requests'
on:
schedule:
- cron: '0 3 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-pr-message: >
There hasn't been any activity on this pull request in the past 90 days, so
it has been marked as stale and it will be closed automatically if no
further activity occurs in the next 7 days.
If you want to continue working on it, please leave a comment.
close-pr-message: >
This pull request was closed due to inactivity.
days-before-stale: -1
days-before-pr-stale: 90
days-before-pr-close: 7

View File

@@ -9,7 +9,6 @@ on:
- composer.*
- src/**
- phpstan*
- psalm*
- tests/StaticAnalysis/**
push:
branches:
@@ -19,7 +18,6 @@ on:
- composer.*
- src/**
- phpstan*
- psalm*
- tests/StaticAnalysis/**
jobs:
@@ -43,7 +41,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: "8.3"
php-version: "8.4"
tools: cs2pr
- name: Require specific DBAL version
@@ -56,34 +54,3 @@ jobs:
- name: Run static analysis with phpstan/phpstan
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"
static-analysis-psalm:
name: Static Analysis with Psalm
runs-on: ubuntu-22.04
strategy:
matrix:
dbal-version:
- default
- 3.8.2
steps:
- name: "Checkout code"
uses: "actions/checkout@v4"
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: "8.3"
tools: cs2pr
- name: Require specific DBAL version
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
if: "${{ matrix.dbal-version != 'default' }}"
- name: Install dependencies with Composer
uses: ramsey/composer-install@v3
- name: Run static analysis with Vimeo Psalm
run: vendor/bin/psalm --shepherd

21
.github/workflows/website-schema.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: "Website config validation"
on:
pull_request:
branches:
- "*.x"
paths:
- ".doctrine-project.json"
- ".github/workflows/website-schema.yml"
push:
branches:
- "*.x"
paths:
- ".doctrine-project.json"
- ".github/workflows/website-schema.yml"
jobs:
json-validate:
name: "Validate JSON schema"
uses: "doctrine/.github/.github/workflows/website-schema.yml@7.1.0"

View File

@@ -1,9 +1,7 @@
| [4.0.x][4.0] | [3.3.x][3.3] | [3.2.x][3.2] | [2.20.x][2.20] | [2.19.x][2.19] |
| [4.0.x][4.0] | [3.4.x][3.4] | [3.3.x][3.3] | [2.21.x][2.21] | [2.20.x][2.20] |
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
| [![Build status][4.0 image]][4.0] | [![Build status][3.3 image]][3.3] | [![Build status][3.2 image]][3.2] | [![Build status][2.20 image]][2.20] | [![Build status][2.19 image]][2.19] |
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][3.2 coverage image]][3.2 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] |
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
| [![Build status][4.0 image]][4.0] | [![Build status][3.4 image]][3.4] | [![Build status][3.3 image]][3.3] | [![Build status][2.21 image]][2.21] | [![Build status][2.20 image]][2.20] |
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.4 coverage image]][3.4 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
@@ -22,19 +20,19 @@ without requiring unnecessary code duplication.
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
[3.4 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.4.x
[3.4]: https://github.com/doctrine/orm/tree/3.4.x
[3.4 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.4.x/graph/badge.svg
[3.4 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.4.x
[3.3 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.3.x
[3.3]: https://github.com/doctrine/orm/tree/3.3.x
[3.3 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.3.x/graph/badge.svg
[3.3 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.3.x
[3.2 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.2.x
[3.2]: https://github.com/doctrine/orm/tree/3.2.x
[3.2 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.2.x/graph/badge.svg
[3.2 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.2.x
[2.21 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.21.x
[2.21]: https://github.com/doctrine/orm/tree/2.21.x
[2.21 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.21.x/graph/badge.svg
[2.21 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.21.x
[2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
[2.19 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.19.x
[2.19]: https://github.com/doctrine/orm/tree/2.19.x
[2.19 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.19.x/graph/badge.svg
[2.19 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.19.x

View File

@@ -1,9 +1,24 @@
# Upgrade to 3.4
Using the same class several times in a discriminator map is deprecated.
In 4.0, this will be an error.
# Upgrade to 3.3
## Deprecate `DatabaseDriver`
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is deprecated without replacement.
## 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.
# Upgrade to 3.2
## Deprecate the `NotSupported` exception
@@ -109,6 +124,36 @@ WARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydra
`Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed.
- `Doctrine\ORM\EntityManager*::getPartialReference()` is removed.
## BC BREAK: Enforce ArrayCollection Type on `\Doctrine\ORM\QueryBuilder::setParameters(ArrayCollection $parameters)`
The argument $parameters can no longer be a key=>value array. Only ArrayCollection types are allowed.
### Before
```php
$qb = $em->createQueryBuilder()
->select('u')
->from('User', 'u')
->where('u.id = :user_id1 OR u.id = :user_id2')
->setParameter(array(
'user_id1' => 1,
'user_id2' => 2
));
```
### After
```php
$qb = $em->createQueryBuilder()
->select('u')
->from('User', 'u')
->where('u.id = :user_id1 OR u.id = :user_id2')
->setParameter(new ArrayCollection(array(
new Parameter('user_id1', 1),
new Parameter('user_id2', 2)
)));
```
## BC BREAK: `Doctrine\ORM\Persister\Entity\EntityPersister::executeInserts()` return type changed to `void`
Implementors should adapt to the new signature, and should call
@@ -737,7 +782,7 @@ Use `toIterable()` instead.
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
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.
@@ -750,7 +795,7 @@ change in behavior.
Progress on this is tracked at https://github.com/doctrine/orm/issues/11624 .
## PARTIAL DQL syntax is undeprecated
## 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

View File

@@ -31,7 +31,7 @@
"doctrine/inflector": "^1.4 || ^2.0",
"doctrine/instantiator": "^1.3 || ^2",
"doctrine/lexer": "^3",
"doctrine/persistence": "^3.3.1",
"doctrine/persistence": "^3.3.1 || ^4",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"symfony/var-exporter": "^6.3.9 || ^7.0"
@@ -41,13 +41,12 @@
"phpbench/phpbench": "^1.0",
"phpdocumentor/guides-cli": "^1.4",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "1.12.6",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpstan/phpstan": "2.0.3",
"phpstan/phpstan-deprecation-rules": "^2",
"phpunit/phpunit": "^10.4.0",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^5.4 || ^6.2 || ^7.0",
"vimeo/psalm": "5.24.0"
"symfony/cache": "^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-dom": "Provides support for XSD validation for XML mapping files",

View File

@@ -1,91 +0,0 @@
#Copyright (c) 2010 Fabien Potencier
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is furnished
#to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
from docutils.parsers.rst import Directive, directives
from docutils import nodes
from string import upper
class configurationblock(nodes.General, nodes.Element):
pass
class ConfigurationBlock(Directive):
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
formats = {
'html': 'HTML',
'xml': 'XML',
'php': 'PHP',
'jinja': 'Twig',
'html+jinja': 'Twig',
'jinja+html': 'Twig',
'php+html': 'PHP',
'html+php': 'PHP',
'ini': 'INI',
}
def run(self):
env = self.state.document.settings.env
node = nodes.Element()
node.document = self.state.document
self.state.nested_parse(self.content, self.content_offset, node)
entries = []
for i, child in enumerate(node):
if isinstance(child, nodes.literal_block):
# add a title (the language name) before each block
#targetid = "configuration-block-%d" % env.new_serialno('configuration-block')
#targetnode = nodes.target('', '', ids=[targetid])
#targetnode.append(child)
innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']])
para = nodes.paragraph()
para += [innernode, child]
entry = nodes.list_item('')
entry.append(para)
entries.append(entry)
resultnode = configurationblock()
resultnode.append(nodes.bullet_list('', *entries))
return [resultnode]
def visit_configurationblock_html(self, node):
self.body.append(self.starttag(node, 'div', CLASS='configuration-block'))
def depart_configurationblock_html(self, node):
self.body.append('</div>\n')
def visit_configurationblock_latex(self, node):
pass
def depart_configurationblock_latex(self, node):
pass
def setup(app):
app.add_node(configurationblock,
html=(visit_configurationblock_html, depart_configurationblock_html),
latex=(visit_configurationblock_latex, depart_configurationblock_latex))
app.add_directive('configuration-block', ConfigurationBlock)

View File

@@ -352,7 +352,7 @@ the database using a FOR UPDATE.
use Bank\Entities\Account;
use Doctrine\DBAL\LockMode;
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_WRITE);
Keeping Updates and Deletes in Sync
-----------------------------------

View File

@@ -1,113 +0,0 @@
@ECHO OFF
REM Command file for Sphinx documentation
set SPHINXBUILD=sphinx-build
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

View File

@@ -33,14 +33,13 @@ Doctrine ORM is divided into four main packages.
- ORM (depends on DBAL+Persistence+Collections)
This manual mainly covers the ORM package, sometimes touching parts
of the underlying DBAL and Persistence packages. The Doctrine code base
is split in to these packages for a few reasons and they are to...
of the underlying DBAL and Persistence packages. The Doctrine codebase
is split into these packages for a few reasons:
- ...make things more maintainable and decoupled
- ...allow you to use the code in Doctrine Persistence and Collections
without the ORM or DBAL
- ...allow you to use the DBAL without the ORM
- to make things more maintainable and decoupled
- to allow you to use the code in Doctrine Persistence and Collections without the ORM or DBAL
- to allow you to use the DBAL without the ORM
Collection, Event Manager and Persistence
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -903,8 +903,7 @@ defaults to "id", just as in one-to-one or many-to-one mappings.
Additionally, when using typed properties with Doctrine 2.9 or newer
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
associations as they will be set based on type. Also ``nullable``
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
associations as they will be set based on type. So that:
.. configuration-block::
@@ -931,7 +930,7 @@ Is essentially the same as following:
<?php
/** One Product has One Shipment. */
#[OneToOne(targetEntity: Shipment::class)]
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id', nullable: false)]
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]
private Shipment $shipment;
.. code-block:: annotation
@@ -940,7 +939,7 @@ Is essentially the same as following:
/**
* One Product has One Shipment.
* @OneToOne(targetEntity="Shipment")
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
* @JoinColumn(name="shipment_id", referencedColumnName="id")
*/
private Shipment $shipment;

View File

@@ -167,6 +167,7 @@ Here is a complete list of ``Column``s attributes (all optional):
- ``nullable`` (default: ``false``): Whether the column is nullable.
- ``insertable`` (default: ``true``): Whether the column should be inserted.
- ``updatable`` (default: ``true``): Whether the column should be updated.
- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
(applies only for decimal column),

View File

@@ -16,7 +16,7 @@ is common to multiple entity classes.
Mapped superclasses, just as regular, non-mapped classes, can
appear in the middle of an otherwise mapped inheritance hierarchy
(through Single Table Inheritance or Class Table Inheritance). They
are not query-able, and need not have an ``#[Id]`` property.
are not query-able, and do not require an ``#[Id]`` property.
No database table will be created for a mapped superclass itself,
only for entity classes inheriting from it. That implies that a

View File

@@ -102,7 +102,7 @@ How Doctrine Detects Changes
----------------------------
Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI).
This means you map php objects into a relational database that don't
This means you map PHP objects into a relational database that don't
necessarily know about the database at all. A natural question would now be,
"how does Doctrine even detect objects have changed?".

View File

@@ -166,7 +166,7 @@ your code. See the following code:
Traversing the object graph for parts that are lazy-loaded will
easily trigger lots of SQL queries and will perform badly if used
to heavily. Make sure to use DQL to fetch-join all the parts of the
too heavily. Make sure to use DQL to fetch-join all the parts of the
object-graph that you need as efficiently as possible.

View File

@@ -17,7 +17,9 @@ can be called without triggering a full load of the collection:
- ``Collection#contains($entity)``
- ``Collection#containsKey($key)``
- ``Collection#count()``
- ``Collection#first()``
- ``Collection#get($key)``
- ``Collection#isEmpty()``
- ``Collection#slice($offset, $length = null)``
For each of the above methods the following semantics apply:

View File

@@ -9,7 +9,7 @@ i.e. attributes and associations metadata in particular. The example here shows
the overriding of a class that uses a trait but is similar when extending a base
class as shown at the end of this tutorial.
Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait:
Suppose we have a class ``ExampleEntityWithOverride``. This class uses trait ``ExampleTrait``:
.. code-block:: php
@@ -17,22 +17,20 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
#[Entity]
#[AttributeOverrides([
new AttributeOverride('foo', [
'column' => new Column([
'name' => 'foo_overridden',
'type' => 'integer',
'length' => 140,
'nullable' => false,
'unique' => false,
]),
]),
new AttributeOverride('foo', new Column(
name: 'foo_overridden',
type: 'integer',
length: 140,
nullable: false,
unique: false,
)),
])]
#[AssociationOverrides([
new AssociationOverride('bar', [
'joinColumns' => new JoinColumn([
'name' => 'example_entity_overridden_bar_id',
'referencedColumnName' => 'id',
]),
new JoinColumn(
name: 'example_entity_overridden_bar_id',
referencedColumnName: 'id',
),
]),
])]
class ExampleEntityWithOverride
@@ -47,7 +45,7 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
private $id;
}
The docblock is showing metadata override of the attribute and association type. It
``#[AttributeOverrides]`` contains metadata override of the attribute and association type. It
basically changes the names of the columns mapped for a property ``foo`` and for
the association ``bar`` which relates to Bar class shown above. Here is the trait
which has mapping metadata that is overridden by the attribute above:

View File

@@ -29,83 +29,11 @@ You can map indexed associations by adding:
The code and mappings for the Market entity looks like this:
.. configuration-block::
.. code-block:: attribute
.. literalinclude:: working-with-indexed-associations/Market.php
:language: attribute
<?php
namespace Doctrine\Tests\Models\StockExchange;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[Entity]
#[Table(name: 'exchange_markets')]
class Market
{
#[Id, Column(type: 'integer'), GeneratedValue]
private int|null $id = null;
#[Column(type: 'string')]
private string $name;
/** @var Collection<string, Stock> */
#[OneToMany(targetEntity: Stock::class, mappedBy: 'market', indexBy: 'symbol')]
private Collection $stocks;
public function __construct(string $name)
{
$this->name = $name;
$this->stocks = new ArrayCollection();
}
public function getId(): int|null
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function addStock(Stock $stock): void
{
$this->stocks[$stock->getSymbol()] = $stock;
}
public function getStock(string $symbol): Stock
{
if (!isset($this->stocks[$symbol])) {
throw new \InvalidArgumentException("Symbol is not traded on this market.");
}
return $this->stocks[$symbol];
}
/** @return array<string, Stock> */
public function getStocks(): array
{
return $this->stocks->toArray();
}
}
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Market">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="name" type="string"/>
<one-to-many target-entity="Stock" mapped-by="market" field="stocks" index-by="symbol" />
</entity>
</doctrine-mapping>
.. literalinclude:: working-with-indexed-associations/market.xml
:language: xml
Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol,
so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)``

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\StockExchange;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\Table;
use InvalidArgumentException;
#[Entity]
#[Table(name: 'exchange_markets')]
class Market
{
#[Id]
#[Column(type: 'integer')]
#[GeneratedValue]
private int|null $id = null;
#[Column(type: 'string')]
private string $name;
/** @var Collection<string, Stock> */
#[OneToMany(targetEntity: Stock::class, mappedBy: 'market', indexBy: 'symbol')]
private Collection $stocks;
public function __construct(string $name)
{
$this->name = $name;
$this->stocks = new ArrayCollection();
}
public function getId(): int|null
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function addStock(Stock $stock): void
{
$this->stocks[$stock->getSymbol()] = $stock;
}
public function getStock(string $symbol): Stock
{
if (! isset($this->stocks[$symbol])) {
throw new InvalidArgumentException('Symbol is not traded on this market.');
}
return $this->stocks[$symbol];
}
/** @return array<string, Stock> */
public function getStocks(): array
{
return $this->stocks->toArray();
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Market">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="name" type="string"/>
<one-to-many target-entity="Stock" mapped-by="market" field="stocks" index-by="symbol" />
</entity>
</doctrine-mapping>

View File

@@ -321,7 +321,7 @@
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="value" type="orm:type" use="required"/>
<xs:attribute name="class" type="orm:fqcn" use="required"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>

View File

@@ -1,5 +1,7 @@
<?xml version="1.0"?>
<ruleset>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="PHP_CodeSniffer"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
@@ -48,6 +50,8 @@
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>src/Mapping/Driver/LoadMappingFileImplementation.php</exclude-pattern>
<exclude-pattern>src/Mapping/GetReflectionClassImplementation.php</exclude-pattern>
<exclude-pattern>tests/*</exclude-pattern>
</rule>

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ includes:
- phpstan-params.neon
parameters:
reportUnmatchedIgnoredErrors: false # Some errors in the baseline only apply to DBAL 4
ignoreErrors:
# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
@@ -16,9 +17,10 @@ parameters:
-
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
path: src/Query/AST/Functions/TrimFunction.php
-
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
path: src/Persisters/Entity/BasicEntityPersister.php
message: '~.*getTrimExpression.*expects int.*~'
path: src/Query/AST/Functions/TrimFunction.php
- '~^Class Doctrine\\DBAL\\Platforms\\SQLitePlatform not found\.$~'
@@ -34,3 +36,42 @@ parameters:
-
message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~'
path: src/Tools/Console/ConsoleRunner.php
# Compatibility with Persistence 3
-
message: '#Expression on left side of \?\? is not nullable.#'
path: src/Mapping/Driver/AttributeDriver.php
-
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
path: src/Persisters/Entity/BasicEntityPersister.php
-
message: '~getTypes.*should return~'
path: src/Persisters/Entity/BasicEntityPersister.php
-
message: '~.*appendLockHint.*expects.*LockMode given~'
paths:
- src/Persisters/Entity/BasicEntityPersister.php
- src/Persisters/Entity/JoinedSubclassPersister.php
-
message: '~.*executeStatement.*expects~'
path: src/Query/Exec/MultiTableUpdateExecutor.php
-
message: '~method_exists.*getEventManager~'
path: src/EntityManager.php
-
message: '~method_exists.*getIdentitySequence~'
path: src/Mapping/ClassMetadataFactory.php
-
message: '~expand(Criteria)?Parameters.*should return array~'
path: src/Persisters/Entity/BasicEntityPersister.php
-
message: '~inferType.*never returns~'
path: src/Query/ParameterTypeInferer.php

View File

@@ -1,5 +1,5 @@
parameters:
level: 5
level: 7
paths:
- src
- tests/StaticAnalysis
@@ -8,4 +8,4 @@ parameters:
earlyTerminatingMethodCalls:
Doctrine\ORM\Query\Parser:
- syntaxError
phpVersion: 80200
phpVersion: 80400

View File

@@ -45,3 +45,8 @@ parameters:
message: '#Negated boolean expression is always false\.#'
paths:
- src/Mapping/Driver/AttributeDriver.php
# Compatibility with Persistence 3
-
message: '#Expression on left side of \?\? is not nullable.#'
path: src/Mapping/Driver/AttributeDriver.php

View File

@@ -14,6 +14,7 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true"
failOnNotice="true"
@@ -67,5 +68,12 @@
<var name="privileged_db_port" value="3306"/>
-->
<env name="COLUMNS" value="120"/>
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
</php>
<source ignoreSuppressionOfDeprecations="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>

File diff suppressed because it is too large Load Diff

245
psalm.xml
View File

@@ -1,245 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="2"
phpVersion="8.2"
resolveFromConfigFile="true"
findUnusedBaselineEntry="true"
findUnusedCode="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
<directory name="tests/StaticAnalysis" />
<ignoreFiles>
<directory name="vendor" />
<file name="src/Mapping/Driver/AttributeReader.php" />
</ignoreFiles>
</projectFiles>
<enableExtensions>
<extension name="simplexml" />
</enableExtensions>
<issueHandlers>
<DeprecatedClass>
<errorLevel type="suppress">
<!-- We wire the command as long as DBAL ships it -->
<referencedClass name="Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand" />
<!-- Remove on 3.0.x -->
<referencedClass name="Doctrine\ORM\Event\LifecycleEventArgs"/>
<referencedClass name="Doctrine\ORM\Exception\UnknownEntityNamespace"/>
<referencedClass name="Doctrine\ORM\Mapping\Driver\AnnotationDriver"/>
<referencedClass name="Doctrine\ORM\Mapping\Driver\YamlDriver"/>
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQueries"/>
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQuery"/>
<referencedClass name="Doctrine\ORM\Mapping\NamedQueries"/>
<referencedClass name="Doctrine\ORM\Mapping\NamedQuery"/>
<referencedClass name="Doctrine\ORM\Query\AST\InExpression"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand"/>
<referencedClass name="Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper"/>
<referencedClass name="Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider"/>
</errorLevel>
</DeprecatedClass>
<DeprecatedMethod>
<errorLevel type="suppress">
<!-- Remove on 3.0.x -->
<!-- Compatibility with DBAL 3 -->
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
<file name="src/Query/TreeWalkerChain.php"/>
</errorLevel>
</DeprecatedMethod>
<DocblockTypeContradiction>
<errorLevel type="suppress">
<!-- We're catching invalid input here. -->
<file name="src/Internal/Hydration/AbstractHydrator.php"/>
<!-- DBAL 3.2 forward compatibility -->
<file name="src/Tools/Pagination/CountOutputWalker.php"/>
<file name="src/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
<!-- https://github.com/vimeo/psalm/issues/8520 -->
<file name="src/PersistentCollection.php"/>
<!-- Remove on 4.0.x -->
<file name="src/Mapping/Driver/AttributeDriver.php"/>
<file name="src/Mapping/Driver/XmlDriver.php"/>
<file name="src/ORMSetup.php"/>
</errorLevel>
</DocblockTypeContradiction>
<ForbiddenCode>
<errorLevel type="suppress">
<file name="src/Tools/Debug.php"/>
</errorLevel>
</ForbiddenCode>
<InvalidArgument>
<errorLevel type="suppress">
<referencedFunction name="Doctrine\ORM\Mapping\ClassMetadata::addInheritedAssociationMapping"/>
</errorLevel>
</InvalidArgument>
<InvalidArrayAccess>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/9160 -->
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</InvalidArrayAccess>
<InvalidArrayAssignment>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/9160 -->
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</InvalidArrayAssignment>
<LessSpecificReturnStatement>
<errorLevel type="suppress">
<!-- In DBAL 4, column precision is nullable. See https://github.com/doctrine/dbal/pull/3511 -->
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
</errorLevel>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<errorLevel type="suppress">
<!-- In DBAL 4, the default column value is mixed. See https://github.com/doctrine/dbal/pull/3511 -->
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
</errorLevel>
</MoreSpecificReturnType>
<InvalidReturnType>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/8819 -->
<file name="src/Internal/Hydration/AbstractHydrator.php"/>
</errorLevel>
</InvalidReturnType>
<InvalidParamDefault>
<errorLevel type="suppress">
<!-- Remove on 3.0.x -->
<file name="src/Query/AST/InstanceOfExpression.php"/>
</errorLevel>
</InvalidParamDefault>
<InvalidPropertyAssignmentValue>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/9155 -->
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</InvalidPropertyAssignmentValue>
<MethodSignatureMismatch>
<errorLevel type="suppress">
<!-- See https://github.com/vimeo/psalm/issues/7357 -->
<file name="src/Mapping/ReflectionReadonlyProperty.php"/>
</errorLevel>
</MethodSignatureMismatch>
<MissingParamType>
<errorLevel type="suppress">
<!-- Persistence 2 compatibility -->
<file name="src/EntityManager.php"/>
<file name="src/Mapping/ClassMetadataFactory.php"/>
<file name="src/Mapping/ClassMetadata.php"/>
</errorLevel>
</MissingParamType>
<PossiblyInvalidArgument>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/9155 -->
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</PossiblyInvalidArgument>
<PossiblyNullArrayOffset>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/7878 -->
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
</errorLevel>
</PossiblyNullArrayOffset>
<PropertyNotSetInConstructor>
<errorLevel type="suppress">
<directory name="src/Query/AST" />
</errorLevel>
</PropertyNotSetInConstructor>
<PropertyTypeCoercion>
<errorLevel type="suppress">
<file name="src/Mapping/ClassMetadata.php"/>
</errorLevel>
</PropertyTypeCoercion>
<RedundantCastGivenDocblockType>
<errorLevel type="suppress">
<!-- Can be removed once the "getMaxResults" methods of those classes have native parameter types -->
<file name="src/Query.php"/>
<file name="src/QueryBuilder.php"/>
</errorLevel>
</RedundantCastGivenDocblockType>
<ReferenceConstraintViolation>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/9155 -->
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</ReferenceConstraintViolation>
<RiskyTruthyFalsyComparison>
<!-- TODO: Enable this new rule on higher branches. -->
<errorLevel type="suppress">
<directory name="src" />
</errorLevel>
</RiskyTruthyFalsyComparison>
<TooManyArguments>
<errorLevel type="suppress">
<!-- Symfony cache supports passing a key prefix to the clear method. -->
<referencedFunction name="Psr\Cache\CacheItemPoolInterface::clear"/>
<!-- Persistence 2 compatibility -->
<referencedFunction name="Doctrine\Persistence\ObjectManager::clear"/>
<!-- See https://github.com/doctrine/orm/issues/8850 -->
<referencedFunction name="Doctrine\DBAL\Connection::lastInsertId"/>
<!-- FIXME -->
<referencedFunction name="Doctrine\DBAL\DriverManager::getConnection"/>
</errorLevel>
</TooManyArguments>
<TypeDoesNotContainNull>
<errorLevel type="suppress">
<!-- DBAL 3 compatibility -->
<file name="src/Tools/SchemaTool.php"/>
</errorLevel>
</TypeDoesNotContainNull>
<TypeDoesNotContainType>
<errorLevel type="suppress">
<file name="src/Internal/SQLResultCasing.php"/>
<file name="src/Mapping/ClassMetadataFactory.php"/>
<!-- DBAL 3 compatibility -->
<file name="src/UnitOfWork.php"/>
<file name="src/Utility/LockSqlHelper.php"/>
</errorLevel>
</TypeDoesNotContainType>
<UndefinedClass>
<errorLevel type="suppress">
<!-- Compatibility with DBAL 3 -->
<referencedClass name="Doctrine\DBAL\Platforms\SQLitePlatform"/>
</errorLevel>
</UndefinedClass>
<UndefinedMethod>
<errorLevel type="suppress">
<!-- Compatibility with DBAL 3 -->
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
<!-- FIXME -->
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSaveSql"/>
</errorLevel>
</UndefinedMethod>
<UndefinedPropertyFetch>
<errorLevel type="suppress">
<!-- https://github.com/vimeo/psalm/issues/7878 -->
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
<file name="src/PersistentCollection.php"/>
<file name="src/Utility/PersisterHelper.php"/>
<file name="src/Tools/SchemaValidator.php"/>
</errorLevel>
</UndefinedPropertyFetch>
<UnhandledMatchCondition>
<errorLevel type="suppress">
<!-- We can be certain that those values are not matched. -->
<file name="src/Persisters/Entity/BasicEntityPersister.php"/>
</errorLevel>
</UnhandledMatchCondition>
<ArgumentTypeCoercion>
<errorLevel type="suppress">
<!-- See https://github.com/JetBrains/phpstorm-stubs/pull/1383 -->
<file name="src/Mapping/ClassMetadata.php"/>
</errorLevel>
</ArgumentTypeCoercion>
</issueHandlers>
</psalm>

View File

@@ -83,7 +83,7 @@ abstract class AbstractQuery
* The parameter map of this query.
*
* @var ArrayCollection|Parameter[]
* @psalm-var ArrayCollection<int, Parameter>
* @phpstan-var ArrayCollection<int, Parameter>
*/
protected ArrayCollection $parameters;
@@ -95,14 +95,14 @@ abstract class AbstractQuery
/**
* The map of query hints.
*
* @psalm-var array<string, mixed>
* @phpstan-var array<string, mixed>
*/
protected array $hints = [];
/**
* The hydration mode.
*
* @psalm-var string|AbstractQuery::HYDRATE_*
* @phpstan-var string|AbstractQuery::HYDRATE_*
*/
protected string|int $hydrationMode = self::HYDRATE_OBJECT;
@@ -130,7 +130,7 @@ abstract class AbstractQuery
/**
* Second level query cache mode.
*
* @psalm-var Cache::MODE_*|null
* @phpstan-var Cache::MODE_*|null
*/
protected int|null $cacheMode = null;
@@ -217,14 +217,14 @@ abstract class AbstractQuery
return $this;
}
/** @psalm-return Cache::MODE_*|null */
/** @phpstan-return Cache::MODE_*|null */
public function getCacheMode(): int|null
{
return $this->cacheMode;
}
/**
* @psalm-param Cache::MODE_* $cacheMode
* @phpstan-param Cache::MODE_* $cacheMode
*
* @return $this
*/
@@ -267,7 +267,7 @@ abstract class AbstractQuery
/**
* Get all defined parameters.
*
* @psalm-return ArrayCollection<int, Parameter>
* @phpstan-return ArrayCollection<int, Parameter>
*/
public function getParameters(): ArrayCollection
{
@@ -296,14 +296,14 @@ abstract class AbstractQuery
* Sets a collection of query parameters.
*
* @param ArrayCollection|mixed[] $parameters
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
* @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters
*
* @return $this
*/
public function setParameters(ArrayCollection|array $parameters): static
{
if (is_array($parameters)) {
/** @psalm-var ArrayCollection<int, Parameter> $parameterCollection */
/** @phpstan-var ArrayCollection<int, Parameter> $parameterCollection */
$parameterCollection = new ArrayCollection();
foreach ($parameters as $key => $value) {
@@ -642,7 +642,7 @@ abstract class AbstractQuery
* Change the default fetch mode of an association for this query.
*
* @param class-string $class
* @psalm-param Mapping\ClassMetadata::FETCH_EAGER|Mapping\ClassMetadata::FETCH_LAZY $fetchMode
* @phpstan-param Mapping\ClassMetadata::FETCH_EAGER|Mapping\ClassMetadata::FETCH_LAZY $fetchMode
*/
public function setFetchMode(string $class, string $assocName, int $fetchMode): static
{
@@ -656,7 +656,7 @@ abstract class AbstractQuery
*
* @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Query::HYDRATE_* constants.
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
*
* @return $this
*/
@@ -670,7 +670,7 @@ abstract class AbstractQuery
/**
* Gets the hydration mode currently used by the query.
*
* @psalm-return string|AbstractQuery::HYDRATE_*
* @phpstan-return string|AbstractQuery::HYDRATE_*
*/
public function getHydrationMode(): string|int
{
@@ -682,7 +682,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
*
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
*/
public function getResult(string|int $hydrationMode = self::HYDRATE_OBJECT): mixed
{
@@ -728,7 +728,7 @@ abstract class AbstractQuery
/**
* Get exactly one result or null.
*
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*
* @throws NonUniqueResultException
*/
@@ -763,7 +763,7 @@ abstract class AbstractQuery
* If the result is not unique, a NonUniqueResultException is thrown.
* If there is no result, a NoResultException is thrown.
*
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*
* @throws NonUniqueResultException If the query result is not unique.
* @throws NoResultException If the query returned no result.
@@ -843,8 +843,8 @@ abstract class AbstractQuery
* Executes the query and returns an iterable that can be used to incrementally
* iterate over the result.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*
* @return iterable<mixed>
*/
@@ -877,8 +877,8 @@ abstract class AbstractQuery
/**
* Executes the query.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*/
public function execute(
ArrayCollection|array|null $parameters = null,
@@ -894,8 +894,8 @@ abstract class AbstractQuery
/**
* Execute query ignoring second level cache.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*/
private function executeIgnoreQueryCache(
ArrayCollection|array|null $parameters = null,
@@ -965,8 +965,8 @@ abstract class AbstractQuery
/**
* Load from second level cache or executes the query and put into cache.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
*/
private function executeUsingQueryCache(
ArrayCollection|array|null $parameters = null,
@@ -1029,7 +1029,7 @@ abstract class AbstractQuery
* automatically generated for you.
*
* @return string[] ($key, $hash)
* @psalm-return array{string, string} ($key, $hash)
* @phpstan-return array{string, string} ($key, $hash)
*/
protected function getHydrationCacheId(): array
{

View File

@@ -29,11 +29,14 @@ class CollectionCacheKey extends CacheKey
public readonly string $entityClass,
public readonly string $association,
array $ownerIdentifier,
string $filterHash = '',
) {
ksort($ownerIdentifier);
$this->ownerIdentifier = $ownerIdentifier;
parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association);
$filterHash = $filterHash === '' ? '' : '_' . $filterHash;
parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association . $filterHash);
}
}

View File

@@ -25,7 +25,7 @@ class DefaultCache implements Cache
/**
* @var QueryCache[]
* @psalm-var array<string, QueryCache>
* @phpstan-var array<string, QueryCache>
*/
private array $queryCaches = [];

View File

@@ -231,7 +231,6 @@ class DefaultQueryCache implements QueryCache
$region = $persister->getCacheRegion();
$cm = $this->em->getClassMetadata($entityName);
assert($cm instanceof ClassMetadata);
foreach ($result as $index => $entity) {
$identifier = $this->uow->getEntityIdentifier($entity);
@@ -297,7 +296,7 @@ class DefaultQueryCache implements QueryCache
/**
* @return mixed[]|null
* @psalm-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
* @phpstan-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
*/
private function storeAssociationCache(QueryCacheKey $key, AssociationMapping $assoc, mixed $assocValue): array|null
{
@@ -348,7 +347,7 @@ class DefaultQueryCache implements QueryCache
];
}
/** @psalm-return list<mixed>|object|null */
/** @phpstan-return list<mixed>|object|null */
private function getAssociationValue(
ResultSetMapping $rsm,
string $assocAlias,
@@ -374,9 +373,9 @@ class DefaultQueryCache implements QueryCache
}
/**
* @psalm-param array<array-key, array{field: string, class: string}> $path
* @phpstan-param array<array-key, array{field: string, class: string}> $path
*
* @psalm-return list<mixed>|object|null
* @phpstan-return list<mixed>|object|null
*/
private function getAssociationPathValue(mixed $value, array $path): array|object|null
{

View File

@@ -18,6 +18,7 @@ use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Doctrine\ORM\Query\FilterCollection;
use Doctrine\ORM\UnitOfWork;
use function array_values;
@@ -35,6 +36,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
protected array $queuedCache = [];
protected string $regionName;
protected FilterCollection $filters;
protected CollectionHydrator $hydrator;
protected CacheLogger|null $cacheLogger;
@@ -48,6 +50,10 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->filters = $em->getFilters();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
@@ -135,7 +141,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
public function count(PersistentCollection $collection): int
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$entry = $this->region->get($key);
if ($entry !== null) {

View File

@@ -36,7 +36,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
public function delete(PersistentCollection $collection): void
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$this->persister->delete($collection);
@@ -53,7 +53,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
}
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
// Invalidate non initialized collections OR ordered collection
if ($isDirty && ! $isInitialized || $this->association->isOrdered()) {

View File

@@ -61,7 +61,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
public function delete(PersistentCollection $collection): void
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$lock = $this->region->lock($key);
$this->persister->delete($collection);
@@ -88,7 +88,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
$this->persister->update($collection);
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$lock = $this->region->lock($key);
if ($lock === null) {

View File

@@ -24,11 +24,12 @@ use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Doctrine\ORM\Query\FilterCollection;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\UnitOfWork;
use function array_merge;
use function assert;
use function func_get_args;
use function serialize;
use function sha1;
@@ -44,6 +45,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
protected TimestampCacheKey $timestampKey;
protected EntityHydrator $hydrator;
protected Cache $cache;
protected FilterCollection $filters;
protected CacheLogger|null $cacheLogger = null;
protected string $regionName;
@@ -65,6 +67,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$cacheFactory = $cacheConfig->getCacheFactory();
$this->cache = $em->getCache();
$this->filters = $em->getFilters();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
@@ -216,7 +219,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
? $this->persister->expandCriteriaParameters($criteria)
: $this->persister->expandParameters($criteria);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $this->filters->getHash());
}
/**
@@ -473,7 +476,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
$list = $persister->loadCollectionCache($collection, $key);
if ($list !== null) {
@@ -504,7 +507,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
$list = $persister->loadCollectionCache($collection, $key);
if ($list !== null) {
@@ -547,11 +550,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
/** @param array<string, mixed> $ownerId */
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId): CollectionCacheKey
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey
{
$metadata = $this->metadataFactory->getMetadataFor($association->sourceEntity);
assert($metadata instanceof ClassMetadata);
$filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release
return new CollectionCacheKey($metadata->rootEntityName, $association->fieldName, $ownerId);
return new CollectionCacheKey(
$this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,
$association->fieldName,
$ownerId,
$filterHash,
);
}
}

View File

@@ -41,16 +41,16 @@ class Configuration extends \Doctrine\DBAL\Configuration
/** @var mixed[] */
protected array $attributes = [];
/** @psalm-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */
/** @phpstan-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */
private $identityGenerationPreferences = [];
/** @psalm-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
/** @phpstan-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
public function setIdentityGenerationPreferences(array $value): void
{
$this->identityGenerationPreferences = $value;
}
/** @psalm-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
/** @phpstan-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
public function getIdentityGenerationPreferences(): array
{
return $this->identityGenerationPreferences;
@@ -122,7 +122,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the entity alias map.
*
* @psalm-param array<string, string> $entityNamespaces
* @phpstan-param array<string, string> $entityNamespaces
*/
public function setEntityNamespaces(array $entityNamespaces): void
{
@@ -132,7 +132,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Retrieves the list of registered entity namespace aliases.
*
* @psalm-return array<string, string>
* @phpstan-return array<string, string>
*/
public function getEntityNamespaces(): array
{
@@ -191,7 +191,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* DQL function names are case-insensitive.
*
* @param class-string|callable $className Class name or a callable that returns the function.
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
*/
public function addCustomStringFunction(string $name, string|callable $className): void
{
@@ -201,7 +201,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom string DQL function.
*
* @psalm-return class-string<FunctionNode>|callable(string):FunctionNode|null
* @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null
*/
public function getCustomStringFunction(string $name): string|callable|null
{
@@ -218,7 +218,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* Any previously added string functions are discarded.
*
* @psalm-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions The map of custom
* @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions The map of custom
* DQL string functions.
*/
public function setCustomStringFunctions(array $functions): void
@@ -236,7 +236,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* DQL function names are case-insensitive.
*
* @param class-string|callable $className Class name or a callable that returns the function.
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
*/
public function addCustomNumericFunction(string $name, string|callable $className): void
{
@@ -246,7 +246,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom numeric DQL function.
*
* @psalm-return ?class-string<FunctionNode>|callable(string):FunctionNode
* @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null
*/
public function getCustomNumericFunction(string $name): string|callable|null
{
@@ -281,7 +281,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* DQL function names are case-insensitive.
*
* @param string|callable $className Class name or a callable that returns the function.
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
*/
public function addCustomDatetimeFunction(string $name, string|callable $className): void
{
@@ -309,7 +309,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Any previously added date/time functions are discarded.
*
* @param array $functions The map of custom DQL date/time functions.
* @psalm-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions
* @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions
*/
public function setCustomDatetimeFunctions(array $functions): void
{
@@ -538,7 +538,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Returns query hints, which will be applied to every query in application
*
* @psalm-return array<string, mixed>
* @phpstan-return array<string, mixed>
*/
public function getDefaultQueryHints(): array
{
@@ -548,7 +548,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets array of query hints, which will be applied to every query in application
*
* @psalm-param array<string, mixed> $defaultQueryHints
* @phpstan-param array<string, mixed> $defaultQueryHints
*/
public function setDefaultQueryHints(array $defaultQueryHints): void
{

View File

@@ -565,9 +565,9 @@ class EntityManager implements EntityManagerInterface
/**
* {@inheritDoc}
*/
public function isUninitializedObject($obj): bool
public function isUninitializedObject($value): bool
{
return $this->unitOfWork->isUninitializedObject($obj);
return $this->unitOfWork->isUninitializedObject($value);
}
public function getFilters(): FilterCollection
@@ -586,7 +586,7 @@ class EntityManager implements EntityManagerInterface
}
/**
* @psalm-param LockMode::* $lockMode
* @phpstan-param LockMode::* $lockMode
*
* @throws OptimisticLockException
* @throws TransactionRequiredException

View File

@@ -71,10 +71,10 @@ interface EntityManagerInterface extends ObjectManager
* If an exception occurs during execution of the function or flushing or transaction commit,
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
*
* @psalm-param callable(self): T $func The function to execute transactionally.
* @phpstan-param callable(self): T $func The function to execute transactionally.
*
* @return mixed The value returned from the closure.
* @psalm-return T
* @phpstan-return T
*
* @template T
*/
@@ -117,11 +117,11 @@ interface EntityManagerInterface extends ObjectManager
* during the search.
* @param int|null $lockVersion The version of the entity to find when using
* optimistic locking.
* @psalm-param class-string<T> $className
* @psalm-param LockMode::*|null $lockMode
* @phpstan-param class-string<T> $className
* @phpstan-param LockMode::*|null $lockMode
*
* @return object|null The entity instance or NULL if the entity can not be found.
* @psalm-return T|null
* @phpstan-return T|null
*
* @throws OptimisticLockException
* @throws ORMInvalidArgumentException
@@ -139,7 +139,7 @@ interface EntityManagerInterface extends ObjectManager
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* during the search.
* @psalm-param LockMode::*|null $lockMode
* @phpstan-param LockMode::*|null $lockMode
*
* @throws ORMInvalidArgumentException
* @throws ORMException
@@ -172,7 +172,7 @@ interface EntityManagerInterface extends ObjectManager
/**
* Acquire a lock on the given entity.
*
* @psalm-param LockMode::* $lockMode
* @phpstan-param LockMode::* $lockMode
*
* @throws OptimisticLockException
* @throws PessimisticLockException
@@ -202,7 +202,7 @@ interface EntityManagerInterface extends ObjectManager
/**
* Create a new instance for the given hydration mode.
*
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
*
* @throws ORMException
*/
@@ -233,9 +233,9 @@ interface EntityManagerInterface extends ObjectManager
*
* @param string|class-string<T> $className
*
* @psalm-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)
* @phpstan-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)
*
* @psalm-template T of object
* @phpstan-template T of object
*/
public function getClassMetadata(string $className): Mapping\ClassMetadata;
}

View File

@@ -76,10 +76,10 @@ class EntityRepository implements ObjectRepository, Selectable
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* during the search.
* @psalm-param LockMode::*|null $lockMode
* @phpstan-param LockMode::*|null $lockMode
*
* @return object|null The entity instance or NULL if the entity can not be found.
* @psalm-return ?T
* @phpstan-return ?T
*/
public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
{
@@ -89,7 +89,7 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Finds all entities in the repository.
*
* @psalm-return list<T> The entities.
* @phpstan-return list<T> The entities.
*/
public function findAll(): array
{
@@ -101,7 +101,7 @@ class EntityRepository implements ObjectRepository, Selectable
*
* {@inheritDoc}
*
* @psalm-return list<T>
* @phpstan-return list<T>
*/
public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array
{
@@ -113,10 +113,10 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Finds a single entity by a set of criteria.
*
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, string>|null $orderBy
* @phpstan-param array<string, mixed> $criteria
* @phpstan-param array<string, string>|null $orderBy
*
* @psalm-return T|null
* @phpstan-return T|null
*/
public function findOneBy(array $criteria, array|null $orderBy = null): object|null
{
@@ -128,10 +128,10 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Counts entities by a set of criteria.
*
* @psalm-param array<string, mixed> $criteria
* @phpstan-param array<string, mixed> $criteria
*
* @return int The cardinality of the objects that match the given criteria.
* @psalm-return 0|positive-int
* @phpstan-return 0|positive-int
*
* @todo Add this method to `ObjectRepository` interface in the next major release
*/
@@ -144,7 +144,7 @@ class EntityRepository implements ObjectRepository, Selectable
* Adds support for magic method calls.
*
* @param mixed[] $arguments
* @psalm-param list<mixed> $arguments
* @phpstan-param list<mixed> $arguments
*
* @throws BadMethodCallException If the method called is invalid.
*/
@@ -185,7 +185,7 @@ class EntityRepository implements ObjectRepository, Selectable
return $this->em;
}
/** @psalm-return ClassMetadata<T> */
/** @phpstan-return ClassMetadata<T> */
protected function getClassMetadata(): ClassMetadata
{
return $this->class;
@@ -195,7 +195,7 @@ class EntityRepository implements ObjectRepository, Selectable
* Select all elements from a selectable that match the expression and
* return a new collection containing these elements.
*
* @psalm-return AbstractLazyCollection<int, T>&Selectable<int, T>
* @phpstan-return AbstractLazyCollection<int, T>&Selectable<int, T>
*/
public function matching(Criteria $criteria): AbstractLazyCollection&Selectable
{
@@ -209,7 +209,7 @@ class EntityRepository implements ObjectRepository, Selectable
*
* @param string $method The method to call
* @param string $by The property name used as condition
* @psalm-param list<mixed> $arguments The arguments to pass at method call
* @phpstan-param list<mixed> $arguments The arguments to pass at method call
*
* @throws InvalidMagicMethodCall If the method called is invalid or the
* requested field/association does not exist.

View File

@@ -38,7 +38,7 @@ class ListenersInvoker
* @param ClassMetadata $metadata The entity metadata.
* @param string $eventName The entity lifecycle event.
*
* @psalm-return int-mask-of<self::INVOKE_*> Bitmask of subscribed event systems.
* @phpstan-return int-mask-of<self::INVOKE_*> Bitmask of subscribed event systems.
*/
public function getSubscribedSystems(ClassMetadata $metadata, string $eventName): int
{
@@ -66,7 +66,7 @@ class ListenersInvoker
* @param string $eventName The entity lifecycle event.
* @param object $entity The Entity on which the event occurred.
* @param EventArgs $event The Event args.
* @psalm-param int-mask-of<self::INVOKE_*> $invoke Bitmask to invoke listeners.
* @phpstan-param int-mask-of<self::INVOKE_*> $invoke Bitmask to invoke listeners.
*/
public function invoke(
ClassMetadata $metadata,

View File

@@ -24,7 +24,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
/**
* @param mixed[][] $changeSet
* @psalm-param array<string, array{mixed, mixed}|PersistentCollection> $changeSet
* @phpstan-param array<string, array{mixed, mixed}|PersistentCollection> $changeSet
*/
public function __construct(object $entity, EntityManagerInterface $em, array &$changeSet)
{
@@ -37,7 +37,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
* Retrieves entity changeset.
*
* @return mixed[][]
* @psalm-return array<string, array{mixed, mixed}|PersistentCollection>
* @phpstan-return array<string, array{mixed, mixed}|PersistentCollection>
*/
public function getEntityChangeSet(): array
{

View File

@@ -103,16 +103,14 @@ final class Events
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
* after any changes to managed entities have been determined but before any
* actual database operations are executed. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the onFlush event is not raised.
* actually something to do for the underlying UnitOfWork.
*/
public const onFlush = 'onFlush';
/**
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
* after all actual database operations are executed successfully. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the postFlush event is not raised. The event won't be raised if an error occurs during the
* actually something to do for the underlying UnitOfWork. The event won't be raised if an error occurs during the
* flush operation.
*/
public const postFlush = 'postFlush';

View File

@@ -29,7 +29,7 @@ use function is_array;
* Base class for all hydrators. A hydrator is a class that provides some form
* of transformation of an SQL result set into another structure.
*
* @psalm-consistent-constructor
* @phpstan-consistent-constructor
*/
abstract class AbstractHydrator
{
@@ -86,7 +86,7 @@ abstract class AbstractHydrator
/**
* Initiates a row-by-row hydration.
*
* @psalm-param array<string, mixed> $hints
* @phpstan-param array<string, mixed> $hints
*
* @return Generator<array-key, mixed>
*
@@ -153,7 +153,7 @@ abstract class AbstractHydrator
/**
* Hydrates all rows returned by the passed statement instance at once.
*
* @psalm-param array<string, string> $hints
* @phpstan-param array<string, string> $hints
*/
public function hydrateAll(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): mixed
{
@@ -242,14 +242,14 @@ abstract class AbstractHydrator
* the values applied. Scalar values are kept in a specific key 'scalars'.
*
* @param mixed[] $data SQL Result Row.
* @psalm-param array<string, string> $id Dql-Alias => ID-Hash.
* @psalm-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
* @phpstan-param array<string, string> $id Dql-Alias => ID-Hash.
* @phpstan-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
*
* @return array<string, array<string, mixed>> An array with all the fields
* (name => value) of the data
* row, grouped by their
* component alias.
* @psalm-return array{
* @phpstan-return array{
* data: array<array-key, array>,
* newObjects?: array<array-key, array{
* class: ReflectionClass,
@@ -367,10 +367,10 @@ abstract class AbstractHydrator
* of elements as before.
*
* @param mixed[] $data
* @psalm-param array<string, mixed> $data
* @phpstan-param array<string, mixed> $data
*
* @return mixed[] The processed row.
* @psalm-return array<string, mixed>
* @phpstan-return array<string, mixed>
*/
protected function gatherScalarRowData(array &$data): array
{
@@ -405,7 +405,7 @@ abstract class AbstractHydrator
* @param string $key Column name
*
* @return mixed[]|null
* @psalm-return array<string, mixed>|null
* @phpstan-return array<string, mixed>|null
*/
protected function hydrateColumnInfo(string $key): array|null
{
@@ -502,7 +502,7 @@ abstract class AbstractHydrator
/**
* @return string[]
* @psalm-return non-empty-list<string>
* @phpstan-return non-empty-list<string>
*/
private function getDiscriminatorValues(ClassMetadata $classMetadata): array
{

View File

@@ -213,7 +213,7 @@ class ObjectHydrator extends AbstractHydrator
* Gets an entity instance.
*
* @param string $dqlAlias The DQL alias of the entity's class.
* @psalm-param array<string, mixed> $data The instance data.
* @phpstan-param array<string, mixed> $data The instance data.
*
* @throws HydrationException
*/
@@ -266,7 +266,7 @@ class ObjectHydrator extends AbstractHydrator
/**
* @param class-string $className
* @psalm-param array<string, mixed> $data
* @phpstan-param array<string, mixed> $data
*/
private function getEntityFromIdentityMap(string $className, array $data): object|bool
{

View File

@@ -17,6 +17,7 @@ use function array_search;
use function assert;
use function count;
use function in_array;
use function is_array;
use function key;
use function reset;
use function sprintf;
@@ -138,14 +139,21 @@ class SimpleObjectHydrator extends AbstractHydrator
}
if ($value !== null && isset($cacheKeyInfo['enumType'])) {
$originalValue = $value;
$originalValue = $currentValue = $value;
try {
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
if (! is_array($originalValue)) {
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
} else {
$value = [];
foreach ($originalValue as $i => $currentValue) {
$value[$i] = $this->buildEnum($currentValue, $cacheKeyInfo['enumType']);
}
}
} catch (ValueError $e) {
throw MappingException::invalidEnumValue(
$entityName,
$cacheKeyInfo['fieldName'],
(string) $originalValue,
(string) $currentValue,
$cacheKeyInfo['enumType'],
$e,
);

View File

@@ -29,7 +29,7 @@ trait NoUnknownNamedArguments
* @param TItem[] $parameter
*
* @template TItem
* @psalm-assert list<TItem> $parameter
* @phpstan-assert list<TItem> $parameter
*/
private static function validateVariadicParameter(array $parameter): void
{

View File

@@ -97,7 +97,7 @@ abstract class AssociationMapping implements ArrayAccess
/**
* @param mixed[] $mappingArray
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,
@@ -153,75 +153,75 @@ abstract class AssociationMapping implements ArrayAccess
}
/**
* @psalm-assert-if-true OwningSideMapping $this
* @psalm-assert-if-false InverseSideMapping $this
* @phpstan-assert-if-true OwningSideMapping $this
* @phpstan-assert-if-false InverseSideMapping $this
*/
final public function isOwningSide(): bool
{
return $this instanceof OwningSideMapping;
}
/** @psalm-assert-if-true ToOneAssociationMapping $this */
/** @phpstan-assert-if-true ToOneAssociationMapping $this */
final public function isToOne(): bool
{
return $this instanceof ToOneAssociationMapping;
}
/** @psalm-assert-if-true ToManyAssociationMapping $this */
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
final public function isToMany(): bool
{
return $this instanceof ToManyAssociationMapping;
}
/** @psalm-assert-if-true OneToOneOwningSideMapping $this */
/** @phpstan-assert-if-true OneToOneOwningSideMapping $this */
final public function isOneToOneOwningSide(): bool
{
return $this->isOneToOne() && $this->isOwningSide();
}
/** @psalm-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */
/** @phpstan-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */
final public function isToOneOwningSide(): bool
{
return $this->isToOne() && $this->isOwningSide();
}
/** @psalm-assert-if-true ManyToManyOwningSideMapping $this */
/** @phpstan-assert-if-true ManyToManyOwningSideMapping $this */
final public function isManyToManyOwningSide(): bool
{
return $this instanceof ManyToManyOwningSideMapping;
}
/** @psalm-assert-if-true OneToOneAssociationMapping $this */
/** @phpstan-assert-if-true OneToOneAssociationMapping $this */
final public function isOneToOne(): bool
{
return $this instanceof OneToOneAssociationMapping;
}
/** @psalm-assert-if-true OneToManyAssociationMapping $this */
/** @phpstan-assert-if-true OneToManyAssociationMapping $this */
final public function isOneToMany(): bool
{
return $this instanceof OneToManyAssociationMapping;
}
/** @psalm-assert-if-true ManyToOneAssociationMapping $this */
/** @phpstan-assert-if-true ManyToOneAssociationMapping $this */
final public function isManyToOne(): bool
{
return $this instanceof ManyToOneAssociationMapping;
}
/** @psalm-assert-if-true ManyToManyAssociationMapping $this */
/** @phpstan-assert-if-true ManyToManyAssociationMapping $this */
final public function isManyToMany(): bool
{
return $this instanceof ManyToManyAssociationMapping;
}
/** @psalm-assert-if-true ToManyAssociationMapping $this */
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
final public function isOrdered(): bool
{
return $this->isToMany() && $this->orderBy() !== [];
}
/** @psalm-assert-if-true ToManyAssociationMapping $this */
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
public function isIndexed(): bool
{
return false;

View File

@@ -27,7 +27,7 @@ final class AssociationOverride implements MappingAttribute
* @param JoinColumn|array<JoinColumn> $inverseJoinColumns
* @param JoinTable|null $joinTable The join table that maps the relationship.
* @param string|null $inversedBy The name of the association-field on the inverse-side.
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch
*/
public function __construct(
public readonly string $name,

View File

@@ -109,7 +109,7 @@ class ClassMetadataBuilder
/**
* Adds Index.
*
* @psalm-param list<string> $columns
* @phpstan-param list<string> $columns
*
* @return $this
*/
@@ -127,7 +127,7 @@ class ClassMetadataBuilder
/**
* Adds Unique Constraint.
*
* @psalm-param list<string> $columns
* @phpstan-param list<string> $columns
*
* @return $this
*/
@@ -235,7 +235,7 @@ class ClassMetadataBuilder
/**
* Adds Field.
*
* @psalm-param array<string, mixed> $mapping
* @phpstan-param array<string, mixed> $mapping
*
* @return $this
*/

View File

@@ -12,7 +12,7 @@ namespace Doctrine\ORM\Mapping\Builder;
class OneToManyAssociationBuilder extends AssociationBuilder
{
/**
* @psalm-param array<string, string> $fieldNames
* @phpstan-param array<string, string> $fieldNames
*
* @return $this
*/

View File

@@ -10,7 +10,7 @@ use Attribute;
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
final class Cache implements MappingAttribute
{
/** @psalm-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */
/** @phpstan-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */
public function __construct(
public readonly string $usage = 'READ_ONLY',
public readonly string|null $region = null,

View File

@@ -9,7 +9,7 @@ use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
final class ChangeTrackingPolicy implements MappingAttribute
{
/** @psalm-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */
/** @phpstan-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */
public function __construct(
public readonly string $value,
) {

View File

@@ -25,7 +25,10 @@ use ReflectionProperty;
use Stringable;
use function array_column;
use function array_count_values;
use function array_diff;
use function array_filter;
use function array_flip;
use function array_intersect;
use function array_key_exists;
use function array_keys;
@@ -39,6 +42,7 @@ use function count;
use function defined;
use function enum_exists;
use function explode;
use function implode;
use function in_array;
use function interface_exists;
use function is_string;
@@ -67,12 +71,14 @@ use function trim;
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* @psalm-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping
* @phpstan-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping
* @template-covariant T of object
* @template-implements PersistenceClassMetadata<T>
*/
class ClassMetadata implements PersistenceClassMetadata, Stringable
{
use GetReflectionClassImplementation;
/* The inheritance mapping types */
/**
* NONE means the class does not participate in an inheritance hierarchy
@@ -231,7 +237,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
* as {@link $name}.
*
* @psalm-var class-string
* @phpstan-var class-string
*/
public string $rootEntityName;
@@ -255,7 +261,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* The name of the custom repository class used for the entity class.
* (Optional).
*
* @psalm-var ?class-string<EntityRepository>
* @phpstan-var ?class-string<EntityRepository>
*/
public string|null $customRepositoryClassName = null;
@@ -273,7 +279,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* 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>
* @phpstan-var list<class-string>
*/
public array $parentClasses = [];
@@ -300,14 +306,14 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* 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>
* @phpstan-var list<class-string>
*/
public array $subClasses = [];
/**
* READ-ONLY: The names of all embedded classes based on properties.
*
* @psalm-var array<string, EmbeddedClassMapping>
* @phpstan-var array<string, EmbeddedClassMapping>
*/
public array $embeddedClasses = [];
@@ -315,21 +321,21 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
* of the mapped entity class.
*
* @psalm-var list<string>
* @phpstan-var list<string>
*/
public array $identifier = [];
/**
* READ-ONLY: The inheritance mapping type used by the class.
*
* @psalm-var self::INHERITANCE_TYPE_*
* @phpstan-var self::INHERITANCE_TYPE_*
*/
public int $inheritanceType = self::INHERITANCE_TYPE_NONE;
/**
* READ-ONLY: The Id generator type used by the class.
*
* @psalm-var self::GENERATOR_TYPE_*
* @phpstan-var self::GENERATOR_TYPE_*
*/
public int $generatorType = self::GENERATOR_TYPE_NONE;
@@ -345,7 +351,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* READ-ONLY: An array of field names. Used to look up field names from column names.
* Keys are column names and values are field names.
*
* @psalm-var array<string, string>
* @phpstan-var array<string, string>
*/
public array $fieldNames = [];
@@ -380,7 +386,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
*
* @var array<int|string, string>
*
* @psalm-var array<int|string, class-string>
* @phpstan-var array<int|string, class-string>
*/
public array $discriminatorMap = [];
@@ -400,7 +406,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* uniqueConstraints => array
*
* @var mixed[]
* @psalm-var array{
* @phpstan-var array{
* name: string,
* schema?: string,
* indexes?: array,
@@ -414,14 +420,14 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* READ-ONLY: The registered lifecycle callbacks for entities of this class.
*
* @psalm-var array<string, list<string>>
* @phpstan-var array<string, list<string>>
*/
public array $lifecycleCallbacks = [];
/**
* READ-ONLY: The registered entity listeners.
*
* @psalm-var array<string, list<array{class: class-string, method: string}>>
* @phpstan-var array<string, list<array{class: class-string, method: string}>>
*/
public array $entityListeners = [];
@@ -437,7 +443,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* )
* </pre>
*
* @psalm-var array<string, ConcreteAssociationMapping>
* @phpstan-var array<string, ConcreteAssociationMapping>
*/
public array $associationMappings = [];
@@ -481,7 +487,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* </code>
*
* @var array<string, mixed>|null
* @psalm-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null
* @phpstan-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public array|null $sequenceGeneratorDefinition = null;
@@ -548,7 +554,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* metadata of the class with the given name.
*
* @param string $name The name of the entity class the new instance is used for.
* @psalm-param class-string<T> $name
* @phpstan-param class-string<T> $name
*/
public function __construct(public string $name, NamingStrategy|null $namingStrategy = null, TypedFieldMapper|null $typedFieldMapper = null)
{
@@ -562,7 +568,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Gets the ReflectionProperties of the mapped class.
*
* @return ReflectionProperty[]|null[] An array of ReflectionProperty instances.
* @psalm-return array<ReflectionProperty|null>
* @phpstan-return array<ReflectionProperty|null>
*/
public function getReflectionProperties(): array
{
@@ -628,7 +634,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Populates the entity identifier of an entity.
*
* @psalm-param array<string, mixed> $id
* @phpstan-param array<string, mixed> $id
*
* @todo Rename to assignIdentifier()
*/
@@ -932,17 +938,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
}
}
/**
* {@inheritDoc}
*
* Can return null when using static reflection, in violation of the LSP
*/
public function getReflectionClass(): ReflectionClass|null
{
return $this->reflClass;
}
/** @psalm-param array{usage?: mixed, region?: mixed} $cache */
/** @phpstan-param array{usage?: mixed, region?: mixed} $cache */
public function enableCache(array $cache): void
{
if (! isset($cache['usage'])) {
@@ -956,17 +952,17 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
$this->cache = $cache;
}
/** @psalm-param array{usage?: int, region?: string} $cache */
/** @phpstan-param array{usage?: int, region?: string} $cache */
public function enableAssociationCache(string $fieldName, array $cache): void
{
$this->associationMappings[$fieldName]->cache = $this->getAssociationCacheDefaults($fieldName, $cache);
}
/**
* @psalm-param array{usage?: int, region?: string|null} $cache
* @phpstan-param array{usage?: int, region?: string|null} $cache
*
* @return int[]|string[]
* @psalm-return array{usage: int, region: string|null}
* @phpstan-return array{usage: int, region: string|null}
*/
public function getAssociationCacheDefaults(string $fieldName, array $cache): array
{
@@ -1083,7 +1079,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Gets all association mappings of the class.
*
* @psalm-return array<string, AssociationMapping>
* @phpstan-return array<string, AssociationMapping>
*/
public function getAssociationMappings(): array
{
@@ -1150,7 +1146,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Validates & completes the given field mapping.
*
* @psalm-param array{
* @phpstan-param array{
* fieldName?: string,
* columnName?: string,
* id?: bool,
@@ -1250,7 +1246,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Validates & completes the basic mapping information that is common to all
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
*
* @psalm-param array<string, mixed> $mapping The mapping.
* @phpstan-param array<string, mixed> $mapping The mapping.
*
* @return ConcreteAssociationMapping
*
@@ -1465,7 +1461,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Sets the mapped identifier/primary key fields of this class.
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
*
* @psalm-param list<mixed> $identifier
* @phpstan-param list<mixed> $identifier
*/
public function setIdentifier(array $identifier): void
{
@@ -1489,10 +1485,10 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Gets an array containing all the column names.
*
* @psalm-param list<string>|null $fieldNames
* @phpstan-param list<string>|null $fieldNames
*
* @return mixed[]
* @psalm-return list<string>
* @phpstan-return list<string>
*/
public function getColumnNames(array|null $fieldNames = null): array
{
@@ -1506,7 +1502,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Returns an array with all the identifier column names.
*
* @psalm-return list<string>
* @phpstan-return list<string>
*/
public function getIdentifierColumnNames(): array
{
@@ -1533,7 +1529,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets the type of Id generator to use for the mapped class.
*
* @psalm-param self::GENERATOR_TYPE_* $generatorType
* @phpstan-param self::GENERATOR_TYPE_* $generatorType
*/
public function setIdGeneratorType(int $generatorType): void
{
@@ -1586,7 +1582,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Checks whether the class uses a sequence for id generation.
*
* @psalm-assert-if-true !null $this->sequenceGeneratorDefinition
* @phpstan-assert-if-true !null $this->sequenceGeneratorDefinition
*/
public function isIdGeneratorSequence(): bool
{
@@ -1642,7 +1638,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets the mapped subclasses of this class.
*
* @psalm-param list<string> $subclasses The names of all mapped subclasses.
* @phpstan-param list<string> $subclasses The names of all mapped subclasses.
*/
public function setSubclasses(array $subclasses): void
{
@@ -1657,7 +1653,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Assumes that the class names in the passed array are in the order:
* directParent -> directParentParent -> directParentParentParent ... -> root.
*
* @psalm-param list<class-string> $classNames
* @phpstan-param list<class-string> $classNames
*/
public function setParentClasses(array $classNames): void
{
@@ -1671,7 +1667,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets the inheritance type used by the class and its subclasses.
*
* @psalm-param self::INHERITANCE_TYPE_* $type
* @phpstan-param self::INHERITANCE_TYPE_* $type
*
* @throws MappingException
*/
@@ -1687,7 +1683,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets the association to override association mapping of property for an entity relationship.
*
* @psalm-param array<string, mixed> $overrideMapping
* @phpstan-param array{joinColumns?: array, inversedBy?: ?string, joinTable?: array, fetch?: ?string, cascade?: string[]} $overrideMapping
*
* @throws MappingException
*/
@@ -1723,6 +1719,10 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
$mapping['fetch'] = $overrideMapping['fetch'];
}
if (isset($overrideMapping['cascade'])) {
$mapping['cascade'] = $overrideMapping['cascade'];
}
switch ($mapping['type']) {
case self::ONE_TO_ONE:
case self::MANY_TO_ONE:
@@ -1741,7 +1741,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets the override for a mapped field.
*
* @psalm-param array<string, mixed> $overrideMapping
* @phpstan-param array<string, mixed> $overrideMapping
*
* @throws MappingException
*/
@@ -1836,7 +1836,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
*
* If a key is omitted, the current value is kept.
*
* @psalm-param array<string, mixed> $table The table description.
* @phpstan-param array<string, mixed> $table The table description.
*/
public function setPrimaryTable(array $table): void
{
@@ -1888,7 +1888,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Adds a mapped field to the class.
*
* @psalm-param array<string, mixed> $mapping The field mapping.
* @phpstan-param array<string, mixed> $mapping The field mapping.
*
* @throws MappingException
*/
@@ -1956,7 +1956,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Adds a one-to-many mapping.
*
* @psalm-param array<string, mixed> $mapping The mapping.
* @phpstan-param array<string, mixed> $mapping The mapping.
*/
public function mapOneToMany(array $mapping): void
{
@@ -1970,7 +1970,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Adds a many-to-one mapping.
*
* @psalm-param array<string, mixed> $mapping The mapping.
* @phpstan-param array<string, mixed> $mapping The mapping.
*/
public function mapManyToOne(array $mapping): void
{
@@ -1984,7 +1984,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Adds a many-to-many mapping.
*
* @psalm-param array<string, mixed> $mapping The mapping.
* @phpstan-param array<string, mixed> $mapping The mapping.
*/
public function mapManyToMany(array $mapping): void
{
@@ -2015,7 +2015,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* 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
* @phpstan-param class-string<EntityRepository>|null $repositoryClassName
*/
public function setCustomRepositoryClass(string|null $repositoryClassName): void
{
@@ -2055,7 +2055,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Gets the registered lifecycle callbacks for an event.
*
* @return string[]
* @psalm-return list<string>
* @phpstan-return list<string>
*/
public function getLifecycleCallbacks(string $event): array
{
@@ -2082,7 +2082,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Sets the lifecycle callbacks for entities of this class.
* Any previously registered callbacks are overwritten.
*
* @psalm-param array<string, list<string>> $callbacks
* @phpstan-param array<string, list<string>> $callbacks
*/
public function setLifecycleCallbacks(array $callbacks): void
{
@@ -2128,7 +2128,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* @see getDiscriminatorColumn()
*
* @param DiscriminatorColumnMapping|mixed[]|null $columnDef
* @psalm-param DiscriminatorColumnMapping|array{
* @phpstan-param DiscriminatorColumnMapping|array{
* name: string|null,
* fieldName?: string|null,
* type?: string|null,
@@ -2186,6 +2186,22 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
*/
public function setDiscriminatorMap(array $map): void
{
if (count(array_flip($map)) !== count($map)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/3519',
<<<'DEPRECATION'
Mapping a class to multiple discriminator values is deprecated,
and the discriminator mapping of %s contains duplicate values
for the following discriminator values: %s.
DEPRECATION,
$this->name,
implode(', ', array_keys(array_filter(array_count_values($map), static function (int $value): bool {
return $value > 1;
}))),
);
}
foreach ($map as $value => $className) {
$this->addDiscriminatorMapClass($value, $className);
}
@@ -2334,7 +2350,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Sets definition.
*
* @psalm-param array<string, string|null> $definition
* @phpstan-param array<string, string|null> $definition
*/
public function setCustomGeneratorDefinition(array $definition): void
{
@@ -2354,7 +2370,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* )
* </code>
*
* @psalm-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition
* @phpstan-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition
*
* @throws MappingException
*/
@@ -2387,7 +2403,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* Sets the version field mapping used for versioning. Sets the default
* value to use depending on the column type.
*
* @psalm-param array<string, mixed> $mapping The version field mapping array.
* @phpstan-param array<string, mixed> $mapping The version field mapping array.
*
* @throws MappingException
*/
@@ -2456,7 +2472,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* {@inheritDoc}
*
* @psalm-return class-string
* @phpstan-return class-string
*
* @throws InvalidArgumentException
*/
@@ -2501,7 +2517,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
* @param C $className
*
* @return string|null null if and only if the input value is null
* @psalm-return (C is class-string ? class-string : (C is string ? string : null))
* @phpstan-return (C is class-string ? class-string : (C is string ? string : null))
*
* @template C of string|null
*/
@@ -2533,7 +2549,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
/**
* Map Embedded Class
*
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* class?: class-string,
* declaredField?: string,
@@ -2642,7 +2658,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
return $sequencePrefix;
}
/** @psalm-param class-string $class */
/** @phpstan-param class-string $class */
private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ReflectionProperty|null
{
$reflectionProperty = $reflService->getAccessibleProperty($class, $field);

View File

@@ -612,7 +612,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
}
}
/** @psalm-return ClassMetadata::GENERATOR_TYPE_* */
/** @phpstan-return ClassMetadata::GENERATOR_TYPE_* */
private function determineIdGeneratorStrategy(AbstractPlatform $platform): int
{
assert($this->em !== null);

View File

@@ -15,7 +15,7 @@ final class Column implements MappingAttribute
* @param int|null $scale The scale for a decimal (exact numeric) column (Applies only for decimal column).
* @param class-string<BackedEnum>|null $enumType
* @param array<string,mixed> $options
* @psalm-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated
* @phpstan-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated
*/
public function __construct(
public readonly string|null $name = null,

View File

@@ -24,7 +24,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($class->fieldMappings[$fieldName]->quoted)
? $platform->quoteIdentifier($class->fieldMappings[$fieldName]->columnName)
? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName)
: $class->fieldMappings[$fieldName]->columnName;
}
@@ -42,7 +42,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
}
return isset($class->table['quoted'])
? $platform->quoteIdentifier($tableName)
? $platform->quoteSingleIdentifier($tableName)
: $tableName;
}
@@ -52,14 +52,14 @@ class DefaultQuoteStrategy implements QuoteStrategy
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($definition['quoted'])
? $platform->quoteIdentifier($definition['sequenceName'])
? $platform->quoteSingleIdentifier($definition['sequenceName'])
: $definition['sequenceName'];
}
public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->name)
? $platform->quoteSingleIdentifier($joinColumn->name)
: $joinColumn->name;
}
@@ -69,7 +69,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
AbstractPlatform $platform,
): string {
return isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->referencedColumnName)
? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName)
: $joinColumn->referencedColumnName;
}
@@ -87,7 +87,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
$tableName = $association->joinTable->name;
if (isset($association->joinTable->quoted)) {
$tableName = $platform->quoteIdentifier($tableName);
$tableName = $platform->quoteSingleIdentifier($tableName);
}
return $schema . $tableName;
@@ -113,7 +113,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
$joinColumns = $assoc->joinColumns;
$assocQuotedColumnNames = array_map(
static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->name)
? $platform->quoteSingleIdentifier($joinColumn->name)
: $joinColumn->name,
$joinColumns,
);

View File

@@ -20,7 +20,7 @@ use function defined;
use function enum_exists;
use function is_a;
/** @psalm-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */
/** @phpstan-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */
final class DefaultTypedFieldMapper implements TypedFieldMapper
{
/** @var array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */
@@ -74,8 +74,6 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
assert(is_a($type->getName(), BackedEnum::class, true));
$mapping['enumType'] = $type->getName();
$type = $reflection->getBackingType();
assert($type instanceof ReflectionNamedType);
}
if (isset($mapping['type'])) {

View File

@@ -35,7 +35,7 @@ final class DiscriminatorColumnMapping implements ArrayAccess
}
/**
* @psalm-param array{
* @phpstan-param array{
* type: string,
* fieldName: string,
* name: string,

View File

@@ -15,7 +15,6 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function assert;
use function class_exists;
@@ -273,8 +272,6 @@ class AttributeDriver implements MappingDriver
}
foreach ($reflectionClass->getProperties() as $property) {
assert($property instanceof ReflectionProperty);
if ($this->isRepeatedPropertyDeclaration($property, $metadata)) {
continue;
}
@@ -285,8 +282,6 @@ class AttributeDriver implements MappingDriver
// Evaluate #[Cache] attribute
$cacheAttribute = $this->reader->getPropertyAttribute($property, Mapping\Cache::class);
if ($cacheAttribute !== null) {
assert($cacheAttribute instanceof Mapping\Cache);
$mapping['cache'] = $metadata->getAssociationCacheDefaults(
$mapping['fieldName'],
[
@@ -560,7 +555,6 @@ class AttributeDriver implements MappingDriver
$listenerClass = new ReflectionClass($listenerClassName);
foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
assert($method instanceof ReflectionMethod);
// find method callbacks.
$callbacks = $this->getMethodCallbacks($method);
$hasMapping = $hasMapping ?: ! empty($callbacks);
@@ -584,7 +578,6 @@ class AttributeDriver implements MappingDriver
}
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
assert($method instanceof ReflectionMethod);
foreach ($this->getMethodCallbacks($method) as $value) {
$metadata->addLifecycleCallback($value[0], $value[1]);
}
@@ -629,7 +622,7 @@ class AttributeDriver implements MappingDriver
* Parses the given method.
*
* @return list<array{string, string}>
* @psalm-return list<array{string, (Events::*)}>
* @phpstan-return list<array{string, (Events::*)}>
*/
private function getMethodCallbacks(ReflectionMethod $method): array
{
@@ -677,7 +670,7 @@ class AttributeDriver implements MappingDriver
* Parse the given JoinColumn as array
*
* @return mixed[]
* @psalm-return array{
* @phpstan-return array{
* name: string|null,
* unique: bool,
* nullable: bool,
@@ -709,7 +702,7 @@ class AttributeDriver implements MappingDriver
* Parse the given Column as array
*
* @return mixed[]
* @psalm-return array{
* @phpstan-return array{
* fieldName: string,
* type: mixed,
* scale: int,

View File

@@ -24,7 +24,7 @@ final class AttributeReader
private array $isRepeatableAttribute = [];
/**
* @psalm-return class-string-map<T, T|RepeatableAttributeCollection<T>>
* @phpstan-return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of MappingAttribute
*/

View File

@@ -61,7 +61,7 @@ class DatabaseDriver implements MappingDriver
/** @var array<class-string, string> */
private array $classToTableNames = [];
/** @psalm-var array<string, Table> */
/** @phpstan-var array<string, Table> */
private array $manyToManyTables = [];
/** @var mixed[] */
@@ -126,8 +126,8 @@ class DatabaseDriver implements MappingDriver
*
* @param Table[] $entityTables
* @param Table[] $manyToManyTables
* @psalm-param list<Table> $entityTables
* @psalm-param list<Table> $manyToManyTables
* @phpstan-param list<Table> $entityTables
* @phpstan-param list<Table> $manyToManyTables
*/
public function setTables(array $entityTables, array $manyToManyTables): void
{
@@ -366,7 +366,7 @@ class DatabaseDriver implements MappingDriver
* Build field mapping from a schema column definition
*
* @return mixed[]
* @psalm-return array{
* @phpstan-return array{
* fieldName: string,
* columnName: string,
* type: string,

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Persistence\Mapping\StaticReflectionService;
use function class_exists;
if (! class_exists(StaticReflectionService::class)) {
/** @internal */
trait LoadMappingFileImplementation
{
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file): array
{
return $this->doLoadMappingFile($file);
}
}
} else {
/** @internal */
trait LoadMappingFileImplementation
{
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)
{
return $this->doLoadMappingFile($file);
}
}
}

View File

@@ -22,8 +22,13 @@ trait ReflectionBasedDriver
*/
private function isRepeatedPropertyDeclaration(ReflectionProperty $property, ClassMetadata $metadata): bool
{
/** @var class-string $declaringClass */
$declaringClass = $property->class;
if ($this->isTransient($declaringClass)) {
return isset($metadata->fieldMappings[$property->name]);
}
if (
isset($metadata->fieldMappings[$property->name]->declared)
&& $metadata->fieldMappings[$property->name]->declared === $declaringClass

View File

@@ -43,6 +43,8 @@ use function strtoupper;
*/
class XmlDriver extends FileDriver
{
use LoadMappingFileImplementation;
public const DEFAULT_FILE_EXTENSION = '.dcm.xml';
/**
@@ -408,7 +410,6 @@ class XmlDriver extends FileDriver
if (isset($oneToManyElement->{'order-by'})) {
$orderBy = [];
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
// @phpstan-ignore classConstant.deprecated
@@ -538,7 +539,6 @@ class XmlDriver extends FileDriver
if (isset($manyToManyElement->{'order-by'})) {
$orderBy = [];
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
// @phpstan-ignore classConstant.deprecated
@@ -666,7 +666,7 @@ class XmlDriver extends FileDriver
* Parses (nested) option elements.
*
* @return mixed[] The options array.
* @psalm-return array<int|string, array<int|string, mixed|string>|bool|string>
* @phpstan-return array<int|string, array<int|string, mixed|string>|bool|string>
*/
private function parseOptions(SimpleXMLElement|null $options): array
{
@@ -701,7 +701,7 @@ class XmlDriver extends FileDriver
* @param SimpleXMLElement $joinColumnElement The XML element.
*
* @return mixed[] The mapping array.
* @psalm-return array{
* @phpstan-return array{
* name: string,
* referencedColumnName: string,
* unique?: bool,
@@ -745,7 +745,7 @@ class XmlDriver extends FileDriver
* Parses the given field as array.
*
* @return mixed[]
* @psalm-return array{
* @phpstan-return array{
* fieldName: string,
* type?: string,
* columnName?: string,
@@ -831,7 +831,7 @@ class XmlDriver extends FileDriver
* Parse / Normalize the cache configuration
*
* @return mixed[]
* @psalm-return array{usage: int|null, region?: string}
* @phpstan-return array{usage: int|null, region?: string}
*/
private function cacheToArray(SimpleXMLElement $cacheMapping): array
{
@@ -858,7 +858,7 @@ class XmlDriver extends FileDriver
* @param SimpleXMLElement $cascadeElement The cascade element.
*
* @return string[] The list of cascade options.
* @psalm-return list<string>
* @phpstan-return list<string>
*/
private function getCascadeMappings(SimpleXMLElement $cascadeElement): array
{
@@ -878,10 +878,8 @@ class XmlDriver extends FileDriver
return $cascades;
}
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)
/** @return array<class-string, SimpleXMLElement> */
private function doLoadMappingFile(string $file): array
{
$this->validateMapping($file);
$result = [];

View File

@@ -48,7 +48,7 @@ final class EmbeddedClassMapping implements ArrayAccess
}
/**
* @psalm-param array{
* @phpstan-param array{
* class: class-string,
* columnPrefix?: false|string|null,
* declaredField?: string|null,

View File

@@ -11,7 +11,7 @@ use Doctrine\ORM\EntityRepository;
#[Attribute(Attribute::TARGET_CLASS)]
final class Entity implements MappingAttribute
{
/** @psalm-param class-string<EntityRepository<T>>|null $repositoryClass */
/** @phpstan-param class-string<EntityRepository<T>>|null $repositoryClass */
public function __construct(
public readonly string|null $repositoryClass = null,
public readonly bool $readOnly = false,

View File

@@ -26,7 +26,7 @@ final class FieldMapping implements ArrayAccess
public bool|null $notInsertable = null;
public bool|null $notUpdatable = null;
public string|null $columnDefinition = null;
/** @psalm-var ClassMetadata::GENERATED_*|null */
/** @phpstan-var ClassMetadata::GENERATED_*|null */
public int|null $generated = null;
/** @var class-string<BackedEnum>|null */
public string|null $enumType = null;
@@ -85,7 +85,7 @@ final class FieldMapping implements ArrayAccess
/**
* @param array<string, mixed> $mappingArray
* @psalm-param array{
* @phpstan-param array{
* type: string,
* fieldName: string,
* columnName: string,

View File

@@ -9,7 +9,7 @@ use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class GeneratedValue implements MappingAttribute
{
/** @psalm-param 'AUTO'|'SEQUENCE'|'IDENTITY'|'NONE'|'CUSTOM' $strategy */
/** @phpstan-param 'AUTO'|'SEQUENCE'|'IDENTITY'|'NONE'|'CUSTOM' $strategy */
public function __construct(
public readonly string $strategy = 'AUTO',
) {

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use Doctrine\Persistence\Mapping\StaticReflectionService;
use ReflectionClass;
use function class_exists;
if (! class_exists(StaticReflectionService::class)) {
trait GetReflectionClassImplementation
{
public function getReflectionClass(): ReflectionClass
{
return $this->reflClass;
}
}
} else {
trait GetReflectionClassImplementation
{
/**
* {@inheritDoc}
*
* Can return null when using static reflection, in violation of the LSP
*/
public function getReflectionClass(): ReflectionClass|null
{
return $this->reflClass;
}
}
}

View File

@@ -9,7 +9,7 @@ use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
final class InheritanceType implements MappingAttribute
{
/** @psalm-param 'NONE'|'JOINED'|'SINGLE_TABLE' $value */
/** @phpstan-param 'NONE'|'JOINED'|'SINGLE_TABLE' $value */
public function __construct(
public readonly string $value,
) {

View File

@@ -31,7 +31,7 @@ final class JoinColumnMapping implements ArrayAccess
/**
* @param array<string, mixed> $mappingArray
* @psalm-param array{
* @phpstan-param array{
* name: string,
* referencedColumnName: string,
* unique?: bool|null,

View File

@@ -33,7 +33,7 @@ final class JoinTableMapping implements ArrayAccess
/**
* @param mixed[] $mappingArray
* @psalm-param array{
* @phpstan-param array{
* name: string,
* quoted?: bool|null,
* joinColumns?: mixed[],

View File

@@ -12,7 +12,7 @@ final class ManyToMany implements MappingAttribute
/**
* @param class-string $targetEntity
* @param string[]|null $cascade
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
*/
public function __construct(
public readonly string $targetEntity,

View File

@@ -37,7 +37,7 @@ final class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implemen
/**
* @param mixed[] $mappingArray
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,

View File

@@ -12,7 +12,7 @@ final class ManyToOne implements MappingAttribute
/**
* @param class-string|null $targetEntity
* @param string[]|null $cascade
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
*/
public function __construct(
public readonly string|null $targetEntity = null,

View File

@@ -12,7 +12,7 @@ final class OneToMany implements MappingAttribute
/**
* @param class-string|null $targetEntity
* @param string[]|null $cascade
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
*/
public function __construct(
public readonly string|null $targetEntity = null,

View File

@@ -8,7 +8,7 @@ final class OneToManyAssociationMapping extends ToManyInverseSideMapping
{
/**
* @param mixed[] $mappingArray
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,
@@ -41,7 +41,7 @@ final class OneToManyAssociationMapping extends ToManyInverseSideMapping
/**
* @param mixed[] $mappingArray
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,

View File

@@ -12,7 +12,7 @@ final class OneToOne implements MappingAttribute
/**
* @param class-string|null $targetEntity
* @param array<string>|null $cascade
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch
*/
public function __construct(
public readonly string|null $targetEntity = null,

View File

@@ -52,7 +52,7 @@ interface QuoteStrategy
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @psalm-return list<string>
* @phpstan-return list<string>
*/
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array;

View File

@@ -23,7 +23,7 @@ final class ReflectionEmbeddedProperty extends ReflectionProperty
/**
* @param ReflectionProperty $parentProperty reflection property of the class where the embedded object has to be put
* @param ReflectionProperty $childProperty reflection property of the embedded object
* @psalm-param class-string $embeddedClass
* @phpstan-param class-string $embeddedClass
*/
public function __construct(
private readonly ReflectionProperty $parentProperty,

View File

@@ -6,7 +6,7 @@ namespace Doctrine\ORM\Mapping;
interface ToManyAssociationMapping
{
/** @psalm-assert-if-true string $this->indexBy() */
/** @phpstan-assert-if-true string $this->indexBy() */
public function isIndexed(): bool;
public function indexBy(): string;

View File

@@ -32,7 +32,7 @@ trait ToManyAssociationMappingImplementation
return $this->orderBy;
}
/** @psalm-assert-if-true !null $this->indexBy */
/** @phpstan-assert-if-true !null $this->indexBy */
final public function isIndexed(): bool
{
return $this->indexBy !== null;

View File

@@ -9,7 +9,7 @@ abstract class ToOneInverseSideMapping extends InverseSideMapping
/**
* @param mixed[] $mappingArray
* @param class-string $name
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,

View File

@@ -27,7 +27,7 @@ abstract class ToOneOwningSideMapping extends OwningSideMapping implements ToOne
/**
* @param array<string, mixed> $mappingArray
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,
@@ -74,7 +74,7 @@ abstract class ToOneOwningSideMapping extends OwningSideMapping implements ToOne
/**
* @param mixed[] $mappingArray
* @param class-string $name
* @psalm-param array{
* @phpstan-param array{
* fieldName: string,
* sourceEntity: class-string,
* targetEntity: class-string,

View File

@@ -35,8 +35,8 @@ use function strtoupper;
* Similarly, if you remove entities from a collection that is part of a one-many
* mapping this will only result in the nulling out of the foreign keys on flush.
*
* @psalm-template TKey of array-key
* @psalm-template T
* @phpstan-template TKey of array-key
* @phpstan-template T
* @template-extends AbstractLazyCollection<TKey,T>
* @template-implements Selectable<TKey,T>
*/
@@ -46,7 +46,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* A snapshot of the collection at the moment it was fetched from the database.
* This is used to create a diff of the collection at commit time.
*
* @psalm-var array<string|int, mixed>
* @phpstan-var array<string|int, mixed>
*/
private array $snapshot = [];
@@ -80,7 +80,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
*
* @param EntityManagerInterface $em The EntityManager the collection will be associated with.
* @param ClassMetadata $typeClass The class descriptor of the entity type of this collection.
* @psalm-param Collection<TKey, T>&Selectable<TKey, T> $collection The collection elements.
* @phpstan-param Collection<TKey, T>&Selectable<TKey, T> $collection The collection elements.
*/
public function __construct(
private EntityManagerInterface|null $em,
@@ -202,7 +202,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* INTERNAL:
* Returns the last snapshot of the elements in the collection.
*
* @psalm-return array<string|int, mixed> The last snapshot of the elements.
* @phpstan-return array<string|int, mixed> The last snapshot of the elements.
*/
public function getSnapshot(): array
{
@@ -492,7 +492,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* with circular references. This solution seems simpler and works well.
*
* @return string[]
* @psalm-return array{0: string, 1: string}
* @phpstan-return array{0: string, 1: string}
*/
public function __sleep(): array
{
@@ -504,6 +504,20 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
$this->em = null;
}
/**
* {@inheritDoc}
*/
public function first()
{
if (! $this->initialized && ! $this->isDirty && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) {
$persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());
return array_values($persister->slice($this, 0, 1))[0] ?? false;
}
return parent::first();
}
/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
@@ -512,7 +526,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @return mixed[]
* @psalm-return array<TKey,T>
* @phpstan-return array<TKey,T>
*/
public function slice(int $offset, int|null $length = null): array
{
@@ -554,7 +568,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* Selects all elements from a selectable that match the expression and
* return a new collection containing these elements.
*
* @psalm-return Collection<TKey, T>
* @phpstan-return Collection<TKey, T>
*
* @throws RuntimeException
*/

View File

@@ -292,7 +292,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* @return string[] ordered tuple:
* - JOIN condition to add to the SQL
* - WHERE condition to add to the SQL
* @psalm-return array{0: string, 1: string}
* @phpstan-return array{0: string, 1: string}
*/
public function getFilterSql(AssociationMapping $mapping): array
{
@@ -340,7 +340,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* Generate ON condition
*
* @return string[]
* @psalm-return list<string>
* @phpstan-return list<string>
*/
protected function getOnConditionSQL(AssociationMapping $mapping): array
{
@@ -412,7 +412,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
* of types for bound parameters
* @psalm-return array{0: string, 1: list<string>}
* @phpstan-return array{0: string, 1: list<string>}
*/
protected function getDeleteRowSQL(PersistentCollection $collection): array
{
@@ -447,7 +447,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.
*
* @return mixed[]
* @psalm-return list<mixed>
* @phpstan-return list<mixed>
*/
protected function getDeleteRowSQLParameters(PersistentCollection $collection, object $element): array
{
@@ -459,7 +459,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
* of types for bound parameters
* @psalm-return array{0: string, 1: list<string>}
* @phpstan-return array{0: string, 1: list<string>}
*/
protected function getInsertRowSQL(PersistentCollection $collection): array
{
@@ -496,7 +496,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.
*
* @return mixed[]
* @psalm-return list<mixed>
* @phpstan-return list<mixed>
*/
protected function getInsertRowSQLParameters(PersistentCollection $collection, object $element): array
{
@@ -508,7 +508,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* of the join table columns as specified in ManyToManyMapping#joinTableColumns.
*
* @return mixed[]
* @psalm-return list<mixed>
* @phpstan-return list<mixed>
*/
private function collectJoinTableColumnParameters(
PersistentCollection $collection,
@@ -557,7 +557,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* - where clauses to be added for filtering
* - parameters to be bound for filtering
* - types of the parameters to be bound for filtering
* @psalm-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}
* @phpstan-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}
*/
private function getJoinTableRestrictionsWithKey(
PersistentCollection $collection,
@@ -647,7 +647,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* - where clauses to be added for filtering
* - parameters to be bound for filtering
* - types of the parameters to be bound for filtering
* @psalm-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}
* @phpstan-return array{0: string, 1: list<string>, 2: list<mixed>, 3: list<string>}
*/
private function getJoinTableRestrictions(
PersistentCollection $collection,

View File

@@ -128,7 +128,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Queued inserts.
*
* @psalm-var array<int, object>
* @phpstan-var array<int, object>
*/
protected array $queuedInserts = [];
@@ -173,6 +173,8 @@ class BasicEntityPersister implements EntityPersister
private readonly CachedPersisterContext $limitsHandlingContext;
private readonly CachedPersisterContext $noLimitsContext;
private string|null $filterHash = null;
/**
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
@@ -340,7 +342,7 @@ class BasicEntityPersister implements EntityPersister
* @param mixed[] $id
*
* @return list<ParameterType|int|string>
* @psalm-return list<ParameterType::*|ArrayParameterType::*|string>
* @phpstan-return list<ParameterType::*|ArrayParameterType::*|string>
*/
final protected function extractIdentifierTypes(array $id, ClassMetadata $versionedClass): array
{
@@ -584,7 +586,7 @@ class BasicEntityPersister implements EntityPersister
* @param bool $isInsert Whether the data to be prepared refers to an insert statement.
*
* @return mixed[][] The prepared data.
* @psalm-return array<string, array<array-key, mixed|null>>
* @phpstan-return array<string, array<array-key, mixed|null>>
*/
protected function prepareUpdateData(object $entity, bool $isInsert = false): array
{
@@ -703,7 +705,7 @@ class BasicEntityPersister implements EntityPersister
* @param object $entity The entity for which to prepare the data.
*
* @return mixed[][] The prepared data for the tables to update.
* @psalm-return array<string, mixed[]>
* @phpstan-return array<string, mixed[]>
*/
protected function prepareInsertData(object $entity): array
{
@@ -1164,7 +1166,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Gets the ORDER BY SQL snippet for ordered collections.
*
* @psalm-param array<string, string> $orderBy
* @phpstan-param array<string, string> $orderBy
*
* @throws InvalidOrientation
* @throws InvalidFindByCall
@@ -1229,7 +1231,7 @@ class BasicEntityPersister implements EntityPersister
*/
protected function getSelectColumnsSQL(): string
{
if ($this->currentPersisterContext->selectColumnListSql !== null) {
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->filterHash === $this->em->getFilters()->getHash()) {
return $this->currentPersisterContext->selectColumnListSql;
}
@@ -1339,6 +1341,7 @@ class BasicEntityPersister implements EntityPersister
}
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
$this->filterHash = $this->em->getFilters()->getHash();
return $this->currentPersisterContext->selectColumnListSql;
}
@@ -1445,7 +1448,7 @@ class BasicEntityPersister implements EntityPersister
* Subclasses should override this method to alter or change the list of
* columns placed in the INSERT statements used by the persister.
*
* @psalm-return list<string>
* @phpstan-return list<string>
*/
protected function getInsertColumnList(): array
{
@@ -1497,7 +1500,15 @@ class BasicEntityPersister implements EntityPersister
$tableAlias = $this->getSQLTableAlias($class->name, $root);
$fieldMapping = $class->fieldMappings[$field];
$sql = sprintf('%s.%s', $tableAlias, $this->quoteStrategy->getColumnName($field, $class, $this->platform));
$columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);
$columnAlias = null;
if ($this->currentPersisterContext->rsm->hasColumnAliasByField($alias, $field)) {
$columnAlias = $this->currentPersisterContext->rsm->getColumnAliasByField($alias, $field);
}
if ($columnAlias === null) {
$columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);
}
$this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field);
if (! empty($fieldMapping->enumType)) {
@@ -1560,7 +1571,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister.
*
* @psalm-param LockMode::* $lockMode
* @phpstan-param LockMode::* $lockMode
*/
protected function getLockTablesSql(LockMode|int $lockMode): string
{
@@ -1662,7 +1673,7 @@ class BasicEntityPersister implements EntityPersister
* Builds the left-hand-side of a where condition statement.
*
* @return string[]
* @psalm-return list<string>
* @phpstan-return list<string>
*
* @throws InvalidFindByCall
* @throws UnrecognizedField
@@ -1737,7 +1748,7 @@ class BasicEntityPersister implements EntityPersister
* Subclasses are supposed to override this method if they intend to change
* or alter the criteria by which entities are selected.
*
* @psalm-param array<string, mixed> $criteria
* @phpstan-param array<string, mixed> $criteria
*/
protected function getSelectConditionSQL(array $criteria, AssociationMapping|null $assoc = null): string
{
@@ -1861,7 +1872,7 @@ class BasicEntityPersister implements EntityPersister
* - class to which the field belongs to
*
* @return mixed[][]
* @psalm-return array{0: array, 1: list<ParameterType::*|ArrayParameterType::*|string>}
* @phpstan-return array{0: array, 1: list<ParameterType::*|ArrayParameterType::*|string>}
*/
private function expandToManyParameters(array $criteria): array
{
@@ -1884,7 +1895,7 @@ class BasicEntityPersister implements EntityPersister
* Infers field types to be used by parameter type casting.
*
* @return list<ParameterType|ArrayParameterType|int|string>
* @psalm-return list<ParameterType::*|ArrayParameterType::*|string>
* @phpstan-return list<ParameterType::*|ArrayParameterType::*|string>
*
* @throws QueryException
*/
@@ -1926,7 +1937,7 @@ class BasicEntityPersister implements EntityPersister
return $types;
}
/** @psalm-return ArrayParameterType::* */
/** @phpstan-return ArrayParameterType::* */
private function getArrayBindingType(ParameterType|int|string $type): ArrayParameterType|int
{
if (! $type instanceof ParameterType) {
@@ -1963,7 +1974,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Retrieves an individual parameter value.
*
* @psalm-return list<mixed>
* @phpstan-return list<mixed>
*/
private function getIndividualValue(mixed $value): array
{
@@ -2094,7 +2105,7 @@ class BasicEntityPersister implements EntityPersister
/**
* @return string[]
* @psalm-return list<string>
* @phpstan-return list<string>
*/
protected function getClassIdentifiersTypes(ClassMetadata $class): array
{

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