Compare commits

...

150 Commits
2.8.2 ... 2.8.5

Author SHA1 Message Date
Grégoire Paris
a6577b89a2 Merge pull request #8701 from jderusse/symfony6
Allow Symfony 6.0
2021-05-20 07:58:49 +02:00
Jérémy Derussé
0ca87566a9 Allow Symfony 6.0 2021-05-20 07:48:21 +02:00
Grégoire Paris
5d01f94a36 Merge pull request #8699 from greg0ire/fix-psalm
Fix some static analysis issues
2021-05-20 07:43:12 +02:00
Grégoire Paris
3d02b02636 Update static analysis baseline files
These issues were not introduced with new code, but with upgrades.
2021-05-20 00:03:39 +02:00
Grégoire Paris
6de321cb09 Address Psalm issues introduced by persistence 2021-05-20 00:03:39 +02:00
Grégoire Paris
535bc92dc8 Merge pull request #8700 from deguif/fix-undefined-offset
Fix undefined index
2021-05-18 12:20:38 +02:00
François-Xavier de Guillebon
ebb5d03f7a Fix undefined offset 2021-05-18 10:00:19 +02:00
Grégoire Paris
8e13369621 Merge pull request #8698 from deguif/cache-deprecation
Fix cache deprecation
2021-05-17 22:15:13 +02:00
François-Xavier de Guillebon
8eff4b775a Fix cache deprecation 2021-05-17 21:33:52 +02:00
tweet9ra
3a194ad699 SimpleObjectHydrator: skip unsuit custom type before converting it (#8566)
When using inheritance, it is possible to map the same column to properties of
different child classes. This can result in the same column being selecting several
times with different aliases in one SQL query, and only one aliased field needs
to be hydrated per row.
We now check that such an aliased value is mapped to the class we are hydrated
before attempting to convert it as it might result in an error when using a custom
type that does not get the expected data to initialize php value.

Co-authored-by: Sergey Naumov <s.naumov@lamoda.ru>
2021-05-15 09:37:16 +02:00
Grégoire Paris
4ccc4e19fc Merge pull request #8600 from VincentLanglet/computeChangeset
Remove internal tag from computeChangeSet
2021-05-05 19:27:39 +02:00
Grégoire Paris
c25b822217 Merge pull request #8673 from Seldaek/patch-1
Add hint for ->iterate() deprecation
2021-05-05 13:31:30 +02:00
Jordi Boggiano
c3dcc5af91 Add hint for ->iterate() deprecation 2021-05-05 10:31:33 +02:00
Alexandr Li
d141f27875 ConvertDoctrine1Schema: Fix Doctrine 1 notnull field import (#8649)
* ConvertDoctrine1Schema: Fix Doctrine 1 `notnull` field import

* cs fix

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-05-01 13:56:41 +02:00
Grégoire Paris
8555fc1d34 Merge pull request #8659 from greg0ire/make-tests-independent
Make tests independent
2021-04-29 09:07:26 +02:00
Grégoire Paris
850d57827f Make tests independent
It seems like IdentityMapTest cannot be run on its own when the second
level cache is enabled (with ENABLE_SECOND_LEVEL_CACHE=1).
It does work when running the whole test suite because
ExtraLazyCollectionTest disables part of that cache in its setUp()
method.
In this patch, we restore the class metadata as it was before running
setUp() and put the test in IdentityMapTest inside the group that is
excluded when running with ENABLE_SECOND_LEVEL_CACHE=1 on the CI.
2021-04-28 14:12:31 +02:00
Grégoire Paris
9a48450481 Merge pull request #7608 from mavroprovato/patch-1
Avoid unnecessary flush after processing first row
2021-04-24 14:23:09 +02:00
Kostas Kokkoros
cff8b96dd6 Avoid unnecessary flush after processing first row
The code as is needlessly flushes after just one row is updated or
removed. It makes more sense to update after ``$batchSize`` elements are
updated or removed, just as it is in the insert case.
2021-04-24 13:01:33 +02:00
Grégoire Paris
996c1c74b3 Merge pull request #8644 from greg0ire/more-accurate-return-type
Describe return types more accurately
2021-04-22 22:31:05 +02:00
Grégoire Paris
48612e6dc6 Merge pull request #8641 from Jean85/remove-deprecated-proxy-usage
Replace deprecated Proxy usages with parent interface to reduce baseline
2021-04-19 23:37:22 +02:00
Grégoire Paris
eb860a704e Change incorrect DBAL return types 2021-04-19 19:27:00 +02:00
Grégoire Paris
51ffcb4891 Describe return types more accurately
This fixes an SA regression introduced when using stricter types in
SchemaTool.

Fixes #8642
2021-04-19 19:09:10 +02:00
Alessandro Lai
9ea0769d78 Replace deprecated Proxy usages with parent interface to reduce baseline 2021-04-19 10:43:49 +02:00
Grégoire Paris
06fadcdd8c Merge pull request #8630 from Jean85/reduce-baseline
Reduce baseline
2021-04-18 18:50:59 +02:00
Alessandro Lai
7c56aa2141 Reduce baseline with a nullable return where needed 2021-04-18 18:39:20 +02:00
Alessandro Lai
4cdcb5f760 Reduce baseline for AbstractCollectionPersister 2021-04-18 18:39:20 +02:00
Alessandro Lai
b542b36e45 Remove baseline for DefaultCacheFactory 2021-04-18 18:39:20 +02:00
Alessandro Lai
e5a7a13e1e Remove single baseline rule from DefaultCache 2021-04-18 18:39:20 +02:00
Alessandro Lai
8336dd3779 Remove baseline for AbstractQuery 2021-04-18 18:39:14 +02:00
Grégoire Paris
a959a474fd Merge pull request #8636 from greg0ire/update-gitattributes
Update ignore rules to reflect current situation
2021-04-18 17:56:47 +02:00
Grégoire Paris
5ee71c54d4 Update ignore rules to reflect current situation
We no longer use Travis, we do not use git submodules as far as I know,
and we now use baseline files as well as project metadata.
2021-04-18 10:49:48 +02:00
Grégoire Paris
da3a9fa361 Merge pull request #8634 from orklah/static-upgrade
upgrade static tools
2021-04-17 15:41:51 +02:00
orklah
4fd81d26ff upgrade static tools 2021-04-17 13:12:35 +02:00
Grégoire Paris
e2e9f8fa97 Merge pull request #8627 from greg0ire/add-baselines
Add baseline files for static analyzers
2021-04-16 19:04:04 +02:00
Grégoire Paris
f7249ec709 Declare return type
This helps SA tools figure out that it is fine to call count on the
return value of that method.
As a side-effect, using $metadata->name is not really an option since it
is not part of the ClassMetadata interface.
2021-04-16 13:14:54 +02:00
Grégoire Paris
87dbcca454 Add baseline files for static analyzers
There are many CS and SA-related changes in the ORM as we pursue better
code quality, and easier contributions. These often involve huge
changes, which can be hard to review and inevitably lead to some
regressions. I know some of those could have been avoided if we were
using stricter levels for PHPStan and Psalm.

By adding baselines, we ensure new code is at level 5 for both tools,
which should allow us to catch the most interesting issues.
2021-04-16 09:23:11 +02:00
Grégoire Paris
305e0d6664 Merge pull request #8617 from greg0ire/cs9
Upgrade to doctrine/coding-standard 9
2021-04-13 18:48:12 +02:00
Grégoire Paris
199be94e6d Upgrade to doctrine/coding-standard 9 2021-04-13 09:00:33 +02:00
Grégoire Paris
fa588af3b1 Merge pull request #8604 from janatjak/2.8.x
Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method
2021-04-09 12:57:12 +02:00
Grégoire Paris
d4741720fa Merge pull request #8605 from greg0ire/fix-phpdoc-lsp-violations 2021-04-09 11:59:28 +02:00
Grégoire Paris
343385d060 Pin squizlabs/php_codesniffer
We are referencing rules in phpcs.xml.dist, and may experience
unexpected BC-breaks because of that when they get renamed.
2021-04-09 09:27:44 +02:00
Grégoire Paris
6d04dced03 Address sniff rename
This sniff seems to have been renamed or split in the latest version of
phpcs.
2021-04-09 09:19:10 +02:00
Grégoire Paris
22fa3a8556 Document actual return types
Some executors may return integers, for instance executors that only
execute update or delete statements.
Also, in case an integer is not returned, what's actually returned is a
Doctrine\DBAL\Driver\ResultStatement, and not a Doctrine\DBAL\Driver\Statement
2021-04-09 08:52:56 +02:00
Jakub Janata
eb05756dc3 Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method 2021-04-08 22:52:50 +02:00
Grégoire Paris
5bb7e20708 Merge pull request #8602 from NicoHaase/fix-8599
Adjusted return type annotation for getOriginalEntityData
2021-04-07 23:22:19 +02:00
Nico Haase
a9076313c7 Adjusted return type 2021-04-07 21:06:58 +02:00
Grégoire Paris
2a87821b28 Merge pull request #8552 from acoulton/maint-phpunit-upgrade 2021-04-07 15:33:06 +02:00
acoulton
da5877d60c Only polyfill older phpunit methods when required 2021-04-07 12:08:55 +01:00
Andrew Coulton
67dfe8e1af Simplify mock building calls
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-07 11:07:35 +01:00
Vincent Langlet
2dfe51b396 Remove internal tag 2021-04-07 11:41:34 +02:00
Grégoire Paris
5ac036de02 Merge pull request #8594 from greg0ire/make-sure-test-is-run 2021-04-06 17:47:17 +02:00
Grégoire Paris
23e1fd8ad6 Drop assertion about not being an instance of proxy
We do not want to enforce it as it is an internal details that seems to
vary from environment to environment.
2021-04-06 14:55:25 +02:00
Grégoire Paris
a588555ecd Merge pull request #8586 from KartaviK/patch-3
Additional psalm param typehint for orderBy argument in findBy method
2021-04-05 20:38:36 +02:00
Grégoire Paris
501057da83 Ensure test is suffixed with Test 2021-04-05 14:42:17 +02:00
Grégoire Paris
7de84537f6 Merge pull request #8591 from DmitriiBezborodnikov/case_insensive_parenthesis
Return case insensitive check
2021-04-05 14:40:51 +02:00
Grégoire Paris
97f8325dad Make sure tests are suffixed with Test
They will not be taken into account when running vendor/bin/phpunit
otherwise.
2021-04-05 14:32:40 +02:00
Grégoire Paris
0ebd7052d7 Drop create table at shutdown
It makes tests more isolated from each other: another test relying on
some tables including some of the ones created here may fail creating
the tables it needs because they already exist.
2021-04-05 14:03:49 +02:00
Dmitrii Bezborodnikov
5d73378b92 Return case insensitive check 2021-04-05 14:03:49 +02:00
Grégoire Paris
10572ec441 Merge pull request #8590 from VincentLanglet/patch-2
Fix phpdoc of ClassMetadataInfo::getIdentifierValues
2021-04-04 23:47:09 +02:00
Vincent Langlet
76278d801d Fix phpdoc 2021-04-04 21:19:54 +02:00
Roman Varkuta
ca80830b26 Describe $orderBy parameter as a hash
A list of string is incorrect, it actually looks like this:
['someField' => 'DESC', 'someOtherField' => 'ASC'…]`
2021-04-03 12:45:24 +02:00
Grégoire Paris
bcb4889a2c Merge pull request #8583 from greg0ire/sync-static-analysis-workflows
Synchronize static analysis jobs with upstream
2021-04-02 08:58:50 +02:00
Grégoire Paris
961da8b0cc Synchronize static analysis jobs with upstream 2021-04-01 23:32:04 +02:00
Benjamin Eberlei
657a30f8ce [GH-6394] Bugfix: IdentifierFlattener support for association non-object values. (#8384)
* [GH-6394] Bugfix: IdentifierFlattener support for association non-object values

* [GH-6394] Bugfix: BasicEntityPersister::update used wrong identifiers for version assignment.

* Exclude MissingNativeTypeHint phpcs rule as 7.4 is not lowest version.
2021-04-01 23:16:53 +02:00
Grégoire Paris
0b25d4d8b0 Merge pull request #8573 from greg0ire/fix-build
Fix build issues
2021-04-01 07:49:27 +02:00
Grégoire Paris
a88242ee6c Adapt test logic to PHP and SQLite
There seems to be at least 2 camps in the software world when it comes
to the question "What's today minus one month", today being at the end
of march.

While PHP and SQLite agree that that would be the 2nd of March, other
RDBMS than SQLite and humans will tell you that it's the last day of
February.

This patch ensures that we check one logic for SQLite, and the other
logic for other platforms.
2021-03-30 21:08:29 +02:00
Grégoire Paris
fe4964008d Accommodate 2 behaviors of symfony/console in test
Decorated text used to be wrapped too early in SymfonyStyle->block()
See https://github.com/symfony/symfony/pull/40348
The fix was not contributed to version 3, which means we have to rewrite
the test so that it passes for both the correct and the buggy version.
2021-03-30 08:41:10 +02:00
Grégoire Paris
3f3de70c3e Merge pull request #8564 from cybercitizen7/featureIncludeDirectory
Adding DIR to include statement to fix issue with pathing
2021-03-26 19:46:40 +01:00
darkw1z
eb4e317144 Adding DIR to include statement to fix issue with pathing 2021-03-26 14:04:46 +01:00
Grégoire Paris
c8f2f61ea1 Merge pull request #8556 from VincentLanglet/patch-2
Fix fieldMapping phpdoc
2021-03-26 08:26:40 +01:00
Vincent Langlet
c9502d3d0b Fix fieldMapping phpdoc 2021-03-24 15:07:08 +01:00
Grégoire Paris
3358ccde39 Merge pull request #8547 from greg0ire/psalm-lv6-phpdoc
Make phpdoc types correct
2021-03-21 22:11:51 +01:00
acoulton
1f4e6ebeeb Add a forward-compatibility wrapper for phpunit8 assertions
While doctrine still supports php7.2 the test cases need to run
under phpunit8 as well. However some assertion methods produce
deprecation warnings in the test output with phpunit >= 9.

This commit adds a thin forward compatiblity wrapper for the
new assertion method names so that they can be used with both
supported phpunit versions.
2021-03-18 11:25:18 +00:00
acoulton
a94db4f5c0 Fix remaining warnings from the phpunit9 upgrade
Some tests were still using deprecated assertion and mocking methods,
resulting in a long list of warnings in the phpunit output.

This commit resolves all the warnings:

* Fixes a couple of test names in `@depends` tags (presumably these
  tests were renamed at some point for coding standards).

* Changes how mocks are configured when asserting the same method
  is called multiple times with a sequence of arguments / sequence
  of return values. The old `->at` expectation is deprecated because
  it can be brittle and give unreliable results. Some of this
  mocking could probably still be refactored further, but my focus
  was on solving the deprecation with the existing setup.

* Removes one use of prophecy for mocking, in favour of using
  phpunit mock objects. Prophecy now requires an extra composer
  dependency and a trait which seems overkill given it was only
  used in one place.

* Updates to the new names for assertFileExists and assertRegExp
  (and their `not` versions) - these are functionally equivalent.

* Replaces the last few references to old PHPUnit_Framework_XXX
  classes with their namespaced equivalent. These were mostly in
  comments, or in native php `assert()` statements that were sanity
  checking mocked object types. These asserts are probably redundant
  (and are clearly not running in CI since the classes they referenced
  no longer exist).
2021-03-18 10:51:43 +00:00
Grégoire Paris
47475f3a67 Merge pull request #8532 from acoulton/bug-fix-ci-db-connection
All CI runs are using the sqlite fallback connection instead of the expected driver
2021-03-17 19:49:56 +01:00
acoulton
61c4a5da0a Rename tmpdb_ to privileged_db in test config and TestUtil
To avoid confusion, the `tmpdb_` test config values are now named
`privileged_db_` and better documented in the phpunit.xml.dist.

The TestUtil class has been refactored to more closely mirror the
structure and method / variable naming of the equivalent in
doctrine/dbal. This does not introduce any significant functional
changes. The only real difference is that the test output now prints
the selected database driver the first time it is referenced,
rather than repeating this through the test run.
2021-03-17 10:31:22 +00:00
acoulton
dd34bca4eb Upgrade previously-skipped tests to phpunit 9
These tests had not been running in CI so missed the previous
phpunit upgrade.

Note that assertions in decimal/floaty values in GH7941Test have
been changed to compare numerically with a reasonable level of
precision, instead of using regex. This is because the types
returned by the different drivers are inconsistent, and phpunit
now only allows regex comparisons on strings.
2021-03-17 10:31:22 +00:00
acoulton
3e21c50f61 Fix unit test and CI database driver / credential configuration
Builds using the github actions phpunit.xml files were not properly
recognising driver-specific configuration values, so were all
falling back to use the in-memory sqlite database instead of the
expected driver. This also meant a number of tests were skipped
as they rely on functionality not available in sqlite.

This commit addresses that by:

* REMOVING the automatic fallback to the sqlite memory database -
  phpunit.xml must now always specify explicit parameters for the
  desired connection.

* Displaying the active driver in the build output for visibility
  and debugging.

* Changing the way TestUtil loads the database config in line
  with the equivalent logic in doctrine/dbal, and to support the
  way that the config is/was specified in the phpunit.xml files
  for CI.

Note that this means a couple of the expected config variable names
have changed. Developers that are using customised phpunit.xml files
locally will need to update them to provide:

* Database config variables if they want to use the sqlite/memory
  driver - see phpunit.xml.dist for details.
* `db_driver` instead of `db_type`
* `db_user` instead of `db_username`
* `db_dbname` instead of `db_name`
* And, if in use, the equivalent changes to the `tmpdb_` values

The other change is that now if you provide any value for
`db_driver` we will attempt to create that connection type and
that will throw if other details (username / password / etc as
required by the driver) are not provided. Previously providing
partial configuration would cause TestUtil to silently fall back
to the in-memory sqlite driver.
2021-03-17 10:31:22 +00:00
Grégoire Paris
bc3592bcc8 Make phpdoc type correct 2021-03-16 19:20:11 +01:00
Grégoire Paris
5247c56fce Merge pull request #8539 from greg0ire/cs-20210311
🎉 Final CS batch 🎉
2021-03-14 18:47:03 +01:00
Grégoire Paris
cc37c490c2 Synchronize coding standard workflow with upstream
Now that there no longer are cs issues, we can thank diff-sniffer and
kiss it goodbye!
2021-03-13 09:55:38 +01:00
Grégoire Paris
95824efd61 Manually fix cs 2021-03-13 09:46:38 +01:00
Grégoire Paris
44d4712e64 Ignore rule about annotation phpdoc
These phpdoc is parsed by doctrine/annotations, and that package does
not understand things like array<string, mixed> yet.
2021-03-13 09:46:38 +01:00
Grégoire Paris
930f44c02f Ignore rule for externally-defined property 2021-03-13 00:08:03 +01:00
Grégoire Paris
6e3c011e65 Ignore rule about superflous comment
We can fix it with a breaking change.
2021-03-13 00:08:03 +01:00
Grégoire Paris
a82de0d422 Ignore rule about unused method
It cannot work when you call the private method like a callable.
2021-03-13 00:08:03 +01:00
Grégoire Paris
9917488179 Ignore rule about empty statements
This should be implemented in a separate pull request.
2021-03-12 08:20:46 +01:00
Grégoire Paris
93f31d2c33 Merge pull request #8533 from acoulton/test-string-lock-version
Add test coverage for passing optimistic lock version as string
2021-03-11 21:41:32 +01:00
acoulton
77356b954f Add test coverage for passing optimistic lock version as string
As discussed in #8527, when using optimistic locking with integer
version columns, Doctrine has always supported passing the lock
version as a string. For example when passing in a version
received in POST / GET.

Technically speaking this does not comply with the docs and phdoc
(which show the app explicitly casting to int before passing).

Nonetheless the maintainers decided it should continue to be valid
for now and reinstated the old soft-equals logic with #8531.

This modified test just avoids accidental changes in future.
2021-03-11 13:24:53 +00:00
Grégoire Paris
92f764206e Ignore broken rule 2021-03-11 09:11:24 +01:00
Grégoire Paris
141539673e Merge pull request #8530 from doctrine/cs-20210310
CS-batch 25/26 🤩
2021-03-11 00:05:31 +01:00
Grégoire Paris
23dc804c9b Merge pull request #8531 from beberlei/GH-8527-RevertLockEquals
[GH-8527] Revert cs fixes for entity version compares in lock+merge
2021-03-10 23:30:25 +01:00
Benjamin Eberlei
9e3baa7baa [GH-8527] Revert cs fixes for entity version compares in lock+merge 2021-03-10 22:46:07 +01:00
Grégoire Paris
322ea51ecf Manually fix cs 2021-03-10 22:17:31 +01:00
Grégoire Paris
21b046452b Merge pull request #8529 from greg0ire/cs-20210308
CS batch 24/26 🤞
2021-03-10 19:53:52 +01:00
Grégoire Paris
c57b81ada4 Manually fix cs 2021-03-09 21:15:33 +01:00
Grégoire Paris
4fa7c9c6de Merge pull request #8521 from greg0ire/cs-20210228
CS batch 23/an estimated 26
2021-03-08 19:57:18 +01:00
Grégoire Paris
3580517aac Manually fix cs 2021-03-01 21:49:10 +01:00
Grégoire Paris
f9e7c3c2d8 Merge pull request #8516 from greg0ire/cs-20210227
CS batch 22/an estimated 26
2021-02-28 21:27:52 +01:00
Grégoire Paris
3600c0fbca Manually fix cs 2021-02-28 18:31:38 +01:00
Grégoire Paris
f779513042 Remove unused properties
They should have been removed as part of a6b43b93ac
2021-02-28 18:31:38 +01:00
Diego Rin Martín
07d426edf5 Changed lock function to compare timestamps instead of DateTimeInterface objects directly. (#8508)
When using optimistic lock with DateTimeInterface based version field a bug appears due to the use of the === operator for comparing the lock version and the entity version. This comparison always resolves to false because the === operator when comparing objects is only true when both sides are the exact same instance of the object.

To fix the issue I have decided to compare timestamps instead the DateTimeInterface based objects directly, calling getTimestamp() method and doing a strict comparison.

Modified OptimisticLockException to use DateTimeInterface instead of DateTime class.

Added test suite to cover case.

Fixes #8499
2021-02-27 18:40:48 +01:00
Grégoire Paris
4afd4069be Merge pull request #8515 from greg0ire/cs-20210226
CS batch 21/an estimated 26
2021-02-27 14:30:25 +01:00
Grégoire Paris
239215c2e5 Merge pull request #8502 from greg0ire/rework-contributing-md
Rework CONTRIBUTING.md
2021-02-27 13:03:20 +01:00
Grégoire Paris
e6f11652d2 Add section for 2.9.x branch 2021-02-27 12:16:49 +01:00
Grégoire Paris
2910a73927 Rework badges urls
Some .x were missing, and ugly urlencoding can be avoided.
2021-02-27 12:10:47 +01:00
Grégoire Paris
3959b2743c Remove references to Travis 2021-02-27 12:03:35 +01:00
Grégoire Paris
658e54027e Remove trailing whitespace 2021-02-27 12:01:06 +01:00
Grégoire Paris
ba882451b0 Refer to our actual coding/standard
We do much more than just PSR 1 and 2
2021-02-27 12:01:05 +01:00
Grégoire Paris
1ed9840123 Refer to global workflow policy
We are doing things differently now, and the how is already documented.
2021-02-27 12:01:05 +01:00
Grégoire Paris
71044894a1 Manually fix cs 2021-02-26 21:30:49 +01:00
Grégoire Paris
1a41d6b87c Fix configuration mix up 2021-02-26 08:25:44 +01:00
Grégoire Paris
5dfcb08999 Merge pull request #8512 from greg0ire/cs-20210225
CS batch 20/an estimated 26
2021-02-25 23:23:24 +01:00
Grégoire Paris
284bd6fd03 Manually fix cs 2021-02-25 21:22:50 +01:00
Grégoire Paris
e40ac3e1d0 Merge pull request #8510 from greg0ire/cs-20210224
CS batch 19/an estimated 26
2021-02-24 22:41:28 +01:00
Grégoire Paris
0bce2472f2 Manually fix cs 2021-02-24 18:51:02 +01:00
Grégoire Paris
89f57de884 Merge pull request #8504 from greg0ire/cs-20210223
CS batch 18/an estimated 26
2021-02-23 23:18:42 +01:00
Grégoire Paris
ae19f40958 Merge pull request #8495 from Warxcell/fix_to_iterable_with_cache
Fix bug when using Result Cache with Query::toIterable
2021-02-23 20:16:39 +01:00
Grégoire Paris
c2d69a3c48 Merge pull request #8507 from greg0ire/address-move-away-from-master
Address move away from master
2021-02-23 18:19:47 +01:00
Grégoire Paris
6ce91dd37b Address move away from master 2021-02-23 09:24:40 +01:00
Grégoire Paris
57e6ba25c9 Merge pull request #8505 from dbu/patch-1
fix typo in changelog
2021-02-23 08:56:31 +01:00
David Buchmann
9d2e67bbb4 fix typo in changelog 2021-02-23 08:17:31 +01:00
Grégoire Paris
2dce5b20ad Manually fix cs 2021-02-22 23:50:09 +01:00
Warxcell
930859f803 Fix bug when using ResultCache with Query::toIterable.
Signed-off-by: Warxcell <warxcell@gmail.com>
2021-02-22 23:35:22 +02:00
Yup
a70c73ae3a Use RegEx to match if queryPart contains OR/AND (#8453)
This allows fixes cases of queries that contain line feeds or tabs but
do not benefit from automatic wrapping of parenthesis.
2021-02-22 20:58:06 +01:00
David Buchmann
074346b8d5 Note deprecation of AbstractQuery::iterator (#8497) 2021-02-22 20:30:05 +01:00
Grégoire Paris
9ed4a8c043 Merge pull request #8498 from greg0ire/cs-20210222
CS batch 17/an estimated 26
2021-02-22 20:25:06 +01:00
Grégoire Paris
9c917811e5 Manually fix cs 2021-02-22 09:12:04 +01:00
Grégoire Paris
f883820257 Ignore rule about wrong comment style 2021-02-22 08:48:46 +01:00
Grégoire Paris
a32045dd51 Ignore rule violated by external package 2021-02-22 07:52:57 +01:00
Grégoire Paris
672b04a55d Merge pull request #8483 from olsavmic/fix-single-scalar-hydrator-memory-leak-on-exception
Fix single scalar hydrator memory leak on exception
2021-02-21 21:12:50 +01:00
Grégoire Paris
261334aca2 Merge pull request #8494 from greg0ire/cs-20210221
CS batch 16/an estimated 26
2021-02-21 20:29:47 +01:00
Warxcell
7f6ed094cd Add test to verify that using ResultCache with Query::toIterable is failing. 2021-02-21 21:12:52 +02:00
Grégoire Paris
a792655813 Manually fix cs 2021-02-21 12:20:19 +01:00
Grégoire Paris
fb71204910 Relax assertion (#8493)
EntityManager is too restrictive, any implementation can actually be
returned here.

Closes #8488
2021-02-21 07:43:56 +01:00
Grégoire Paris
b918661cf1 Merge pull request #8492 from greg0ire/cs-20210220
CS batch 15/an estimated 26
2021-02-20 20:49:25 +01:00
Michael Olšavský
7971a53164 Method hydrateAll() does not take into account possible exception
from hydrateAllData() which in turn does not call cleanup()
2021-02-20 18:58:50 +01:00
Grégoire Paris
a175f96ae8 Manually fix cs 2021-02-20 15:37:15 +01:00
Grégoire Paris
7c1cde6471 Ignore rule that triggers on external property 2021-02-20 15:32:26 +01:00
Grégoire Paris
1ffc0cacf4 Merge pull request #8491 from greg0ire/cs-20210219
CS batch 14/an estimated 26
2021-02-20 11:10:27 +01:00
Grégoire Paris
e979d0d50f Manually fix cs 2021-02-20 00:01:41 +01:00
Grégoire Paris
553ea03079 Ignore rule about case mismatch
@group does not have to do with the Group entity at all.
2021-02-19 23:33:25 +01:00
Grégoire Paris
149014879d Ignore rule about property defined externally 2021-02-19 23:11:43 +01:00
Grégoire Paris
b991c58988 Merge pull request #8490 from greg0ire/cs-20210218
CS batch 13/an estimated 26
2021-02-19 23:08:25 +01:00
Aleksandr Frolov
ee9627b82e Update QueryBuilder::setParameters docs (#8487)
Use `ArrayCollection` instead of plain array (which is supported only for bc)
2021-02-19 01:45:14 +01:00
Grégoire Paris
e3f03414f9 Manually fix cs 2021-02-18 23:17:03 +01:00
Grégoire Paris
1f406fd3df Merge pull request #8484 from greg0ire/cs-20210217
CS batch 12/an estimated 26
2021-02-18 21:54:43 +01:00
Grégoire Paris
b747bf15ff Manually fix cs 2021-02-17 16:32:39 +01:00
Grégoire Paris
5fe85bfc03 Ignore rule about underscore in method name
We inherit from a class defined in another package.
2021-02-17 15:55:21 +01:00
Grégoire Paris
0dccf05ca8 Automatically fix cs 2021-02-17 15:55:21 +01:00
498 changed files with 7858 additions and 2681 deletions

View File

@@ -7,7 +7,7 @@
"versions": [
{
"name": "3.0",
"branchName": "master",
"branchName": "3.0.x",
"slug": "latest",
"upcoming": true
},

5
.gitattributes vendored
View File

@@ -2,10 +2,9 @@
/tools export-ignore
/docs export-ignore
/.github export-ignore
.doctrine-project.json export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.travis.yml export-ignore
build.properties export-ignore
build.properties.dev export-ignore
build.xml export-ignore
@@ -15,4 +14,6 @@ run-all.sh export-ignore
phpcs.xml.dist export-ignore
phpbench.json export-ignore
phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore

View File

@@ -2,11 +2,16 @@ name: "Coding Standards"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-latest"
runs-on: "ubuntu-20.04"
strategy:
matrix:
@@ -16,8 +21,6 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 10
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
@@ -28,16 +31,9 @@ jobs:
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "highest"
- name: "Install diff-sniffer"
run: "wget https://github.com/diff-sniffer/diff-sniffer/releases/download/0.5.1/diff-sniffer.phar"
- name: "Fetch head branch"
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
- name: "Run diff-sniffer"
run: "php diff-sniffer.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle | cs2pr"
- name: "Run phpcbf"
run: "vendor/bin/phpcbf"
# https://github.com/doctrine/.github/issues/3
- name: "Run PHP_CodeSniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"

View File

@@ -1,12 +1,18 @@
name: Static Analysis
name: "Static Analysis"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
static-analysis-phpstan:
name: "PHPStan"
runs-on: "ubuntu-latest"
name: "Static Analysis with PHPStan"
runs-on: "ubuntu-20.04"
strategy:
matrix:
@@ -22,17 +28,18 @@ jobs:
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: cs2pr
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "highest"
- name: "Run a static analysis with phpstan/phpstan"
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
run: "vendor/bin/phpstan analyse"
static-analysis-psalm:
name: "Psalm"
runs-on: "ubuntu-latest"
name: "Static Analysis with Psalm"
runs-on: "ubuntu-20.04"
strategy:
matrix:
@@ -51,6 +58,8 @@ jobs:
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "highest"
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"

View File

@@ -6,30 +6,17 @@ Before we can merge your Pull-Request here are some guidelines that you need to
These guidelines exist not to annoy you, but to keep the code base clean,
unified and future proof.
## We only accept PRs to "master"
Doctrine has [general contributing guidelines][contributor workflow], make
sure you follow them.
Our branching strategy is "everything to master first", even
bugfixes and we then merge them into the stable branches. You should only
open pull requests against the master branch. Otherwise we cannot accept the PR.
There is one exception to the rule, when we merged a bug into some stable branches
we do occasionally accept pull requests that merge the same bug fix into earlier
branches.
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
## Coding Standard
We use PSR-1 and PSR-2:
This project follows [`doctrine/coding-standard`][coding standard homepage].
You may fix many some of the issues with `vendor/bin/phpcbf`.
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
with some exceptions/differences:
* Keep the nesting of control structures per method as small as possible
* Align equals (=) signs
* Add spaces between assignment, control and return statements
* Prefer early exit over nesting conditions
* Add spaces around a negation if condition ``if ( ! $cond)``
[coding standard homepage]: https://github.com/doctrine/coding-standard
## Unit-Tests
@@ -56,25 +43,19 @@ curl -sS https://getcomposer.org/installer | php --
To run the testsuite against another database, copy the ``phpunit.xml.dist``
to for example ``mysql.phpunit.xml`` and edit the parameters. You can
take a look at the ``tests/travis`` folder for some examples. Then run:
take a look at the ``ci/github/phpunit`` directory for some examples. Then run:
vendor/bin/phpunit -c mysql.phpunit.xml
If you do not provide these parameters, the test suite will use an in-memory
sqlite database.
Tips for creating unit tests:
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
See `https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
example.
## Travis
We automatically run your pull request through [Travis CI](http://www.travis-ci.org)
against SQLite, MySQL and PostgreSQL. If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening up a Pull-Request.
## Getting merged
Please allow us time to review your pull requests. We will give our best to review

View File

@@ -1,7 +1,7 @@
| [Master][Master] | [2.8.x][2.8] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
| [3.0.x][3.0] | [2.9.x][2.9] | [2.8.x][2.8] |
|:----------------:|:----------------:|:----------:|
| [![Build status][3.0 image]][3.0] | [![Build status][2.9 image]][2.9] | [![Build status][2.8 image]][2.8] |
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.9 coverage image]][2.9 coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
@@ -16,11 +16,15 @@ without requiring unnecessary code duplication.
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/orm
[Master coverage image]: https://codecov.io/gh/doctrine/orm/branch/master/graph/badge.svg
[Master coverage]: https://codecov.io/gh/doctrine/orm/branch/master
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.svg?style=flat-square
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
[2.9 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.9.x
[2.9]: https://github.com/doctrine/orm/tree/2.9.x
[2.9 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.9.x/graph/badge.svg
[2.9 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.9.x
[2.8 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg
[2.8]: https://github.com/doctrine/orm/tree/2.8
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8/graph/badge.svg
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8.x/graph/badge.svg
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8.x

View File

@@ -10,8 +10,8 @@ we cannot protect you from SQL injection.
Please read the documentation chapter on Security in Doctrine DBAL and ORM to
understand the assumptions we make.
- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
- [ORM Security Page](https://github.com/doctrine/orm/blob/master/docs/en/reference/security.rst)
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html)
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core

View File

@@ -5,6 +5,11 @@
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
# Upgrade to 2.7
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env php
<?php
include('doctrine.php');
include(__DIR__ . '/doctrine.php');

View File

@@ -7,6 +7,10 @@
failOnRisky="true"
>
<php>
<!-- use an in-memory sqlite database -->
<var name="db_driver" value="pdo_sqlite"/>
<var name="db_memory" value="true"/>
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>

View File

@@ -28,15 +28,16 @@
"doctrine/inflector": "^1.4|^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
"doctrine/persistence": "^2.2",
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
},
"require-dev": {
"doctrine/coding-standard": "^8.0",
"phpstan/phpstan": "^0.12.18",
"doctrine/coding-standard": "^9.0",
"phpstan/phpstan": "^0.12.83",
"phpunit/phpunit": "^8.5|^9.4",
"symfony/yaml": "^3.4|^4.0|^5.0",
"vimeo/psalm": "4.1.1"
"squizlabs/php_codesniffer": "3.6.0",
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
"vimeo/psalm": "4.7.0"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
@@ -51,12 +52,7 @@
}
},
"bin": ["bin/doctrine"],
"extra": {
"branch-alias": {
"dev-master": "2.7.x-dev"
}
},
"archive": {
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
}
}

View File

@@ -89,11 +89,11 @@ with the batching strategy that was already used for bulk inserts:
foreach ($q->toIterable() as $user) {
$user->increaseCredit();
$user->calculateNewBonuses();
++$i;
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all updates.
$em->clear(); // Detaches all objects from Doctrine!
}
++$i;
}
$em->flush();
@@ -147,11 +147,11 @@ The following example shows how to do this:
$q = $em->createQuery('select u from MyProject\Model\User u');
foreach($q->toIterable() as $row) {
$em->remove($row);
++$i;
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all deletions.
$em->clear(); // Detaches all objects from Doctrine!
}
++$i;
}
$em->flush();

View File

@@ -43,7 +43,7 @@ these methods.
This documentation does not cover every single cache driver included
with Doctrine. For an up-to-date-list, see the
`cache directory on GitHub <https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache>`_.
`cache directory on GitHub <https://github.com/doctrine/cache/tree/2.8.x/lib/Doctrine/Common/Cache>`_.
PhpFileCache
~~~~~~~~~~~~

View File

@@ -277,10 +277,17 @@ following syntax:
.. code-block:: php
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Parameter;
// $qb instanceof QueryBuilder
// Query here...
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));
$qb->setParameters(new ArrayCollection([
new Parameter('1', 'value for ?1'),
new Parameter('2', 'value for ?2')
]));
Getting already bound parameters is easy - simply use the above
mentioned syntax with "getParameter()" or "getParameters()":

View File

@@ -24,7 +24,7 @@ use Countable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Cache\TimestampCacheKey;
@@ -112,7 +112,7 @@ abstract class AbstractQuery
/**
* The map of query hints.
*
* @var array
* @psalm-var array<string, mixed>
*/
protected $_hints = [];
@@ -123,7 +123,7 @@ abstract class AbstractQuery
*/
protected $_hydrationMode = self::HYDRATE_OBJECT;
/** @var QueryCacheProfile */
/** @var QueryCacheProfile|null */
protected $_queryCacheProfile;
/**
@@ -289,7 +289,7 @@ abstract class AbstractQuery
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -346,10 +346,9 @@ abstract class AbstractQuery
* Sets a collection of query parameters.
*
* @param ArrayCollection|mixed[] $parameters
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*
* @return static This query instance.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*/
public function setParameters($parameters)
{
@@ -402,10 +401,9 @@ abstract class AbstractQuery
* @param mixed $value
*
* @return mixed[]|string|int|float|bool
* @psalm-return array|scalar
*
* @throws ORMInvalidArgumentException
*
* @psalm-return array|scalar
*/
public function processParameterValue($value)
{
@@ -593,6 +591,7 @@ abstract class AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
/** @phpstan-ignore-next-line */
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
@@ -723,7 +722,7 @@ abstract class AbstractQuery
}
/**
* @return QueryCacheProfile
* @return QueryCacheProfile|null
*/
public function getQueryCacheProfile()
{
@@ -796,7 +795,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_ARRAY).
*
* @return array<int,mixed>
* @return mixed[]
*/
public function getArrayResult()
{
@@ -808,7 +807,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_SCALAR).
*
* @return array<int,mixed>
* @return mixed[]
*/
public function getScalarResult()
{
@@ -949,7 +948,7 @@ abstract class AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
*
* @deprecated
* @deprecated 2.8 Use {@see toIterable} instead. See https://github.com/doctrine/orm/issues/8463
*
* @param ArrayCollection|mixed[]|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
@@ -981,8 +980,8 @@ abstract class AbstractQuery
* Executes the query and returns an iterable that can be used to incrementally
* iterate over the result.
*
* @param ArrayCollection|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
* @param ArrayCollection|array|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
* @return iterable<mixed>
*/
@@ -1045,7 +1044,7 @@ abstract class AbstractQuery
$this->setParameters($parameters);
}
$setCacheEntry = static function (): void {
$setCacheEntry = static function ($data): void {
};
if ($this->_hydrationCacheProfile !== null) {
@@ -1203,7 +1202,9 @@ abstract class AbstractQuery
/**
* Executes the query and returns a the resulting Statement object.
*
* @return Statement The executed database statement that holds the results.
* @return ResultStatement|int The executed database statement that holds
* the results, or an integer indicating how
* many rows were affected.
*/
abstract protected function _doExecute();

View File

@@ -30,9 +30,9 @@ use Doctrine\ORM\PersistentCollection;
interface CollectionHydrator
{
/**
* @param ClassMetadata $metadata The entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param mixed[]|Collection $collection The collection.
* @param ClassMetadata $metadata The entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param array|mixed[]|Collection $collection The collection.
*
* @return CollectionCacheEntry
*/

View File

@@ -48,7 +48,7 @@ class DefaultCache implements Cache
/** @var QueryCache[] */
private $queryCaches = [];
/** @var QueryCache */
/** @var QueryCache|null */
private $defaultQueryCache;
public function __construct(EntityManagerInterface $em)

View File

@@ -111,6 +111,10 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);
}
@@ -134,6 +138,10 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);
}
@@ -201,7 +209,7 @@ class DefaultCacheFactory implements CacheFactory
}
$directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region'];
$region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region']));
$region = new FileLockRegion($region, $directory, (string) $this->regionsConfig->getLockLifetime($cache['region']));
}
return $this->regions[$cache['region']] = $region;

View File

@@ -345,7 +345,6 @@ class DefaultQueryCache implements QueryCache
* @param mixed $assocValue
*
* @return mixed[]|null
*
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
*/
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
@@ -428,7 +427,7 @@ class DefaultQueryCache implements QueryCache
* @param mixed $value
* @param array<mixed> $path
*
* @return array<object>|object|null
* @return mixed
*/
private function getAssociationPathValue($value, array $path)
{

View File

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

View File

@@ -72,7 +72,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/** @var CollectionHydrator */
protected $hydrator;
/** @var CacheLogger */
/** @var CacheLogger|null */
protected $cacheLogger;
/**

View File

@@ -52,7 +52,7 @@ interface CachedCollectionPersister extends CachedPersister, CollectionPersister
/**
* Stores a collection into cache
*
* @param mixed[]|Collection $elements
* @param array|mixed[]|Collection $elements
*
* @return void
*/

View File

@@ -75,7 +75,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/** @var Cache */
protected $cache;
/** @var CacheLogger */
/** @var CacheLogger|null */
protected $cacheLogger;
/** @var string */

View File

@@ -59,12 +59,12 @@ class FileLockRegion implements ConcurrentRegion
/** @var string */
private $directory;
/** @var int */
/** @psalm-var numeric-string */
private $lockLifetime;
/**
* @param string $directory
* @param string $lockLifetime
* @param string $directory
* @param numeric-string $lockLifetime
*
* @throws InvalidArgumentException
*/

View File

@@ -36,6 +36,7 @@ use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\EntityListenerResolver;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
@@ -149,8 +150,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader
* is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported.
*
* @param array $paths
* @param bool $useSimpleAnnotationReader
* @param bool $useSimpleAnnotationReader
* @psalm-param string|list<string> $paths
*
* @return AnnotationDriver
*/
@@ -207,7 +208,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the entity alias map.
*
* @param array $entityNamespaces
* @psalm-param array<string, string> $entityNamespaces
*
* @return void
*/
@@ -219,7 +220,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Retrieves the list of registered entity namespace aliases.
*
* @return array
* @psalm-return array<string, string>
*/
public function getEntityNamespaces()
{
@@ -348,8 +349,11 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name The name of the query.
*
* @return array A tuple with the first element being the SQL string and the second
* element being the ResultSetMapping.
* @psalm-return array{string, ResultSetMapping} A tuple with the first
* element being the SQL
* string and the second
* element being the
* ResultSetMapping.
*
* @throws ORMException
*/
@@ -421,7 +425,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
* @param string $name
*
* @return string|null
*
* @psalm-return ?class-string
*/
public function getCustomStringFunction($name)
@@ -439,7 +442,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* Any previously added string functions are discarded.
*
* @param array $functions The map of custom DQL string functions.
* @psalm-param array<string, class-string> $functions The map of custom
* DQL string functions.
*
* @return void
*/
@@ -473,7 +477,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
* @param string $name
*
* @return string|null
*
* @psalm-return ?class-string
*/
public function getCustomNumericFunction($name)
@@ -491,7 +494,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* Any previously added numeric functions are discarded.
*
* @param array $functions The map of custom DQL numeric functions.
* @psalm-param array<string, class-string> $functions The map of custom
* DQL numeric functions.
*
* @return void
*/
@@ -511,10 +515,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name Function name.
* @param string|callable $className Class name or a callable that returns the function.
* @psalm-param class-string|callable $className
*
* @return void
*
* @psalm-param class-string|callable $className
*/
public function addCustomDatetimeFunction($name, $className)
{
@@ -527,7 +530,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
* @param string $name
*
* @return string|null
*
* @psalm-return ?class-string $name
*/
public function getCustomDatetimeFunction($name)
@@ -546,10 +548,9 @@ 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, string> $functions
*
* @return void
*
* @psalm-param array<string, string> $functions
*/
public function setCustomDatetimeFunctions(array $functions)
{
@@ -561,7 +562,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the custom hydrator modes in one pass.
*
* @param array $modes An array of ($modeName => $hydrator).
* @param array<string, class-string> $modes An array of ($modeName => $hydrator).
*
* @return void
*/
@@ -580,7 +581,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
* @param string $modeName The hydration mode name.
*
* @return string|null The hydrator class name.
*
* @psalm-return ?class-string
*/
public function getCustomHydrationMode($modeName)
@@ -592,7 +592,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Adds a custom hydration mode.
*
* @param string $modeName The hydration mode name.
* @param string $hydrator The hydrator class name.
* @psalm-param class-string $hydrator The hydrator class name.
*
* @return void
*/
@@ -605,10 +605,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Sets a class metadata factory.
*
* @param string $cmfName
* @psalm-param class-string $cmfName
*
* @return void
*
* @psalm-param class-string $cmfName
*/
public function setClassMetadataFactoryName($cmfName)
{
@@ -617,7 +616,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* @return string
*
* @psalm-return class-string
*/
public function getClassMetadataFactoryName()
@@ -647,7 +645,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @return string|null The class name of the filter, or null if it is not
* defined.
*
* @psalm-return ?class-string
*/
public function getFilterClassName($name)
@@ -679,7 +676,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Get default repository class.
*
* @return string
*
* @psalm-return class-string
*/
public function getDefaultRepositoryClassName()
@@ -816,7 +812,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Returns query hints, which will be applied to every query in application
*
* @return array
* @psalm-return array<string, mixed>
*/
public function getDefaultQueryHints()
{
@@ -826,7 +822,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets array of query hints, which will be applied to every query in application
*
* @param array $defaultQueryHints
* @psalm-param array<string, mixed> $defaultQueryHints
*/
public function setDefaultQueryHints(array $defaultQueryHints)
{

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM;
use BadMethodCallException;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\EventManager;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\DBAL\Connection;
@@ -47,6 +48,7 @@ use function is_callable;
use function is_object;
use function is_string;
use function ltrim;
use function method_exists;
use function sprintf;
use function trigger_error;
@@ -166,7 +168,14 @@ use const E_USER_DEPRECATED;
$this->metadataFactory = new $metadataFactoryClassName();
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$metadataCache = $this->config->getMetadataCacheImpl();
if ($metadataCache !== null) {
if (method_exists($this->metadataFactory, 'setCache')) {
$this->metadataFactory->setCache(CacheAdapter::wrap($metadataCache));
} else {
$this->metadataFactory->setCacheDriver($metadataCache);
}
}
$this->repositoryFactory = $config->getRepositoryFactory();
$this->unitOfWork = new UnitOfWork($this);
@@ -284,9 +293,7 @@ use const E_USER_DEPRECATED;
*
* Internal note: Performance-sensitive method.
*
* @param string $className
*
* @return ClassMetadata
* {@inheritDoc}
*/
public function getClassMetadata($className)
{
@@ -386,8 +393,10 @@ use const E_USER_DEPRECATED;
* during the search.
* @param int|null $lockVersion The version of the entity to find when using
* optimistic locking.
* @psalm-param class-string<T> $className
*
* @return object|null The entity instance or NULL if the entity can not be found.
* @psalm-return ?T
*
* @throws OptimisticLockException
* @throws ORMInvalidArgumentException
@@ -395,8 +404,6 @@ use const E_USER_DEPRECATED;
* @throws ORMException
*
* @template T
* @psalm-param class-string<T> $className
* @psalm-return ?T
*/
public function find($className, $id, $lockMode = null, $lockVersion = null)
{
@@ -746,12 +753,12 @@ use const E_USER_DEPRECATED;
* Gets the repository for an entity class.
*
* @param string $entityName The name of the entity.
* @psalm-param class-string<T> $entityName
*
* @return ObjectRepository|EntityRepository The repository class.
* @psalm-return EntityRepository<T>
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return EntityRepository<T>
*/
public function getRepository($entityName)
{

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM;
use BadMethodCallException;
use DateTimeInterface;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
@@ -33,7 +34,7 @@ use Doctrine\Persistence\ObjectManager;
/**
* EntityManager interface
*
* @method Mapping\ClassMetadata getClassMetadata($className)
* @method Mapping\ClassMetadataFactory getMetadataFactory()
*/
interface EntityManagerInterface extends ObjectManager
{
@@ -154,14 +155,14 @@ interface EntityManagerInterface extends ObjectManager
*
* @param string $entityName The name of the entity type.
* @param mixed $id The entity identifier.
* @psalm-param class-string<T> $entityName
*
* @return object|null The entity reference.
* @psalm-return ?T
*
* @throws ORMException
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return ?T
*/
public function getReference($entityName, $id);
@@ -213,9 +214,9 @@ interface EntityManagerInterface extends ObjectManager
/**
* Acquire a lock on the given entity.
*
* @param object $entity
* @param int $lockMode
* @param int|null $lockVersion
* @param object $entity
* @param int $lockMode
* @param int|DateTimeInterface|null $lockVersion
*
* @return void
*
@@ -304,4 +305,16 @@ interface EntityManagerInterface extends ObjectManager
* @return bool True, if the EM has a filter collection.
*/
public function hasFilters();
/**
* {@inheritDoc}
*
* @psalm-param string|class-string<T> $className
*
* @return Mapping\ClassMetadata
* @psalm-return Mapping\ClassMetadata<T>
*
* @template T of object
*/
public function getClassMetadata($className);
}

View File

@@ -160,7 +160,6 @@ class EntityRepository implements ObjectRepository, Selectable
* @param int|null $lockVersion The lock version.
*
* @return object|null The entity instance or NULL if the entity can not be found.
*
* @psalm-return ?T
*/
public function find($id, $lockMode = null, $lockVersion = null)
@@ -171,9 +170,7 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Finds all entities in the repository.
*
* @return array The entities.
*
* @psalm-return list<T>
* @psalm-return list<T> The entities.
*/
public function findAll()
{
@@ -183,14 +180,12 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Finds entities by a set of criteria.
*
* @param array $criteria
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
* @param int|null $limit
* @param int|null $offset
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, string>|null $orderBy
*
* @return array The objects.
*
* @psalm-return list<T>
* @psalm-return list<T> The objects.
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
@@ -202,11 +197,10 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Finds a single entity by a set of criteria.
*
* @param array $criteria
* @param array|null $orderBy
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, string>|null $orderBy
*
* @return object|null The entity instance or NULL if the entity can not be found.
*
* @psalm-return ?T
*/
public function findOneBy(array $criteria, ?array $orderBy = null)
@@ -219,7 +213,7 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Counts entities by a set of criteria.
*
* @param array $criteria
* @psalm-param array<string, mixed> $criteria
*
* @return int The cardinality of the objects that match the given criteria.
*
@@ -234,7 +228,7 @@ class EntityRepository implements ObjectRepository, Selectable
* Adds support for magic method calls.
*
* @param string $method
* @param array $arguments
* @psalm-param list<mixed> $arguments
*
* @return mixed The returned value from the resolved method.
*
@@ -298,8 +292,6 @@ class EntityRepository implements ObjectRepository, Selectable
* Select all elements from a selectable that match the expression and
* return a new collection containing these elements.
*
* @return Collection
*
* @psalm-return Collection<int, T>
*/
public function matching(Criteria $criteria)
@@ -312,15 +304,15 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Resolves a magic method call to the proper existent method at `EntityRepository`.
*
* @param string $method The method to call
* @param string $by The property name used as condition
* @param array $arguments The arguments to pass at method call
* @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
*
* @return mixed
*
* @throws ORMException If the method called is invalid or the requested field/association does not exist.
*/
private function resolveMagicCall($method, $by, array $arguments)
private function resolveMagicCall(string $method, string $by, array $arguments)
{
if (! $arguments) {
throw ORMException::findByRequiresParameter($method . $by);

View File

@@ -20,7 +20,7 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
@@ -79,28 +79,28 @@ abstract class AbstractHydrator
/**
* Local ClassMetadata cache to avoid going to the EntityManager all the time.
*
* @var array
* @var array<string, ClassMetadata>
*/
protected $_metadataCache = [];
/**
* The cache used during row-by-row hydration.
*
* @var array
* @var array<string, mixed[]|null>
*/
protected $_cache = [];
/**
* The statement that provides the data to hydrate.
*
* @var Statement
* @var ResultStatement
*/
protected $_stmt;
/**
* The query hints.
*
* @var array
* @var array<string, mixed>
*/
protected $_hints;
@@ -123,7 +123,7 @@ abstract class AbstractHydrator
*
* @param object $stmt
* @param object $resultSetMapping
* @param array $hints
* @psalm-param array<string, mixed> $hints
*
* @return IterableResult
*/
@@ -150,11 +150,11 @@ abstract class AbstractHydrator
/**
* Initiates a row-by-row hydration.
*
* @param mixed[] $hints
* @psalm-param array<string, mixed> $hints
*
* @return iterable<mixed>
*/
public function toIterable(Statement $stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable
public function toIterable(ResultStatement $stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable
{
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
@@ -194,9 +194,9 @@ abstract class AbstractHydrator
*
* @param object $stmt
* @param object $resultSetMapping
* @param array $hints
* @psalm-param array<string, string> $hints
*
* @return array
* @return mixed[]
*/
public function hydrateAll($stmt, $resultSetMapping, array $hints = [])
{
@@ -205,12 +205,13 @@ abstract class AbstractHydrator
$this->_hints = $hints;
$this->_em->getEventManager()->addEventListener([Events::onClear], $this);
$this->prepare();
$result = $this->hydrateAllData();
$this->cleanup();
try {
$result = $this->hydrateAllData();
} finally {
$this->cleanup();
}
return $result;
}
@@ -318,15 +319,14 @@ abstract class AbstractHydrator
* field names during this procedure as well as any necessary conversions on
* the values applied. Scalar values are kept in a specific key 'scalars'.
*
* @param mixed[] $data SQL Result Row.
* @param array &$id Dql-Alias => ID-Hash.
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
* @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?
*
* @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{
* data: array<array-key, array>,
* newObjects?: array<array-key, array{
@@ -341,7 +341,8 @@ abstract class AbstractHydrator
$rowData = ['data' => []];
foreach ($data as $key => $value) {
if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) {
$cacheKeyInfo = $this->hydrateColumnInfo($key);
if ($cacheKeyInfo === null) {
continue;
}
@@ -410,16 +411,17 @@ abstract class AbstractHydrator
* values according to their types. The resulting row has the same number
* of elements as before.
*
* @param array $data
* @psalm-param array<string, mixed> $data
*
* @return array The processed row.
* @psalm-return array<string, mixed> The processed row.
*/
protected function gatherScalarRowData(&$data)
{
$rowData = [];
foreach ($data as $key => $value) {
if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) {
$cacheKeyInfo = $this->hydrateColumnInfo($key);
if ($cacheKeyInfo === null) {
continue;
}
@@ -445,7 +447,7 @@ abstract class AbstractHydrator
*
* @param string $key Column name
*
* @return array|null
* @psalm-return array<string, mixed>|null
*/
protected function hydrateColumnInfo($key)
{

View File

@@ -43,8 +43,12 @@ class HydrationException extends ORMException
*/
public static function parentObjectOfRelationNotFound($alias, $parentAlias)
{
return new self("The parent object of entity result with alias '$alias' was not found."
. " The parent alias is '$parentAlias'.");
return new self(sprintf(
"The parent object of entity result with alias '%s' was not found."
. " The parent alias is '%s'.",
$alias,
$parentAlias
));
}
/**
@@ -96,7 +100,7 @@ class HydrationException extends ORMException
/**
* @param string $discrValue
* @param array $discrMap
* @psalm-param array<string, string> $discrMap
*
* @return HydrationException
*/

View File

@@ -68,7 +68,7 @@ class IterableResult implements Iterator
/**
* Gets the next set of results.
*
* @return array|false
* @return mixed[]|false
*/
public function next()
{

View File

@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use PDO;
@@ -222,8 +222,8 @@ class ObjectHydrator extends AbstractHydrator
/**
* Gets an entity instance.
*
* @param array $data The instance data.
* @param string $dqlAlias The DQL alias of the entity's class.
* @psalm-param array<string, mixed> $data The instance data.
*
* @return object The entity.
*
@@ -273,7 +273,7 @@ class ObjectHydrator extends AbstractHydrator
/**
* @param string $className
* @param array $data
* @psalm-param array<string, mixed> $data
*
* @return mixed
*/
@@ -282,7 +282,6 @@ class ObjectHydrator extends AbstractHydrator
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_metadataCache[$className];
/** @var ClassMetadata $class */
if ($class->isIdentifierComposite) {
$idHash = '';
@@ -317,8 +316,8 @@ class ObjectHydrator extends AbstractHydrator
* level of the hydrated result. A typical example are the objects of the type
* specified by the FROM clause in a DQL query.
*
* @param array $row The data of the row to process.
* @param array $result The result array to fill.
* @param mixed[] $row The data of the row to process.
* @param mixed[] $result The result array to fill.
*
* @return void
*/
@@ -397,7 +396,8 @@ class ObjectHydrator extends AbstractHydrator
if (! $indexExists || ! $indexIsValid) {
if (isset($this->existingCollections[$collKey])) {
// Collection exists, only look for the element in the identity map.
if ($element = $this->getEntityFromIdentityMap($entityName, $data)) {
$element = $this->getEntityFromIdentityMap($entityName, $data);
if ($element) {
$this->resultPointers[$dqlAlias] = $element;
} else {
unset($this->resultPointers[$dqlAlias]);
@@ -431,7 +431,7 @@ class ObjectHydrator extends AbstractHydrator
// PATH B: Single-valued association
$reflFieldValue = $reflField->getValue($parentObject);
if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && ! $reflFieldValue->__isInitialized__)) {
if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && ! $reflFieldValue->__isInitialized())) {
// we only need to take action if this value is null,
// we refresh the entity or its an uninitialized proxy.
if (isset($nonemptyComponents[$dqlAlias])) {

View File

@@ -96,7 +96,8 @@ class SimpleObjectHydrator extends AbstractHydrator
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
// Find mapped discriminator column from the result set.
if ($metaMappingDiscrColumnName = array_search($discrColumnName, $this->_rsm->metaMappings)) {
$metaMappingDiscrColumnName = array_search($discrColumnName, $this->_rsm->metaMappings);
if ($metaMappingDiscrColumnName) {
$discrColumnName = $metaMappingDiscrColumnName;
}
@@ -132,6 +133,11 @@ class SimpleObjectHydrator extends AbstractHydrator
continue;
}
// If we have inheritance in resultset, make sure the field belongs to the correct class
if (isset($cacheKeyInfo['discriminatorValues']) && ! in_array((string) $discrColumnValue, $cacheKeyInfo['discriminatorValues'], true)) {
continue;
}
// Check if value is null before conversion (because some types convert null to something else)
$valueIsNull = $value === null;
@@ -145,11 +151,6 @@ class SimpleObjectHydrator extends AbstractHydrator
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
if (! isset($data[$fieldName]) || ! $valueIsNull) {
// If we have inheritance in resultset, make sure the field belongs to the correct class
if (isset($cacheKeyInfo['discriminatorValues']) && ! in_array((string) $discrColumnValue, $cacheKeyInfo['discriminatorValues'], true)) {
continue;
}
$data[$fieldName] = $value;
}
}

View File

@@ -137,8 +137,8 @@ class ClassMetadataBuilder
/**
* Adds Index.
*
* @param array $columns
* @param string $name
* @psalm-param list<string> $columns
*
* @return static
*/
@@ -156,8 +156,8 @@ class ClassMetadataBuilder
/**
* Adds Unique Constraint.
*
* @param array $columns
* @param string $name
* @psalm-param list<string> $columns
*
* @return static
*/
@@ -297,7 +297,7 @@ class ClassMetadataBuilder
*
* @param string $name
* @param string $type
* @param array $mapping
* @psalm-param array<string, mixed> $mapping
*
* @return static
*/

View File

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

View File

@@ -24,6 +24,8 @@ namespace Doctrine\ORM\Mapping;
* {@inheritDoc}
*
* @todo remove or rename ClassMetadataInfo to ClassMetadata
* @template T of object
* @template-extends ClassMetadataInfo<T>
*/
class ClassMetadata extends ClassMetadataInfo
{

View File

@@ -41,6 +41,7 @@ use ReflectionClass;
use ReflectionException;
use function array_map;
use function assert;
use function class_exists;
use function count;
use function end;
@@ -53,6 +54,10 @@ use function strtolower;
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
* metadata mapping information of a class which describes how a class should be mapped
* to a relational database.
*
* @method ClassMetadata[] getAllMetadata()
* @method ClassMetadata[] getLoadedMetadata()
* @method ClassMetadata getMetadataFor($className)
*/
class ClassMetadataFactory extends AbstractClassMetadataFactory
{
@@ -119,8 +124,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
*/
protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
{
/** @var ClassMetadata $class */
/** @var ClassMetadata $parent */
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
@@ -728,7 +731,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
*/
protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
{
/** @var ClassMetadata $class */
assert($class instanceof ClassMetadata);
$class->wakeupReflection($reflService);
}
@@ -737,7 +740,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
*/
protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
{
/** @var ClassMetadata $class */
assert($class instanceof ClassMetadata);
$class->initializeReflection($reflService);
}
@@ -746,6 +749,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
*/
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
{
/** @psalm-var class-string */
return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
}

View File

@@ -72,6 +72,9 @@ use function trim;
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* @template T of object
* @template-implements ClassMetadata<T>
*/
class ClassMetadataInfo implements ClassMetadata
{
@@ -235,6 +238,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The name of the entity class.
*
* @var string
* @psalm-var class-string<T>
*/
public $name;
@@ -297,28 +301,28 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The names of the parent classes (ancestors).
*
* @var array
* @psalm-var list<class-string>
*/
public $parentClasses = [];
/**
* READ-ONLY: The names of all subclasses (descendants).
*
* @var array
* @psalm-var list<class-string>
*/
public $subClasses = [];
/**
* READ-ONLY: The names of all embedded classes based on properties.
*
* @var array
* @psalm-var array<string, mixed[]>
*/
public $embeddedClasses = [];
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
* @var array
* @psalm-var array<string, array<string, mixed>>
*/
public $namedQueries = [];
@@ -335,7 +339,7 @@ class ClassMetadataInfo implements ClassMetadata
* )
* </pre>
*
* @var array
* @psalm-var array<string, array<string, mixed>>
*/
public $namedNativeQueries = [];
@@ -351,7 +355,11 @@ class ClassMetadataInfo implements ClassMetadata
* )
* </pre>
*
* @var array
* @psalm-var array<string, array{
* name: string,
* entities: mixed[],
* columns: mixed[]
* }>
*/
public $sqlResultSetMappings = [];
@@ -359,7 +367,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
* of the mapped entity class.
*
* @var array
* @psalm-var list<string>
*/
public $identifier = [];
@@ -416,8 +424,23 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>'unique'</b> (string, optional, schema-only)
* Whether a unique constraint should be generated for the column.
*
* @var array
* @psalm-var array<string, array{type: string, fieldName: string, columnName: string, inherited: class-string}>
* @psalm-var array<string, array{
* type: string,
* fieldName: string,
* columnName?: string,
* length?: int,
* id?: bool,
* nullable?: bool,
* columnDefinition?: string,
* precision?: int,
* scale?: int,
* unique?: string,
* inherited?: class-string,
* originalClass?: class-string,
* originalField?: string,
* quoted?: bool,
* requireSQLConversion?: bool,
* }>
*/
public $fieldMappings = [];
@@ -425,7 +448,7 @@ class ClassMetadataInfo implements ClassMetadata
* 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.
*
* @var array
* @psalm-var array<string, string>
*/
public $fieldNames = [];
@@ -468,7 +491,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
* inheritance mappings.
*
* @var array
* @psalm-var array<string, mixed>
*/
public $discriminatorColumn;
@@ -481,21 +504,21 @@ class ClassMetadataInfo implements ClassMetadata
* indexes => array
* uniqueConstraints => array
*
* @var array
* @psalm-var array<string, mixed>
*/
public $table;
/**
* READ-ONLY: The registered lifecycle callbacks for entities of this class.
*
* @var array[]
* @psalm-var array<string, list<string>>
*/
public $lifecycleCallbacks = [];
/**
* READ-ONLY: The registered entity listeners.
*
* @var array
* @psalm-var array<string, list<array{class: class-string, method: string}>>
*/
public $entityListeners = [];
@@ -552,7 +575,7 @@ class ClassMetadataInfo implements ClassMetadata
* )
* </pre>
*
* @var array
* @psalm-var array<string, array<string, mixed>>
*/
public $associationMappings = [];
@@ -593,7 +616,7 @@ class ClassMetadataInfo implements ClassMetadata
* )
* </code>
*
* @var array
* @psalm-var array<string, mixed>
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public $sequenceGeneratorDefinition;
@@ -602,7 +625,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The definition of the table generator of this class. Only used for the
* TABLE generation strategy.
*
* @var array
* @var array<string, mixed>
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public $tableGeneratorDefinition;
@@ -685,7 +708,6 @@ class ClassMetadataInfo implements ClassMetadata
* Gets the ReflectionProperties of the mapped class.
*
* @return ReflectionProperty[]|null[] An array of ReflectionProperty instances.
*
* @psalm-return array<ReflectionProperty|null>
*/
public function getReflectionProperties()
@@ -729,7 +751,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param object $entity
*
* @return array
* @return array<string, mixed>
*/
public function getIdentifierValues($entity)
{
@@ -761,7 +783,7 @@ class ClassMetadataInfo implements ClassMetadata
* Populates the entity identifier of an entity.
*
* @param object $entity
* @param array $id
* @psalm-param array<string, mixed> $id
*
* @return void
*
@@ -1083,7 +1105,7 @@ class ClassMetadataInfo implements ClassMetadata
}
/**
* @param array $cache
* @psalm-param array{usage?: mixed, region?: mixed} $cache
*
* @return void
*/
@@ -1102,7 +1124,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* @param string $fieldName
* @param array $cache
* @psalm-param array{usage?: mixed, region?: mixed} $cache
*
* @return void
*/
@@ -1114,10 +1136,9 @@ class ClassMetadataInfo implements ClassMetadata
/**
* @param string $fieldName
* @param array $cache
* @psalm-param array{usage?: mixed, region?: mixed} $cache
*
* @return mixed[]
*
* @psalm-param array{usage: mixed, region: mixed} $cache
* @psalm-return array{usage: mixed, region: mixed}
*/
public function getAssociationCacheDefaults($fieldName, array $cache)
@@ -1244,7 +1265,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $fieldName The field name.
*
* @return array The field mapping.
* @psalm-return array<string, mixed> The field mapping.
*
* @throws MappingException
*/
@@ -1265,7 +1286,7 @@ class ClassMetadataInfo implements ClassMetadata
* @param string $fieldName The field name that represents the association in
* the object model.
*
* @return array The mapping.
* @psalm-return array<string, mixed> The mapping.
*
* @throws MappingException
*/
@@ -1281,7 +1302,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets all association mappings of the class.
*
* @return array
* @psalm-return array<string, array<string, mixed>>
*/
public function getAssociationMappings()
{
@@ -1324,7 +1345,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets all named queries of the class.
*
* @return array
* @psalm-return array<string, mixed>
*/
public function getNamedQueries()
{
@@ -1338,7 +1359,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $queryName The query name.
*
* @return array
* @psalm-return array<string, mixed>
*
* @throws MappingException
*/
@@ -1354,7 +1375,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets all named native queries of the class.
*
* @return array
* @psalm-return array<string, array<string, mixed>>
*/
public function getNamedNativeQueries()
{
@@ -1368,7 +1389,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $name The result set mapping name.
*
* @return array
* @psalm-return array<string, mixed>
*
* @throws MappingException
*/
@@ -1384,7 +1405,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets all sql result set mappings of the class.
*
* @return array
* @psalm-return array<string, array<string, mixed>>
*/
public function getSqlResultSetMappings()
{
@@ -1394,7 +1415,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Validates & completes the given field mapping.
*
* @param array $mapping The field mapping to validate & complete.
* @psalm-param array<string, mixed> $mapping The field mapping to validate & complete.
*
* @return void
*
@@ -1459,12 +1480,9 @@ class ClassMetadataInfo implements ClassMetadata
* 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).
*
* @param array $mapping The mapping.
* @psalm-param array<string, mixed> $mapping The mapping.
*
* @return mixed[] The updated mapping.
*
* @throws MappingException If something is wrong with the mapping.
*
* @psalm-return array{
* mappedBy: mixed,
* inversedBy: mixed,
@@ -1478,8 +1496,11 @@ class ClassMetadataInfo implements ClassMetadata
* isCascadePersist: bool,
* isCascadeRefresh: bool,
* isCascadeMerge: bool,
* isCascadeDetach: bool
* isCascadeDetach: bool,
* ?orphanRemoval: bool
* }
*
* @throws MappingException If something is wrong with the mapping.
*/
protected function _validateAndCompleteAssociationMapping(array $mapping)
{
@@ -1597,14 +1618,13 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Validates & completes a one-to-one association mapping.
*
* @param array $mapping The mapping to validate & complete.
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
*
* @return mixed[] The validated & completed mapping.
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool, isCascadeRemove: bool}
*
* @throws RuntimeException
* @throws MappingException
*
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool, isCascadeRemove: bool}
*/
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
@@ -1688,13 +1708,9 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Validates & completes a one-to-many association mapping.
*
* @param array $mapping The mapping to validate and complete.
* @psalm-param array<string, mixed> $mapping The mapping to validate and complete.
*
* @return mixed[] The validated and completed mapping.
*
* @throws MappingException
* @throws InvalidArgumentException
*
* @psalm-return array{
* mappedBy: mixed,
* inversedBy: mixed,
@@ -1711,6 +1727,9 @@ class ClassMetadataInfo implements ClassMetadata
* isCascadeDetach: bool,
* orphanRemoval: bool
* }
*
* @throws MappingException
* @throws InvalidArgumentException
*/
protected function _validateAndCompleteOneToManyMapping(array $mapping)
{
@@ -1732,13 +1751,12 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Validates & completes a many-to-many association mapping.
*
* @param array $mapping The mapping to validate & complete.
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
*
* @return mixed[] The validated & completed mapping.
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool}
*
* @throws InvalidArgumentException
*
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool}
*/
protected function _validateAndCompleteManyToManyMapping(array $mapping)
{
@@ -1884,7 +1902,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the mapped identifier/primary key fields of this class.
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
*
* @param array $identifier
* @psalm-param list<mixed> $identifier
*
* @return void
*/
@@ -1913,10 +1931,9 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets an array containing all the column names.
*
* @param array|null $fieldNames
* @psalm-param list<string>|null $fieldNames
*
* @return mixed[]
*
* @psalm-return list<string>
*/
public function getColumnNames(?array $fieldNames = null)
@@ -1931,7 +1948,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Returns an array with all the identifier column names.
*
* @return array
* @psalm-return list<string>
*/
public function getIdentifierColumnNames()
{
@@ -2135,7 +2152,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Sets the mapped subclasses of this class.
*
* @param array $subclasses The names of all mapped subclasses.
* @psalm-param list<string> $subclasses The names of all mapped subclasses.
*
* @return void
*/
@@ -2151,7 +2168,7 @@ class ClassMetadataInfo implements ClassMetadata
* Assumes that the class names in the passed array are in the order:
* directParent -> directParentParent -> directParentParentParent ... -> root.
*
* @param array $classNames
* @psalm-param list<class-string> $classNames
*
* @return void
*/
@@ -2175,7 +2192,7 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function setInheritanceType($type)
{
if (! $this->_isInheritanceType($type)) {
if (! $this->isInheritanceType($type)) {
throw MappingException::invalidInheritanceType($this->name, $type);
}
@@ -2186,7 +2203,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the association to override association mapping of property for an entity relationship.
*
* @param string $fieldName
* @param array $overrideMapping
* @psalm-param array<string, mixed> $overrideMapping
*
* @return void
*
@@ -2251,7 +2268,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the override for a mapped field.
*
* @param string $fieldName
* @param array $overrideMapping
* @psalm-param array<string, mixed> $overrideMapping
*
* @return void
*
@@ -2329,6 +2346,11 @@ class ClassMetadataInfo implements ClassMetadata
return isset($this->associationMappings[$fieldName]['inherited']);
}
/**
* @param string $fieldName
*
* @return bool
*/
public function isInheritedEmbeddedClass($fieldName)
{
return isset($this->embeddedClasses[$fieldName]['inherited']);
@@ -2358,7 +2380,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* If a key is omitted, the current value is kept.
*
* @param array $table The table description.
* @psalm-param array<string, mixed> $table The table description.
*
* @return void
*/
@@ -2406,7 +2428,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
*/
private function _isInheritanceType($type)
private function isInheritanceType($type)
{
return $type === self::INHERITANCE_TYPE_NONE ||
$type === self::INHERITANCE_TYPE_SINGLE_TABLE ||
@@ -2417,7 +2439,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a mapped field to the class.
*
* @param array $mapping The field mapping.
* @psalm-param array<string, mixed> $mapping The field mapping.
*
* @return void
*
@@ -2436,7 +2458,7 @@ class ClassMetadataInfo implements ClassMetadata
* Adds an association mapping without completing/validating it.
* This is mainly used to add inherited association mappings to derived classes.
*
* @param array $mapping
* @psalm-param array<string, mixed> $mapping
*
* @return void
*
@@ -2456,7 +2478,7 @@ class ClassMetadataInfo implements ClassMetadata
* Adds a field mapping without completing/validating it.
* This is mainly used to add inherited field mappings to derived classes.
*
* @param array $fieldMapping
* @psalm-param array<string, mixed> $fieldMapping
*
* @return void
*/
@@ -2471,7 +2493,7 @@ class ClassMetadataInfo implements ClassMetadata
* INTERNAL:
* Adds a named query to this class.
*
* @param array $queryMapping
* @psalm-param array<string, mixed> $queryMapping
*
* @return void
*
@@ -2506,7 +2528,7 @@ class ClassMetadataInfo implements ClassMetadata
* INTERNAL:
* Adds a named native query to this class.
*
* @param array $queryMapping
* @psalm-param array<string, mixed> $queryMapping
*
* @return void
*
@@ -2549,7 +2571,7 @@ class ClassMetadataInfo implements ClassMetadata
* INTERNAL:
* Adds a sql result set mapping to this class.
*
* @param array $resultMapping
* @psalm-param array<string, mixed> $resultMapping
*
* @return void
*
@@ -2607,7 +2629,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a one-to-one mapping.
*
* @param array $mapping The mapping.
* @param array<string, mixed> $mapping The mapping.
*
* @return void
*/
@@ -2623,7 +2645,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a one-to-many mapping.
*
* @param array $mapping The mapping.
* @psalm-param array<string, mixed> $mapping The mapping.
*
* @return void
*/
@@ -2639,7 +2661,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a many-to-one mapping.
*
* @param array $mapping The mapping.
* @psalm-param array<string, mixed> $mapping The mapping.
*
* @return void
*/
@@ -2656,7 +2678,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a many-to-many mapping.
*
* @param array $mapping The mapping.
* @psalm-param array<string, mixed> $mapping The mapping.
*
* @return void
*/
@@ -2672,7 +2694,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Stores the association mapping.
*
* @param array $assocMapping
* @psalm-param array<string, mixed> $assocMapping
*
* @return void
*
@@ -2691,10 +2713,9 @@ class ClassMetadataInfo implements ClassMetadata
* Registers a custom repository class for the entity class.
*
* @param string $repositoryClassName The class name of the custom mapper.
* @psalm-param class-string $repositoryClassName
*
* @return void
*
* @psalm-param class-string $repositoryClassName
*/
public function setCustomRepositoryClass($repositoryClassName)
{
@@ -2736,7 +2757,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $event
*
* @return array
* @psalm-return list<string>
*/
public function getLifecycleCallbacks($event)
{
@@ -2764,7 +2785,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the lifecycle callbacks for entities of this class.
* Any previously registered callbacks are overwritten.
*
* @param array $callbacks
* @psalm-param array<string, list<string>> $callbacks
*
* @return void
*/
@@ -2811,7 +2832,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @see getDiscriminatorColumn()
*
* @param array $columnDef
* @psalm-param array<string, mixed> $columnDef
*
* @return void
*
@@ -2848,7 +2869,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the discriminator values used by this class.
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
*
* @param array $map
* @psalm-param array<string, class-string> $map
*
* @return void
*/
@@ -2863,7 +2884,7 @@ class ClassMetadataInfo implements ClassMetadata
* Adds one entry of the discriminator map with a new class and corresponding name.
*
* @param string $name
* @param string $className
* @psalm-param class-string $className
*
* @return void
*
@@ -3047,7 +3068,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Sets definition.
*
* @param array $definition
* @psalm-param array<string, string> $definition
*
* @return void
*/
@@ -3069,7 +3090,7 @@ class ClassMetadataInfo implements ClassMetadata
* )
* </code>
*
* @param array $definition
* @psalm-param array<string, string> $definition
*
* @return void
*
@@ -3101,7 +3122,7 @@ class ClassMetadataInfo implements ClassMetadata
* Sets the version field mapping used for versioning. Sets the default
* value to use depending on the column type.
*
* @param array $mapping The version field mapping array.
* @psalm-param array<string, mixed> $mapping The version field mapping array.
*
* @return void
*
@@ -3203,7 +3224,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param AbstractPlatform $platform
*
* @return array
* @psalm-return list<string>
*/
public function getQuotedIdentifierColumnNames($platform)
{
@@ -3305,7 +3326,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* @param string $targetClass
*
* @return array
* @psalm-return array<string, array<string, mixed>>
*/
public function getAssociationsByTargetClass($targetClass)
{
@@ -3322,10 +3343,9 @@ class ClassMetadataInfo implements ClassMetadata
/**
* @param string|null $className
* @psalm-param ?class-string $className
*
* @return string|null null if the input value is null
*
* @psalm-param ?class-string $className
*/
public function fullyQualifiedClassName($className)
{
@@ -3357,7 +3377,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Map Embedded Class
*
* @param array $mapping
* @psalm-param array<string, mixed> $mapping
*
* @return void
*
@@ -3407,11 +3427,9 @@ class ClassMetadataInfo implements ClassMetadata
}
/**
* @param string $fieldName
*
* @throws MappingException
*/
private function assertFieldNotMapped($fieldName)
private function assertFieldNotMapped(string $fieldName): void
{
if (
isset($this->fieldMappings[$fieldName]) ||
@@ -3450,7 +3468,8 @@ class ClassMetadataInfo implements ClassMetadata
$sequencePrefix = $tableName;
// Prepend the schema name to the table name if there is one
if ($schemaName = $this->getSchemaName()) {
$schemaName = $this->getSchemaName();
if ($schemaName) {
$sequencePrefix = $schemaName . '.' . $tableName;
if (! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
@@ -3462,9 +3481,9 @@ class ClassMetadataInfo implements ClassMetadata
}
/**
* @param array $mapping
* @psalm-param array<string, mixed> $mapping
*/
private function assertMappingOrderBy(array $mapping)
private function assertMappingOrderBy(array $mapping): void
{
if (isset($mapping['orderBy']) && ! is_array($mapping['orderBy'])) {
throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));

View File

@@ -25,7 +25,6 @@ use Doctrine\ORM\Events;
use Doctrine\ORM\Id\TableGenerator;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
@@ -60,7 +59,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/** @var ClassMetadataInfo $metadata */
$class = $metadata->getReflectionClass();
if (! $class) {
@@ -299,7 +297,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
$mapping['fieldName'] = $property->getName();
// Evaluate @Cache annotation
if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class)) !== null) {
$cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class);
if ($cacheAnnot !== null) {
$mapping['cache'] = $metadata->getAssociationCacheDefaults(
$mapping['fieldName'],
[
@@ -312,7 +311,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
// Check for JoinColumn/JoinColumns annotations
$joinColumns = [];
if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class)) {
$joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class);
if ($joinColumnAnnot) {
$joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
} else {
$joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class);
@@ -325,18 +325,21 @@ class AnnotationDriver extends AbstractAnnotationDriver
// Field can only be annotated with one of:
// @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
if ($columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class)) {
$columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class);
if ($columnAnnot) {
if ($columnAnnot->type === null) {
throw MappingException::propertyTypeIsRequired($className, $property->getName());
}
$mapping = $this->columnToArray($property->getName(), $columnAnnot);
if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
$idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class);
if ($idAnnot) {
$mapping['id'] = true;
}
if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class)) {
$generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class);
if ($generatedValueAnnot) {
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
}
@@ -347,7 +350,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
$metadata->mapField($mapping);
// Check for SequenceGenerator/TableGenerator definition
if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class)) {
$seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class);
if ($seqGeneratorAnnot) {
$metadata->setSequenceGeneratorDefinition(
[
'sequenceName' => $seqGeneratorAnnot->sequenceName,
@@ -483,8 +487,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
}
/**
* @param mixed[] $mapping
* @param mixed[] $joinColumns
* @psalm-param array<string, mixed> $mapping
*/
private function loadRelationShipMapping(
ReflectionProperty $property,
@@ -660,7 +664,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
* Parse the given JoinColumn as array
*
* @return mixed[]
*
* @psalm-return array{
* name: string,
* unique: bool,
@@ -688,7 +691,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
* @param string $fieldName
*
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type: mixed,

View File

@@ -60,7 +60,7 @@ class DatabaseDriver implements MappingDriver
/** @var mixed[] */
private $classToTableNames = [];
/** @var mixed[] */
/** @psalm-var array<string, Table> */
private $manyToManyTables = [];
/** @var mixed[] */
@@ -145,8 +145,8 @@ class DatabaseDriver implements MappingDriver
/**
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
*
* @param array $entityTables
* @param array $manyToManyTables
* @psalm-param list<Table> $entityTables
* @psalm-param list<Table> $manyToManyTables
*
* @return void
*/
@@ -384,8 +384,6 @@ class DatabaseDriver implements MappingDriver
*
* @param string $tableName
*
* @return array
*
* @psalm-return array{
* fieldName: string,
* columnName: string,
@@ -439,12 +437,14 @@ class DatabaseDriver implements MappingDriver
}
// Comment
if (($comment = $column->getComment()) !== null) {
$comment = $column->getComment();
if ($comment !== null) {
$fieldMapping['options']['comment'] = $comment;
}
// Default
if (($default = $column->getDefault()) !== null) {
$default = $column->getDefault();
if ($default !== null) {
$fieldMapping['options']['default'] = $default;
}
@@ -498,7 +498,6 @@ class DatabaseDriver implements MappingDriver
* Retrieve schema table definition foreign keys.
*
* @return ForeignKeyConstraint[]
*
* @psalm-return array<string, ForeignKeyConstraint>
*/
private function getTableForeignKeys(Table $table)

View File

@@ -23,7 +23,6 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
@@ -63,7 +62,6 @@ class XmlDriver extends FileDriver
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/** @var ClassMetadataInfo $metadata */
$xmlRoot = $this->getElement($className);
assert($xmlRoot instanceof SimpleXMLElement);
@@ -221,7 +219,7 @@ class XmlDriver extends FileDriver
}
if (isset($indexXml->options)) {
$index['options'] = $this->_parseOptions($indexXml->options->children());
$index['options'] = $this->parseOptions($indexXml->options->children());
}
if (isset($indexXml['name'])) {
@@ -239,7 +237,7 @@ class XmlDriver extends FileDriver
$unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
if (isset($uniqueXml->options)) {
$unique['options'] = $this->_parseOptions($uniqueXml->options->children());
$unique['options'] = $this->parseOptions($uniqueXml->options->children());
}
if (isset($uniqueXml['name'])) {
@@ -251,7 +249,7 @@ class XmlDriver extends FileDriver
}
if (isset($xmlRoot->options)) {
$metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
$metadata->table['options'] = $this->parseOptions($xmlRoot->options->children());
}
// The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions
@@ -329,7 +327,7 @@ class XmlDriver extends FileDriver
}
if (isset($idElement->options)) {
$mapping['options'] = $this->_parseOptions($idElement->options->children());
$mapping['options'] = $this->parseOptions($idElement->options->children());
}
$metadata->mapField($mapping);
@@ -400,7 +398,7 @@ class XmlDriver extends FileDriver
}
if (isset($oneToOneElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
$mapping['cascade'] = $this->getCascadeMappings($oneToOneElement->cascade);
}
if (isset($oneToOneElement['orphan-removal'])) {
@@ -430,7 +428,7 @@ class XmlDriver extends FileDriver
}
if (isset($oneToManyElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
$mapping['cascade'] = $this->getCascadeMappings($oneToManyElement->cascade);
}
if (isset($oneToManyElement['orphan-removal'])) {
@@ -496,7 +494,7 @@ class XmlDriver extends FileDriver
$mapping['joinColumns'] = $joinColumns;
if (isset($manyToOneElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
$mapping['cascade'] = $this->getCascadeMappings($manyToOneElement->cascade);
}
// Evaluate second level cache
@@ -552,7 +550,7 @@ class XmlDriver extends FileDriver
}
if (isset($manyToManyElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
$mapping['cascade'] = $this->getCascadeMappings($manyToManyElement->cascade);
}
if (isset($manyToManyElement->{'order-by'})) {
@@ -683,13 +681,13 @@ class XmlDriver extends FileDriver
*
* @return mixed[] The options array.
*/
private function _parseOptions(SimpleXMLElement $options)
private function parseOptions(SimpleXMLElement $options)
{
$array = [];
foreach ($options as $option) {
if ($option->count()) {
$value = $this->_parseOptions($option->children());
$value = $this->parseOptions($option->children());
} else {
$value = (string) $option;
}
@@ -716,7 +714,6 @@ class XmlDriver extends FileDriver
* @param SimpleXMLElement $joinColumnElement The XML element.
*
* @return mixed[] The mapping array.
*
* @psalm-return array{
* name: string,
* referencedColumnName: string,
@@ -756,7 +753,6 @@ class XmlDriver extends FileDriver
* Parses the given field as array.
*
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type?: string,
@@ -814,7 +810,7 @@ class XmlDriver extends FileDriver
}
if (isset($fieldMapping->options)) {
$mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
$mapping['options'] = $this->parseOptions($fieldMapping->options->children());
}
return $mapping;
@@ -824,7 +820,6 @@ class XmlDriver extends FileDriver
* Parse / Normalize the cache configuration
*
* @return mixed[]
*
* @psalm-return array{usage: mixed, region: string|null}
*/
private function cacheToArray(SimpleXMLElement $cacheMapping)
@@ -852,10 +847,9 @@ class XmlDriver extends FileDriver
* @param SimpleXMLElement $cascadeElement The cascade element.
*
* @return string[] The list of cascade options.
*
* @psalm-return list<string>
*/
private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
private function getCascadeMappings(SimpleXMLElement $cascadeElement)
{
$cascades = [];
foreach ($cascadeElement->children() as $action) {
@@ -881,16 +875,19 @@ class XmlDriver extends FileDriver
if (isset($xmlElement->entity)) {
foreach ($xmlElement->entity as $entityElement) {
/** @psalm-var class-string */
$entityName = (string) $entityElement['name'];
$result[$entityName] = $entityElement;
}
} elseif (isset($xmlElement->{'mapped-superclass'})) {
foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
/** @psalm-var class-string */
$className = (string) $mappedSuperClass['name'];
$result[$className] = $mappedSuperClass;
}
} elseif (isset($xmlElement->embeddable)) {
foreach ($xmlElement->embeddable as $embeddableElement) {
/** @psalm-var class-string */
$embeddableName = (string) $embeddableElement['name'];
$result[$embeddableName] = $embeddableElement;
}

View File

@@ -22,7 +22,6 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
@@ -71,7 +70,6 @@ class YamlDriver extends FileDriver
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/** @var ClassMetadataInfo $metadata */
$element = $this->getElement($className);
if ($element['type'] === 'entity') {
@@ -691,10 +689,17 @@ class YamlDriver extends FileDriver
* Constructs a joinColumn mapping array based on the information
* found in the given join column element.
*
* @param array $joinColumnElement The array join column element.
* @psalm-param array{
* referencedColumnName?: mixed,
* name?: mixed,
* fieldName?: mixed,
* unique?: mixed,
* nullable?: mixed,
* onDelete?: mixed,
* columnDefinition?: mixed
* } $joinColumnElement The array join column element.
*
* @return mixed[] The mapping array.
*
* @psalm-return array{
* referencedColumnName?: string,
* name?: string,
@@ -705,7 +710,7 @@ class YamlDriver extends FileDriver
* columnDefinition?: mixed
* }
*/
private function joinColumnToArray($joinColumnElement)
private function joinColumnToArray(array $joinColumnElement): array
{
$joinColumn = [];
if (isset($joinColumnElement['referencedColumnName'])) {
@@ -742,11 +747,19 @@ class YamlDriver extends FileDriver
/**
* Parses the given column as array.
*
* @param string $fieldName
* @param array $column
* @psalm-param array{
* type?: string,
* column?: string,
* precision?: mixed,
* scale?: mixed,
* unique?: mixed,
* options?: mixed,
* nullable?: mixed,
* version?: mixed,
* columnDefinition?: mixed
* }|null $column
*
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type?: string,
@@ -761,7 +774,7 @@ class YamlDriver extends FileDriver
* columnDefinition?: mixed
* }
*/
private function columnToArray($fieldName, $column)
private function columnToArray(string $fieldName, ?array $column): array
{
$mapping = ['fieldName' => $fieldName];
@@ -819,10 +832,9 @@ class YamlDriver extends FileDriver
* Parse / Normalize the cache configuration
*
* @param mixed[] $cacheMapping
* @psalm-param array{usage: mixed, region: (string|null)} $cacheMapping
*
* @return mixed[]
*
* @psalm-param array{usage: mixed, region: (string|null)} $cacheMapping
* @psalm-return array{usage: mixed, region: (string|null)}
*/
private function cacheToArray($cacheMapping)

View File

@@ -32,7 +32,7 @@ interface EntityListenerResolver
*
* @return void
*/
function clear($className = null);
public function clear($className = null);
/**
* Returns a entity listener instance for the given identifier.
@@ -41,12 +41,12 @@ interface EntityListenerResolver
*
* @return object An entity listener
*/
function resolve($className);
public function resolve($className);
/**
* Register a entity listener instance.
*
* @param object $object An entity listener
*/
function register($object);
public function register($object);
}

View File

@@ -34,7 +34,7 @@ interface NamingStrategy
*
* @return string A table name.
*/
function classToTableName($className);
public function classToTableName($className);
/**
* Returns a column name for a property.
@@ -44,7 +44,7 @@ interface NamingStrategy
*
* @return string A column name.
*/
function propertyToColumnName($propertyName, $className = null);
public function propertyToColumnName($propertyName, $className = null);
/**
* Returns a column name for an embedded property.
@@ -56,14 +56,14 @@ interface NamingStrategy
*
* @return string
*/
function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null);
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null);
/**
* Returns the default reference column name.
*
* @return string A column name.
*/
function referenceColumnName();
public function referenceColumnName();
/**
* Returns a join column name for a property.
@@ -72,7 +72,7 @@ interface NamingStrategy
*
* @return string A join column name.
*/
function joinColumnName($propertyName);
public function joinColumnName($propertyName);
/**
* Returns a join table name.
@@ -83,7 +83,7 @@ interface NamingStrategy
*
* @return string A join table name.
*/
function joinTableName($sourceEntity, $targetEntity, $propertyName = null);
public function joinTableName($sourceEntity, $targetEntity, $propertyName = null);
/**
* Returns the foreign key column name for the given parameters.
@@ -93,5 +93,5 @@ interface NamingStrategy
*
* @return string A join column name.
*/
function joinKeyColumnName($entityName, $referencedColumnName = null);
public function joinKeyColumnName($entityName, $referencedColumnName = null);
}

View File

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

View File

@@ -51,10 +51,9 @@ final class ReflectionPropertiesGetter
/**
* @param string $className
* @psalm-param class-string $className
*
* @return ReflectionProperty[] indexed by property internal name
*
* @psalm-param class-string $className
*/
public function getProperties($className)
{
@@ -77,9 +76,9 @@ final class ReflectionPropertiesGetter
/**
* @param string $className
* @psalm-param class-string $className
*
* @return ReflectionClass[]
*
* @psalm-return list<ReflectionClass>
*/
private function getHierarchyClasses($className): array
@@ -91,7 +90,8 @@ final class ReflectionPropertiesGetter
$classes[] = $currentClass;
$parentClassName = null;
if ($parentClass = $currentClass->getParentClass()) {
$parentClass = $currentClass->getParentClass();
if ($parentClass) {
$parentClassName = $parentClass->getName();
}
}
@@ -103,7 +103,6 @@ final class ReflectionPropertiesGetter
/**
* @return ReflectionProperty[]
*
* @psalm-return array<string, ReflectionProperty>
*/
private function getClassProperties(ReflectionClass $reflectionClass): array

View File

@@ -122,8 +122,8 @@ class ORMInvalidArgumentException extends InvalidArgumentException
}
/**
* @param array $associationMapping
* @param object $entry
* @psalm-param array<string, string> $associationMapping
*
* @return ORMInvalidArgumentException
*/
@@ -133,8 +133,8 @@ class ORMInvalidArgumentException extends InvalidArgumentException
}
/**
* @param array $assoc
* @param object $entry
* @psalm-param array<string, string> $assoc
*
* @return ORMInvalidArgumentException
*/
@@ -252,19 +252,16 @@ class ORMInvalidArgumentException extends InvalidArgumentException
/**
* Helper method to show an object as string.
*
* @param object $obj
*/
private static function objToStr($obj): string
private static function objToStr(object $obj): string
{
return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj);
}
/**
* @param array $associationMapping
* @param object $entity
* @psalm-param array<string,string> $associationMapping
*/
private static function newEntityFoundThroughRelationshipMessage(array $associationMapping, $entity): string
private static function newEntityFoundThroughRelationshipMessage(array $associationMapping, object $entity): string
{
return 'A new entity was found through the relationship \''
. $associationMapping['sourceEntity'] . '#' . $associationMapping['fieldName'] . '\' that was not'

View File

@@ -20,7 +20,7 @@
namespace Doctrine\ORM;
use DateTime;
use DateTimeInterface;
/**
* An OptimisticLockException is thrown when a version check on an object
@@ -70,8 +70,8 @@ class OptimisticLockException extends ORMException
*/
public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion)
{
$expectedLockVersion = $expectedLockVersion instanceof DateTime ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
$actualLockVersion = $actualLockVersion instanceof DateTime ? $actualLockVersion->getTimestamp() : $actualLockVersion;
$expectedLockVersion = $expectedLockVersion instanceof DateTimeInterface ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
$actualLockVersion = $actualLockVersion instanceof DateTimeInterface ? $actualLockVersion->getTimestamp() : $actualLockVersion;
return new self('The optimistic lock failed, version ' . $expectedLockVersion . ' was expected, but is actually ' . $actualLockVersion, $entity);
}

View File

@@ -45,6 +45,11 @@ use function spl_object_hash;
* entities from the collection, only the links in the relation table are removed (on flush).
* 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.
*
* @phpstan-template TKey
* @psalm-template TKey of array-key
* @psalm-template T
* @template-implements Collection<TKey,T>
*/
final class PersistentCollection extends AbstractLazyCollection implements Selectable
{
@@ -52,7 +57,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.
*
* @var array
* @psalm-var array<string|int, mixed>
*/
private $snapshot = [];
@@ -67,7 +72,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* The association mapping the collection belongs to.
* This is currently either a OneToManyMapping or a ManyToManyMapping.
*
* @var array
* @psalm-var array<string, mixed>
*/
private $association;
@@ -104,9 +109,9 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
/**
* Creates a new persistent collection.
*
* @param EntityManagerInterface $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param Collection $collection The collection elements.
* @param EntityManagerInterface $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @psalm-param Collection<TKey, T> $collection The collection elements.
*/
public function __construct(EntityManagerInterface $em, $class, Collection $collection)
{
@@ -122,7 +127,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* describes the association between the owner and the elements of the collection.
*
* @param object $entity
* @param array $assoc
* @psalm-param array<string, mixed> $assoc
*
* @return void
*/
@@ -239,7 +244,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* INTERNAL:
* Returns the last snapshot of the elements in the collection.
*
* @return array The last snapshot of the elements.
* @psalm-return array<string|int, mixed> The last snapshot of the elements.
*/
public function getSnapshot()
{
@@ -250,14 +255,14 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* INTERNAL:
* getDeleteDiff
*
* @return array
* @return mixed[]
*/
public function getDeleteDiff()
{
return array_udiff_assoc(
$this->snapshot,
$this->collection->toArray(),
static function ($a, $b) {
static function ($a, $b): int {
return $a === $b ? 0 : 1;
}
);
@@ -267,14 +272,14 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* INTERNAL:
* getInsertDiff
*
* @return array
* @return mixed[]
*/
public function getInsertDiff()
{
return array_udiff_assoc(
$this->collection->toArray(),
$this->snapshot,
static function ($a, $b) {
static function ($a, $b): int {
return $a === $b ? 0 : 1;
}
);
@@ -283,7 +288,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
/**
* INTERNAL: Gets the association mapping of the collection.
*
* @return array
* @psalm-return array<string, mixed>
*/
public function getMapping()
{
@@ -598,7 +603,6 @@ 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}
*/
public function __sleep(): array
@@ -616,7 +620,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* @param int $offset
* @param int|null $length
*
* @return array
* @psalm-return array<TKey,T>
*/
public function slice($offset, $length = null)
{
@@ -660,7 +664,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.
*
* @return Collection
* @return Collection<TKey, T>
*
* @throws RuntimeException
*/
@@ -699,7 +703,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
/**
* Retrieves the wrapped Collection instance.
*
* @return Collection
* @return Collection<TKey, T>
*/
public function unwrap()
{

View File

@@ -57,7 +57,7 @@ interface CollectionPersister
* @param int $offset
* @param int $length
*
* @return array
* @return mixed[]
*/
public function slice(PersistentCollection $collection, $offset, $length = null);
@@ -91,7 +91,7 @@ interface CollectionPersister
/**
* Loads association entities matching the given Criteria object.
*
* @return array
* @return mixed[]
*/
public function loadCriteria(PersistentCollection $collection, Criteria $criteria);
}

View File

@@ -298,12 +298,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
* have to join in the actual entities table leading to additional
* JOIN.
*
* @param array $mapping Array containing mapping information.
* @psalm-param array<string, mixed> $mapping Array containing mapping information.
*
* @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}
*/
public function getFilterSql($mapping)
@@ -337,7 +336,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
$filterClauses = [];
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);
if ($filterExpr) {
$filterClauses[] = '(' . $filterExpr . ')';
}
}
@@ -350,10 +350,9 @@ class ManyToManyPersister extends AbstractCollectionPersister
/**
* Generate ON condition
*
* @param array $mapping
* @psalm-param array<string, mixed> $mapping
*
* @return string[]
*
* @psalm-return list<string>
*/
protected function getOnConditionSQL($mapping)
@@ -373,7 +372,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
$refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
$conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName;
$conditions[] = ' t.' . $joinColumnName . ' = te.' . $refColumnName;
}
return $conditions;
@@ -430,7 +429,6 @@ 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>}
*/
protected function getDeleteRowSQL(PersistentCollection $collection)
@@ -466,8 +464,6 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @param mixed $element
*
* @return array
*
* @psalm-return list<mixed>
*/
protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
@@ -480,7 +476,6 @@ 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>}
*/
protected function getInsertRowSQL(PersistentCollection $collection)
@@ -518,8 +513,6 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @param mixed $element
*
* @return array
*
* @psalm-return list<mixed>
*/
protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
@@ -534,7 +527,6 @@ class ManyToManyPersister extends AbstractCollectionPersister
* @param object $element
*
* @return mixed[]
*
* @psalm-return list<mixed>
*/
private function collectJoinTableColumnParameters(PersistentCollection $collection, $element)
@@ -577,12 +569,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
* @param string $key
* @param bool $addFilters Whether the filter SQL should be included or not.
*
* @return array ordered vector:
* @return mixed[] ordered vector:
* - quoted join table name
* - 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>}
*/
private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
@@ -663,12 +654,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
* @param object $element
* @param bool $addFilters Whether the filter SQL should be included or not.
*
* @return array ordered vector:
* @return mixed[] ordered vector:
* - quoted join table name
* - 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>}
*/
private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
@@ -730,7 +720,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
* Expands Criteria Parameters by walking the expressions and grabbing all
* parameters and types from it.
*
* @return array
* @return mixed[][]
*/
private function expandCriteriaParameters(Criteria $criteria)
{

View File

@@ -24,9 +24,9 @@ use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\ResultStatement as DriverStatement;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Statement;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
@@ -145,7 +145,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Queued inserts.
*
* @var array
* @psalm-var array<string, object>
*/
protected $queuedInserts = [];
@@ -313,8 +313,8 @@ class BasicEntityPersister implements EntityPersister
* by the preceding INSERT statement and assigns it back in to the
* entities version field.
*
* @param object $entity
* @param array $id
* @param object $entity
* @param mixed[] $id
*
* @return void
*/
@@ -359,8 +359,9 @@ class BasicEntityPersister implements EntityPersister
}
/**
* @return int[]|null[]|string[]
* @param mixed[] $id
*
* @return int[]|null[]|string[]
* @psalm-return list<(int|string|null)>
*/
private function extractIdentifierTypes(array $id, ClassMetadata $versionedClass): array
@@ -398,7 +399,7 @@ class BasicEntityPersister implements EntityPersister
$this->updateTable($entity, $quotedTableName, $data, $isVersioned);
if ($isVersioned) {
$id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
$id = $this->class->getIdentifierValues($entity);
$this->assignDefaultVersionValue($entity, $id);
}
@@ -609,7 +610,6 @@ class BasicEntityPersister implements EntityPersister
* @param object $entity The entity for which to prepare the data.
*
* @return mixed[][] The prepared data.
*
* @psalm-return array<string, array<array-key, mixed|null>>
*/
protected function prepareUpdateData($entity)
@@ -701,7 +701,6 @@ 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[]>
*/
protected function prepareInsertData($entity)
@@ -920,8 +919,8 @@ class BasicEntityPersister implements EntityPersister
/**
* Loads an array of entities from a given DBAL statement.
*
* @param mixed[] $assoc
* @param Statement $stmt
* @param mixed[] $assoc
* @param DriverStatement $stmt
*
* @return mixed[]
*/
@@ -942,7 +941,7 @@ class BasicEntityPersister implements EntityPersister
* Hydrates a collection from a given DBAL statement.
*
* @param mixed[] $assoc
* @param Statement $stmt
* @param DriverStatement $stmt
* @param PersistentCollection $coll
*
* @return mixed[]
@@ -974,17 +973,18 @@ class BasicEntityPersister implements EntityPersister
}
/**
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @psalm-param array<string, mixed> $assoc
*
* @return \Doctrine\DBAL\Driver\Statement
* @return DriverStatement
*
* @throws MappingException
*/
private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
private function getManyToManyStatement(
array $assoc,
object $sourceEntity,
?int $offset = null,
?int $limit = null
) {
$this->switchPersisterContext($offset, $limit);
$sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']);
@@ -1137,12 +1137,11 @@ class BasicEntityPersister implements EntityPersister
/**
* Gets the ORDER BY SQL snippet for ordered collections.
*
* @param array $orderBy
* @param string $baseTableAlias
* @psalm-param array<string, string> $orderBy
*
* @throws ORMException
*/
final protected function getOrderBySQL(array $orderBy, $baseTableAlias): string
final protected function getOrderBySQL(array $orderBy, string $baseTableAlias): string
{
$orderByList = [];
@@ -1286,7 +1285,8 @@ class BasicEntityPersister implements EntityPersister
}
// Add filter SQL
if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) {
$filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias);
if ($filterSql) {
$joinCondition[] = $filterSql;
}
} else {
@@ -1347,7 +1347,7 @@ class BasicEntityPersister implements EntityPersister
* Gets the SQL join fragment used when selecting entities from a
* many-to-many association.
*
* @param array $manyToMany
* @psalm-param array<string, mixed> $manyToMany
*
* @return string
*/
@@ -1428,7 +1428,6 @@ class BasicEntityPersister implements EntityPersister
* columns placed in the INSERT statements used by the persister.
*
* @return string[] The list of columns.
*
* @psalm-return list<string>
*/
protected function getInsertColumnList()
@@ -1657,14 +1656,13 @@ class BasicEntityPersister implements EntityPersister
/**
* Builds the left-hand-side of a where condition statement.
*
* @param string $field
* @param array|null $assoc
* @param string $field
* @psalm-param array<string, mixed>|null $assoc
*
* @return string[]
* @psalm-return list<string>
*
* @throws ORMException
*
* @psalm-return list<string>
*/
private function getSelectConditionStatementColumnSQL($field, $assoc = null)
{
@@ -1726,8 +1724,8 @@ 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.
*
* @param array $criteria
* @param array|null $assoc
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, mixed>|null $assoc
*
* @return string
*/
@@ -1767,12 +1765,12 @@ class BasicEntityPersister implements EntityPersister
/**
* Builds criteria and execute SQL statement to fetch the one to many entities from.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @psalm-param array<string, mixed> $assoc
*
* @return Statement
* @return DriverStatement
*/
private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
@@ -1851,7 +1849,6 @@ class BasicEntityPersister implements EntityPersister
* - class to which the field belongs to
*
* @return mixed[][]
*
* @psalm-return array{0: array, 1: list<mixed>}
*/
private function expandToManyParameters($criteria)
@@ -1878,10 +1875,9 @@ class BasicEntityPersister implements EntityPersister
* @param mixed $value
*
* @return int[]|null[]|string[]
* @psalm-return list<(int|string|null)>
*
* @throws QueryException
*
* @psalm-return list<(int|string|null)>
*/
private function getTypes($field, $value, ClassMetadata $class)
{
@@ -1932,7 +1928,7 @@ class BasicEntityPersister implements EntityPersister
*
* @param mixed $value
*
* @return array
* @return array{mixed}
*/
private function getValues($value)
{
@@ -2005,7 +2001,8 @@ class BasicEntityPersister implements EntityPersister
$types = array_merge($types, $criteriaTypes);
}
if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) {
$filterSql = $this->generateFilterConditionSQL($this->class, $alias);
if ($filterSql) {
$sql .= ' AND ' . $filterSql;
}
@@ -2015,7 +2012,7 @@ class BasicEntityPersister implements EntityPersister
/**
* Generates the appropriate join SQL for the given join column.
*
* @param array $joinColumns The join columns definition of an association.
* @psalm-param array<array<string, mixed>> $joinColumns The join columns definition of an association.
*
* @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
*/
@@ -2054,7 +2051,8 @@ class BasicEntityPersister implements EntityPersister
$filterClauses = [];
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);
if ($filterExpr !== '') {
$filterClauses[] = '(' . $filterExpr . ')';
}
}

View File

@@ -74,7 +74,7 @@ class CachedPersisterContext
/**
* Map from class names (FQCN) to the corresponding generated SQL table aliases.
*
* @var array
* @var array<class-string, string>
*/
public $sqlTableAliases = [];

View File

@@ -47,7 +47,7 @@ interface EntityPersister
/**
* Get all queued inserts.
*
* @return array
* @psalm-return array<string, object>
*/
public function getInserts();
@@ -89,14 +89,14 @@ interface EntityPersister
*
* @param string[] $criteria
*
* @return array
* @psalm-return array{list<mixed>, list<int|string|null>}
*/
public function expandParameters($criteria);
/**
* Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it.
*
* @return array
* @psalm-return array{list<mixed>, list<int|string|null>}
*/
public function expandCriteriaParameters(Criteria $criteria);
@@ -105,8 +105,8 @@ interface EntityPersister
*
* @param string $field
* @param mixed $value
* @param array|null $assoc
* @param string|null $comparison
* @psalm-param array<string, mixed>|null $assoc
*
* @return string
*/
@@ -128,8 +128,12 @@ interface EntityPersister
*
* If no inserts are queued, invoking this method is a NOOP.
*
* @return array An array of any generated post-insert IDs. This will be an empty array
* if the entity class does not use the IDENTITY generation strategy.
* @psalm-return list<array{
* generatedId: int,
* entity: object
* }> An array of any generated post-insert IDs. This will be
* an empty array if the entity class does not use the
* IDENTITY generation strategy.
*/
public function executeInserts();
@@ -182,29 +186,41 @@ interface EntityPersister
/**
* Loads an entity by a list of field criteria.
*
* @param array $criteria The criteria by which to load the entity.
* @param object|null $entity The entity to load the data into. If not specified, a new entity is created.
* @param array|null $assoc The association that connects the entity to load to another entity, if any.
* @param array $hints Hints for entity creation.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* for loading the entity.
* @param int|null $limit Limit number of results.
* @param array|null $orderBy Criteria to order by.
* @psalm-param array<string, mixed> $hints Hints for entity creation.
* @psalm-param array<string, mixed> $criteria The criteria by which
* to load the entity.
* @psalm-param array<string, mixed>|null $assoc The association that
* connects the entity to
* load to another entity,
* if any.
* @psalm-param array<string, string>|null $orderBy Criteria to order by.
*
* @return object|null The loaded and managed entity instance or NULL if the entity can not be found.
*
* @todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, ?array $orderBy = null);
public function load(
array $criteria,
$entity = null,
$assoc = null,
array $hints = [],
$lockMode = null,
$limit = null,
?array $orderBy = null
);
/**
* Loads an entity by identifier.
*
* @param array $identifier The entity identifier.
* @param object|null $entity The entity to load the data into. If not specified, a new entity is created.
* @param object|null $entity The entity to load the data into. If not specified, a new entity is created.
* @psalm-param array<string, mixed> $identifier The entity identifier.
*
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
* @return object|null The loaded and managed entity instance or NULL if the entity can not be found.
*
* @todo Check parameters
*/
@@ -214,11 +230,11 @@ interface EntityPersister
* Loads an entity of this persister's mapped class as part of a single-valued
* association from another entity.
*
* @param array $assoc The association to load.
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
* @param array $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @psalm-param array<string, mixed> $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @psalm-param array<string, mixed> $assoc The association to load.
*
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
*
@@ -229,12 +245,13 @@ interface EntityPersister
/**
* Refreshes a managed entity.
*
* @param array $id The identifier of the entity as an associative array from
* column or field names to values.
* @param object $entity The entity to refresh.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* for refreshing the managed entity.
* @psalm-param array<string, mixed> $id The identifier of the entity as an
* associative array from column or
* field names to values.
*
* @return void
*/
@@ -243,61 +260,59 @@ interface EntityPersister
/**
* Loads Entities matching the given Criteria object.
*
* @return array
* @return mixed[]
*/
public function loadCriteria(Criteria $criteria);
/**
* Loads a list of entities by a list of field criteria.
*
* @param array $criteria
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
*
* @return array
* @param int|null $limit
* @param int|null $offset
* @psalm-param array<string, string>|null $orderBy
* @psalm-param array<string, mixed> $criteria
*/
public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null);
/**
* Gets (sliced or full) elements of the given collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @psalm-param array<string, mixed> $assoc
*
* @return array
* @return mixed[]
*/
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null);
/**
* Loads a collection of entities of a many-to-many association.
*
* @param array $assoc The association mapping of the association being loaded.
* @param object $sourceEntity The entity that owns the collection.
* @param PersistentCollection $collection The collection to fill.
* @psalm-param array<string, mixed> $assoc The association mapping of the association being loaded.
*
* @return array
* @return mixed[]
*/
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection);
/**
* Loads a collection of entities in a one-to-many association.
*
* @param array $assoc
* @param object $sourceEntity
* @param PersistentCollection $collection The collection to load/fill.
* @psalm-param array<string, mixed> $assoc
*
* @return array
* @return mixed
*/
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection);
/**
* Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode.
*
* @param array $criteria
* @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants.
* @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants.
* @psalm-param array<string, mixed> $criteria
*
* @return void
*/
@@ -306,12 +321,12 @@ interface EntityPersister
/**
* Returns an array with (sliced or full list) of elements in the specified collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @psalm-param array<string, mixed> $assoc
*
* @return array
* @return mixed[]
*/
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null);

View File

@@ -22,7 +22,6 @@ namespace Doctrine\ORM\Persisters\Entity;
use Doctrine\Common\Collections\Criteria;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Statement;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Utility\PersisterHelper;
@@ -43,14 +42,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
* Map that maps column names to the table names that own them.
* This is mainly a temporary cache, used during a single request.
*
* @var array
* @psalm-var array<string, string>
*/
private $owningTableMap = [];
/**
* Map of table to quoted table names.
*
* @var array
* @psalm-var array<string, string>
*/
private $quotedTableMap = [];
@@ -192,7 +191,6 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Execute inserts on subtables.
// The order doesn't matter because all child tables link to the root table via FK.
foreach ($subTableStmts as $tableName => $stmt) {
/** @var Statement $stmt */
$paramIndex = 1;
$data = $insertData[$tableName] ?? [];
@@ -234,7 +232,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
return;
}
if (($isVersioned = $this->class->isVersioned) === false) {
$isVersioned = $this->class->isVersioned;
if ($isVersioned === false) {
return;
}
@@ -319,8 +318,12 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
? $this->getSelectConditionCriteriaSQL($criteria)
: $this->getSelectConditionSQL($criteria, $assoc);
$filterSql = $this->generateFilterConditionSQL(
$this->em->getClassMetadata($this->class->rootEntityName),
$this->getSQLTableAlias($this->class->rootEntityName)
);
// If the current class in the root entity, add the filters
if ($filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName))) {
if ($filterSql) {
$conditionSql .= $conditionSql
? ' AND ' . $filterSql
: $filterSql;

View File

@@ -83,7 +83,6 @@ class SqlValueVisitor extends ExpressionVisitor
* Returns the Parameters and Types necessary for matching the last visited expression.
*
* @return mixed[][]
*
* @psalm-return array{0: array, 1: array}
*/
public function getParamsAndTypes()
@@ -103,13 +102,13 @@ class SqlValueVisitor extends ExpressionVisitor
switch ($comparison->getOperator()) {
case Comparison::CONTAINS:
return "%{$value}%";
return '%' . $value . '%';
case Comparison::STARTS_WITH:
return "{$value}%";
return $value . '%';
case Comparison::ENDS_WITH:
return "%{$value}";
return '%' . $value;
default:
return $value;

View File

@@ -28,7 +28,6 @@ use Doctrine\Common\Proxy\ProxyGenerator;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityNotFoundException;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Utility\IdentifierFlattener;
@@ -85,7 +84,6 @@ class ProxyFactory extends AbstractProxyFactory
*/
protected function skipClass(ClassMetadata $metadata)
{
/** @var ClassMetadataInfo $metadata */
return $metadata->isMappedSuperclass
|| $metadata->isEmbeddedClass
|| $metadata->getReflectionClass()->isAbstract();
@@ -112,10 +110,9 @@ class ProxyFactory extends AbstractProxyFactory
* Creates a closure capable of initializing a proxy
*
* @return Closure
* @psalm-return Closure(BaseProxy ): void
*
* @throws EntityNotFoundException
*
* @psalm-return Closure(BaseProxy ): void
*/
private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister)
{
@@ -165,10 +162,9 @@ class ProxyFactory extends AbstractProxyFactory
* Creates a closure capable of finalizing state a cloned proxy
*
* @return Closure
* @psalm-return Closure(BaseProxy ): void
*
* @throws EntityNotFoundException
*
* @psalm-return Closure(BaseProxy ): void
*/
private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister)
{

View File

@@ -258,7 +258,7 @@ final class Query extends AbstractQuery
return $this->parserResult;
}
$hash = $this->_getQueryCacheId();
$hash = $this->getQueryCacheId();
$cached = $this->expireQueryCache ? false : $queryCache->fetch($hash);
if ($cached instanceof ParserResult) {
@@ -374,10 +374,9 @@ final class Query extends AbstractQuery
* @param Parameter[] $paramMappings
*
* @return mixed[][]
* @psalm-return array{0: list<mixed>, 1: array}
*
* @throws Query\QueryException
*
* @psalm-return array{0: list<mixed>, 1: array}
*/
private function processParameterMappings(array $paramMappings): array
{
@@ -426,7 +425,6 @@ final class Query extends AbstractQuery
/**
* @return mixed[] tuple of (value, type)
*
* @psalm-return array{0: mixed, 1: mixed}
*/
private function resolveParameterValue(Parameter $parameter): array
@@ -756,10 +754,8 @@ final class Query extends AbstractQuery
/**
* Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
*
* @return string
*/
protected function _getQueryCacheId()
protected function getQueryCacheId(): string
{
ksort($this->_hints);

View File

@@ -27,6 +27,7 @@ namespace Doctrine\ORM\Query\AST;
*/
class CollectionMemberExpression extends Node
{
/** @var mixed */
public $entityExpression;
/** @var PathExpression */

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -31,7 +32,10 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class BitAndFunction extends FunctionNode
{
/** @var Node */
public $firstArithmetic;
/** @var Node */
public $secondArithmetic;
/**

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -31,7 +32,10 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class BitOrFunction extends FunctionNode
{
/** @var Node */
public $firstArithmetic;
/** @var Node */
public $secondArithmetic;
/**

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -33,10 +34,13 @@ use function call_user_func_array;
*/
class ConcatFunction extends FunctionNode
{
/** @var Node */
public $firstStringPrimary;
/** @var Node */
public $secondStringPrimary;
/** @psalm-var list<Node> */
public $concatExpressions = [];
/**

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -31,7 +32,10 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class DateDiffFunction extends FunctionNode
{
/** @var Node */
public $date1;
/** @var Node */
public $date2;
/**

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
@@ -33,6 +34,7 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class LengthFunction extends FunctionNode implements TypedExpression
{
/** @var Node */
public $stringPrimary;
/**

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
@@ -32,7 +33,10 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class LocateFunction extends FunctionNode
{
/** @var Node */
public $firstStringPrimary;
/** @var Node */
public $secondStringPrimary;
/** @var SimpleArithmeticExpression|bool */

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -31,6 +32,7 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class LowerFunction extends FunctionNode
{
/** @psalm-var Node */
public $stringPrimary;
/**

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
@@ -32,6 +33,7 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class SubstringFunction extends FunctionNode
{
/** @var Node */
public $stringPrimary;
/** @var SimpleArithmeticExpression */

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
@@ -31,6 +32,7 @@ use Doctrine\ORM\Query\SqlWalker;
*/
class UpperFunction extends FunctionNode
{
/** @var Node */
public $stringPrimary;
/**

View File

@@ -22,7 +22,8 @@ namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Types\Type;
/**
* Base class for SQL statement executors.
@@ -70,11 +71,12 @@ abstract class AbstractSqlExecutor
/**
* Executes all sql statements.
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @param array $types The parameter types.
* @param Connection $conn The database connection that is used to execute the queries.
* @psalm-param array<int, mixed>|array<string, mixed> $params The parameters.
* @psalm-param array<int, int|string|Type|null>|
* array<string, int|string|Type|null> $types The parameter types.
*
* @return Statement
* @return ResultStatement|int
*/
abstract public function execute(Connection $conn, array $params, array $types);
}

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\SqlWalker;
@@ -38,6 +39,8 @@ class SingleSelectExecutor extends AbstractSqlExecutor
/**
* {@inheritDoc}
*
* @return ResultStatement
*/
public function execute(Connection $conn, array $params, array $types)
{

View File

@@ -457,7 +457,7 @@ class Expr
foreach ($y as &$literal) {
if (! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
$literal = $this->quoteLiteral($literal);
}
}
}
@@ -482,7 +482,7 @@ class Expr
foreach ($y as &$literal) {
if (! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
$literal = $this->quoteLiteral($literal);
}
}
}
@@ -617,17 +617,15 @@ class Expr
*/
public function literal($literal)
{
return new Expr\Literal($this->_quoteLiteral($literal));
return new Expr\Literal($this->quoteLiteral($literal));
}
/**
* Quotes a literal value, if necessary, according to the DQL syntax.
*
* @param mixed $literal The literal value.
*
* @return string
*/
private function _quoteLiteral($literal)
private function quoteLiteral($literal): string
{
if (is_numeric($literal) && ! is_string($literal)) {
return (string) $literal;

View File

@@ -38,8 +38,11 @@ class Andx extends Composite
self::class,
];
/** @psalm-var list<string|Comparison|Func|Orx|self> */
protected $parts = [];
/**
* @return array
* @psalm-return list<string|Comparison|Func|Orx|self>
*/
public function getParts()
{

View File

@@ -45,10 +45,10 @@ abstract class Base
/** @var string */
protected $postSeparator = ')';
/** @var string[] */
/** @psalm-var list<class-string> */
protected $allowedClasses = [];
/** @var mixed[] */
/** @psalm-var list<string|object> */
protected $parts = [];
/**
@@ -60,7 +60,7 @@ abstract class Base
}
/**
* @param array $args
* @psalm-param list<string|object> $args
*
* @return static
*/

View File

@@ -22,7 +22,7 @@ namespace Doctrine\ORM\Query\Expr;
use function implode;
use function is_object;
use function stripos;
use function preg_match;
/**
* Expression class for building DQL and parts.
@@ -63,7 +63,7 @@ class Composite extends Base
}
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) {
if (preg_match('/\s(OR|AND)\s/i', $queryPart)) {
return $this->preSeparator . $queryPart . $this->postSeparator;
}

View File

@@ -39,7 +39,7 @@ class Func
* Creates a function, with the given argument.
*
* @param string $name
* @param array $arguments
* @psalm-param list<mixed> $arguments
*/
public function __construct($name, $arguments)
{
@@ -56,7 +56,7 @@ class Func
}
/**
* @return array
* @psalm-return list<mixed>
*/
public function getArguments()
{

View File

@@ -33,8 +33,11 @@ class GroupBy extends Base
/** @var string */
protected $postSeparator = '';
/** @psalm-var list<string> */
protected $parts = [];
/**
* @return array
* @psalm-return list<string>
*/
public function getParts()
{

View File

@@ -33,8 +33,11 @@ class Literal extends Base
/** @var string */
protected $postSeparator = '';
/** @psalm-var list<string> */
protected $parts = [];
/**
* @return array
* @psalm-return list<string>
*/
public function getParts()
{

View File

@@ -42,7 +42,7 @@ class OrderBy
/** @var string[] */
protected $allowedClasses = [];
/** @var mixed[] */
/** @psalm-var list<string> */
protected $parts = [];
/**
@@ -77,7 +77,7 @@ class OrderBy
}
/**
* @return array
* @psalm-return list<string>
*/
public function getParts()
{

View File

@@ -38,8 +38,11 @@ class Orx extends Composite
self::class,
];
/** @psalm-var list<string|Comparison|Func|Andx|self> */
protected $parts = [];
/**
* @return array
* @psalm-return list<string|Comparison|Func|Andx|self>
*/
public function getParts()
{

View File

@@ -36,8 +36,11 @@ class Select extends Base
/** @var string[] */
protected $allowedClasses = [Func::class];
/** @psalm-var list<string|Func> */
protected $parts = [];
/**
* @return array
* @psalm-return list<string|Func>
*/
public function getParts()
{

View File

@@ -48,7 +48,7 @@ abstract class SQLFilter
/**
* Parameters for the filter.
*
* @var array
* @psalm-var array<string, array{value: string, type: string|null}>
*/
private $parameters = [];

View File

@@ -20,7 +20,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\AggregateExpression;
@@ -52,6 +52,7 @@ use Doctrine\ORM\Query\AST\JoinAssociationPathExpression;
use Doctrine\ORM\Query\AST\LikeExpression;
use Doctrine\ORM\Query\AST\Literal;
use Doctrine\ORM\Query\AST\NewObjectExpression;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\NullComparisonExpression;
use Doctrine\ORM\Query\AST\NullIfExpression;
use Doctrine\ORM\Query\AST\OrderByClause;
@@ -104,10 +105,9 @@ class Parser
/**
* READ-ONLY: Maps BUILT-IN string function names to AST class names.
*
* @var array
* @psalm-var array<string, class-string<Functions\FunctionNode>>
*/
private static $_STRING_FUNCTIONS = [
private static $stringFunctions = [
'concat' => Functions\ConcatFunction::class,
'substring' => Functions\SubstringFunction::class,
'trim' => Functions\TrimFunction::class,
@@ -119,10 +119,9 @@ class Parser
/**
* READ-ONLY: Maps BUILT-IN numeric function names to AST class names.
*
* @var array
* @psalm-var array<string, class-string<Functions\FunctionNode>>
*/
private static $_NUMERIC_FUNCTIONS = [
private static $numericFunctions = [
'length' => Functions\LengthFunction::class,
'locate' => Functions\LocateFunction::class,
'abs' => Functions\AbsFunction::class,
@@ -144,10 +143,9 @@ class Parser
/**
* READ-ONLY: Maps BUILT-IN datetime function names to AST class names.
*
* @var array
* @psalm-var array<string, class-string<Functions\FunctionNode>>
*/
private static $_DATETIME_FUNCTIONS = [
private static $datetimeFunctions = [
'current_date' => Functions\CurrentDateFunction::class,
'current_time' => Functions\CurrentTimeFunction::class,
'current_timestamp' => Functions\CurrentTimestampFunction::class,
@@ -160,19 +158,19 @@ class Parser
* and still need to be validated.
*/
/** @var array */
/** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
private $deferredIdentificationVariables = [];
/** @var array */
/** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
private $deferredPartialObjectExpressions = [];
/** @var array */
/** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
private $deferredPathExpressions = [];
/** @var array */
/** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
private $deferredResultVariables = [];
/** @var array */
/** @psalm-var list<array{token: mixed, expression: mixed, nestingLevel: int}> */
private $deferredNewObjectExpressions = [];
/**
@@ -192,7 +190,7 @@ class Parser
/**
* The EntityManager.
*
* @var EntityManager
* @var EntityManagerInterface
*/
private $em;
@@ -206,7 +204,7 @@ class Parser
/**
* Map of declared query components in the parsed query.
*
* @var array
* @psalm-var array<string, array<string, mixed>>
*/
private $queryComponents = [];
@@ -220,7 +218,7 @@ class Parser
/**
* Any additional custom tree walkers that modify the AST.
*
* @var array
* @psalm-var list<class-string<TreeWalker>>
*/
private $customTreeWalkers = [];
@@ -231,7 +229,7 @@ class Parser
*/
private $customOutputWalker;
/** @var array */
/** @psalm-var list<AST\SelectExpression> */
private $identVariableExpressions = [];
/**
@@ -263,7 +261,7 @@ class Parser
/**
* Adds a custom tree walker for modifying the AST.
*
* @param string $className
* @psalm-param class-string $className
*
* @return void
*/
@@ -295,7 +293,7 @@ class Parser
/**
* Gets the EntityManager used by the parser.
*
* @return EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -412,11 +410,13 @@ class Parser
{
$AST = $this->getAST();
if (($customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
$customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);
if ($customWalkers !== false) {
$this->customTreeWalkers = $customWalkers;
}
if (($customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
$customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER);
if ($customOutputWalker !== false) {
$this->customOutputWalker = $customOutputWalker;
}
@@ -490,8 +490,8 @@ class Parser
/**
* Generates a new syntax error.
*
* @param string $expected Expected string.
* @param array|null $token Got token.
* @param string $expected Expected string.
* @psalm-param array<string, mixed>|null $token Got token.
*
* @return void
*
@@ -505,7 +505,7 @@ class Parser
$tokenPos = $token['position'] ?? '-1';
$message = "line 0, col {$tokenPos}: Error: ";
$message = sprintf('line 0, col %d: Error: ', $tokenPos);
$message .= $expected !== '' ? sprintf('Expected %s, got ', $expected) : 'Unexpected ';
$message .= $this->lexer->lookahead === null ? 'end of string.' : sprintf("'%s'", $token['value']);
@@ -515,8 +515,8 @@ class Parser
/**
* Generates a new semantical error.
*
* @param string $message Optional message.
* @param array|null $token Optional token.
* @param string $message Optional message.
* @psalm-param array<string, mixed>|null $token Optional token.
*
* @return void
*
@@ -552,9 +552,9 @@ class Parser
*
* @param bool $resetPeek Reset peek after finding the closing parenthesis.
*
* @return array
* @psalm-return array<string, mixed>| null
*/
private function peekBeyondClosingParenthesis($resetPeek = true)
private function peekBeyondClosingParenthesis(bool $resetPeek = true)
{
$token = $this->lexer->peek();
$numUnmatched = 1;
@@ -586,11 +586,9 @@ class Parser
/**
* Checks if the given token indicates a mathematical operator.
*
* @param array $token
*
* @return bool TRUE if the token is a mathematical operator, FALSE otherwise.
* @psalm-param array<string, mixed> $token
*/
private function isMathOperator($token)
private function isMathOperator($token): bool
{
return $token !== null && in_array($token['type'], [Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY]);
}
@@ -600,7 +598,7 @@ class Parser
*
* @return bool TRUE if the next-next tokens start a function, FALSE otherwise.
*/
private function isFunction()
private function isFunction(): bool
{
$lookaheadType = $this->lexer->lookahead['type'];
$peek = $this->lexer->peek();
@@ -613,32 +611,34 @@ class Parser
/**
* Checks whether the given token type indicates an aggregate function.
*
* @param int $tokenType
* @psalm-param Lexer::T_* $tokenType
*
* @return bool TRUE if the token type is an aggregate function, FALSE otherwise.
*/
private function isAggregateFunction($tokenType)
private function isAggregateFunction(int $tokenType): bool
{
return in_array($tokenType, [Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT]);
return in_array(
$tokenType,
[Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT]
);
}
/**
* Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
*
* @return bool
*/
private function isNextAllAnySome()
private function isNextAllAnySome(): bool
{
return in_array($this->lexer->lookahead['type'], [Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME]);
return in_array(
$this->lexer->lookahead['type'],
[Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME]
);
}
/**
* Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function processDeferredIdentificationVariables()
private function processDeferredIdentificationVariables(): void
{
foreach ($this->deferredIdentificationVariables as $deferredItem) {
$identVariable = $deferredItem['expression'];
@@ -673,12 +673,8 @@ class Parser
/**
* Validates that the given <tt>NewObjectExpression</tt>.
*
* @param SelectStatement $AST
*
* @return void
*/
private function processDeferredNewObjectExpressions($AST)
private function processDeferredNewObjectExpressions(SelectStatement $AST): void
{
foreach ($this->deferredNewObjectExpressions as $deferredItem) {
$expression = $deferredItem['expression'];
@@ -721,10 +717,8 @@ class Parser
/**
* Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function processDeferredPartialObjectExpressions()
private function processDeferredPartialObjectExpressions(): void
{
foreach ($this->deferredPartialObjectExpressions as $deferredItem) {
$expr = $deferredItem['expression'];
@@ -762,10 +756,8 @@ class Parser
/**
* Validates that the given <tt>ResultVariable</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function processDeferredResultVariables()
private function processDeferredResultVariables(): void
{
foreach ($this->deferredResultVariables as $deferredItem) {
$resultVariable = $deferredItem['expression'];
@@ -806,10 +798,8 @@ class Parser
* StateFieldPathExpression ::= IdentificationVariable "." StateField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
*
* @return void
*/
private function processDeferredPathExpressions()
private function processDeferredPathExpressions(): void
{
foreach ($this->deferredPathExpressions as $deferredItem) {
$pathExpression = $deferredItem['expression'];
@@ -817,7 +807,8 @@ class Parser
$qComp = $this->queryComponents[$pathExpression->identificationVariable];
$class = $qComp['metadata'];
if (($field = $pathExpression->field) === null) {
$field = $pathExpression->field;
if ($field === null) {
$field = $pathExpression->field = $class->identifier[0];
}
@@ -875,10 +866,7 @@ class Parser
}
}
/**
* @return void
*/
private function processRootEntityAliasSelected()
private function processRootEntityAliasSelected(): void
{
if (! count($this->identVariableExpressions)) {
return;
@@ -1008,7 +996,10 @@ class Parser
$exists = isset($this->queryComponents[$aliasIdentVariable]);
if ($exists) {
$this->semanticalError("'$aliasIdentVariable' is already defined.", $this->lexer->token);
$this->semanticalError(
sprintf("'%s' is already defined.", $aliasIdentVariable),
$this->lexer->token
);
}
return $aliasIdentVariable;
@@ -1050,7 +1041,10 @@ class Parser
private function validateAbstractSchemaName($schemaName)
{
if (! (class_exists($schemaName, true) || interface_exists($schemaName, true))) {
$this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token);
$this->semanticalError(
sprintf("Class '%s' is not defined.", $schemaName),
$this->lexer->token
);
}
}
@@ -1067,7 +1061,10 @@ class Parser
$exists = isset($this->queryComponents[$resultVariable]);
if ($exists) {
$this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token);
$this->semanticalError(
sprintf("'%s' is already defined.", $resultVariable),
$this->lexer->token
);
}
return $resultVariable;
@@ -2573,6 +2570,8 @@ class Parser
* InExpression | NullComparisonExpression | ExistsExpression |
* EmptyCollectionComparisonExpression | CollectionMemberExpression |
* InstanceOfExpression
*
* @return Node
*/
public function SimpleConditionalExpression()
{
@@ -2868,7 +2867,8 @@ class Parser
{
$sign = null;
if (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) {
$isPlus = $this->lexer->isNextToken(Lexer::T_PLUS);
if ($isPlus || $this->lexer->isNextToken(Lexer::T_MINUS)) {
$this->match($isPlus ? Lexer::T_PLUS : Lexer::T_MINUS);
$sign = $isPlus;
}
@@ -2889,6 +2889,8 @@ class Parser
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
* | InputParameter | CaseExpression
*
* @return Node|string
*/
public function ArithmeticPrimary()
{
@@ -2970,6 +2972,8 @@ class Parser
/**
* StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
*
* @return Node
*/
public function StringPrimary()
{
@@ -3449,13 +3453,13 @@ class Parser
case $customFunctionDeclaration !== null:
return $customFunctionDeclaration;
case isset(self::$_STRING_FUNCTIONS[$funcName]):
case isset(self::$stringFunctions[$funcName]):
return $this->FunctionsReturningStrings();
case isset(self::$_NUMERIC_FUNCTIONS[$funcName]):
case isset(self::$numericFunctions[$funcName]):
return $this->FunctionsReturningNumerics();
case isset(self::$_DATETIME_FUNCTIONS[$funcName]):
case isset(self::$datetimeFunctions[$funcName]):
return $this->FunctionsReturningDatetime();
default:
@@ -3508,7 +3512,7 @@ class Parser
public function FunctionsReturningNumerics()
{
$funcNameLower = strtolower($this->lexer->lookahead['value']);
$funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
$funcClass = self::$numericFunctions[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
@@ -3547,7 +3551,7 @@ class Parser
public function FunctionsReturningDatetime()
{
$funcNameLower = strtolower($this->lexer->lookahead['value']);
$funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower];
$funcClass = self::$datetimeFunctions[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
@@ -3587,7 +3591,7 @@ class Parser
public function FunctionsReturningStrings()
{
$funcNameLower = strtolower($this->lexer->lookahead['value']);
$funcClass = self::$_STRING_FUNCTIONS[$funcNameLower];
$funcClass = self::$stringFunctions[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);

View File

@@ -47,7 +47,7 @@ class ParserResult
/**
* The mappings of DQL parameter names/positions to SQL parameter positions.
*
* @var array
* @psalm-var array<string|int, list<int>>
*/
private $_parameterMappings = [];
@@ -119,7 +119,7 @@ class ParserResult
/**
* Gets all DQL to SQL parameter mappings.
*
* @return array The parameter mappings.
* @psalm-return array<int|string, list<int>> The parameter mappings.
*/
public function getParameterMappings()
{
@@ -131,7 +131,7 @@ class ParserResult
*
* @param string|int $dqlPosition The name or position of the DQL parameter.
*
* @return array The positions of the corresponding SQL parameters.
* @psalm-return list<int> The positions of the corresponding SQL parameters.
*/
public function getSqlParameterPositions($dqlPosition)
{

View File

@@ -161,11 +161,11 @@ class QueryException extends ORMException
*/
public static function invalidLiteral($literal)
{
return new self("Invalid literal '$literal'");
return new self("Invalid literal '" . $literal . "'");
}
/**
* @param array $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException
*/
@@ -190,7 +190,7 @@ class QueryException extends ORMException
}
/**
* @param array $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException
*/
@@ -215,7 +215,7 @@ class QueryException extends ORMException
}
/**
* @param array $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException
*/

View File

@@ -69,7 +69,7 @@ class QueryExpressionVisitor extends ExpressionVisitor
* Gets bound parameters.
* Filled after {@link dispach()}.
*
* @return Collection
* @return Collection<int, mixed>
*/
public function getParameters()
{

View File

@@ -57,7 +57,7 @@ class ResultSetMapping
* Maps alias names to class names.
*
* @ignore
* @var array
* @psalm-var array<string, class-string>
*/
public $aliasMap = [];
@@ -65,7 +65,7 @@ class ResultSetMapping
* Maps alias names to related association field names.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $relationMap = [];
@@ -73,7 +73,7 @@ class ResultSetMapping
* Maps alias names to parent alias names.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $parentAliasMap = [];
@@ -81,7 +81,7 @@ class ResultSetMapping
* Maps column names in the result set to field names for each class.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $fieldMappings = [];
@@ -89,7 +89,7 @@ class ResultSetMapping
* Maps column names in the result set to the alias/field name to use in the mapped result.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $scalarMappings = [];
@@ -97,7 +97,7 @@ class ResultSetMapping
* Maps column names in the result set to the alias/field type to use in the mapped result.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $typeMappings = [];
@@ -105,7 +105,7 @@ class ResultSetMapping
* Maps entities in the result set to the alias name to use in the mapped result.
*
* @ignore
* @var array
* @psalm-var array<string, string|null>
*/
public $entityMappings = [];
@@ -113,7 +113,7 @@ class ResultSetMapping
* Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $metaMappings = [];
@@ -121,7 +121,7 @@ class ResultSetMapping
* Maps column names in the result set to the alias they belong to.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $columnOwnerMap = [];
@@ -129,7 +129,7 @@ class ResultSetMapping
* List of columns in the result set that are used as discriminator columns.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $discriminatorColumns = [];
@@ -137,7 +137,7 @@ class ResultSetMapping
* Maps alias names to field names that should be used for indexing.
*
* @ignore
* @var array
* @psalm-var array<string, string>
*/
public $indexByMap = [];
@@ -145,35 +145,35 @@ class ResultSetMapping
* Map from column names to class names that declare the field the column is mapped to.
*
* @ignore
* @var array
* @psalm-var array<string, class-string>
*/
public $declaringClasses = [];
/**
* This is necessary to hydrate derivate foreign keys correctly.
*
* @var array
* @psalm-var array<string, array<string, bool>>
*/
public $isIdentifierColumn = [];
/**
* Maps column names in the result set to field names for each new object expression.
*
* @var array
* @psalm-var array<string, array<string, mixed>>
*/
public $newObjectMappings = [];
/**
* Maps metadata parameter names to the metadata attribute.
*
* @var array
* @psalm-var array<int|string, string>
*/
public $metadataParameterMapping = [];
/**
* Contains query parameter names to be resolved as discriminator values
*
* @var array
* @psalm-var array<string, string>
*/
public $discriminatorParameters = [];
@@ -528,7 +528,7 @@ class ResultSetMapping
}
/**
* @return array
* @psalm-return array<string, class-string>
*/
public function getAliasMap()
{

View File

@@ -84,10 +84,10 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
*
* @return void
*/
@@ -103,13 +103,13 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
*
* @return void
*/
@@ -127,7 +127,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
*
* @param string $class
* @param string $alias
* @param array $columnAliasMap
* @psalm-param array<string, string> $columnAliasMap
*
* @return void
*
@@ -179,7 +179,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
}
}
private function isInheritanceSupported(ClassMetadataInfo $classMetadata)
private function isInheritanceSupported(ClassMetadataInfo $classMetadata): bool
{
if (
$classMetadata->isInheritanceTypeSingleTable()
@@ -196,7 +196,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
*
* @param string $columnName
* @param int $mode
* @param array $customRenameColumns
* @psalm-param array<string, string> $customRenameColumns
*
* @return string
*/
@@ -221,10 +221,9 @@ class ResultSetMappingBuilder extends ResultSetMapping
*
* @param string $className
* @param int $mode
* @param array $customRenameColumns
* @psalm-param array<string, string> $customRenameColumns
*
* @return string[]
*
* @psalm-return array<array-key, string>
*/
private function getColumnAliasMap($className, $mode, array $customRenameColumns)
@@ -426,7 +425,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
* Works only for all the entity results. The select parts for scalar
* expressions have to be written manually.
*
* @param array $tableAliases
* @psalm-param array<string, string> $tableAliases
*
* @return string
*/

View File

@@ -25,7 +25,7 @@ use Doctrine\DBAL\Connection;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\QuoteStrategy;
@@ -107,7 +107,7 @@ class SqlWalker implements TreeWalker
/** @var ParserResult */
private $parserResult;
/** @var EntityManager */
/** @var EntityManagerInterface */
private $em;
/** @var Connection */
@@ -122,43 +122,49 @@ class SqlWalker implements TreeWalker
/**
* Map from result variable names to their SQL column alias names.
*
* @var array
* @psalm-var array<string, string|list<string>>
*/
private $scalarResultAliasMap = [];
/**
* Map from Table-Alias + Column-Name to OrderBy-Direction.
*
* @var array
* @var array<string, string>
*/
private $orderedColumnsMap = [];
/**
* Map from DQL-Alias + Field-Name to SQL Column Alias.
*
* @var array
* @var array<string, array<string, string>>
*/
private $scalarFields = [];
/**
* Map of all components/classes that appear in the DQL query.
*
* @var array
* @psalm-var array<string, array{metadata: ClassMetadata, token: array, relation: mixed[], parent: string}>
* @psalm-var array<string, array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }>
*/
private $queryComponents;
/**
* A list of classes that appear in non-scalar SelectExpressions.
*
* @var array
* @psalm-var list<array{class: ClassMetadata, dqlAlias: string, resultAlias: string}>
*/
private $selectedClasses = [];
/**
* The DQL alias of the root class of the currently traversed query.
*
* @var array
* @psalm-var list<string>
*/
private $rootAliases = [];
@@ -222,7 +228,7 @@ class SqlWalker implements TreeWalker
/**
* Gets the EntityManager used by the walker.
*
* @return EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -234,9 +240,14 @@ class SqlWalker implements TreeWalker
*
* @param string $dqlAlias The DQL alias.
*
* @return array
*
* @psalm-return array{metadata: ClassMetadata}
* @psalm-return array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }
*/
public function getQueryComponent($dqlAlias)
{
@@ -350,8 +361,10 @@ class SqlWalker implements TreeWalker
*
* @return string The SQL.
*/
private function _generateClassTableInheritanceJoins($class, $dqlAlias)
{
private function generateClassTableInheritanceJoins(
ClassMetadata $class,
string $dqlAlias
): string {
$sql = '';
$baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
@@ -372,11 +385,9 @@ class SqlWalker implements TreeWalker
}
// Add filters on the root class
if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
$sqlParts[] = $filterSql;
}
$sqlParts[] = $this->generateFilterConditionSQL($parentClass, $tableAlias);
$sql .= implode(' AND ', $sqlParts);
$sql .= implode(' AND ', array_filter($sqlParts));
}
// Ignore subclassing inclusion if partial objects is disallowed
@@ -403,10 +414,7 @@ class SqlWalker implements TreeWalker
return $sql;
}
/**
* @return string
*/
private function _generateOrderedCollectionOrderByItems()
private function generateOrderedCollectionOrderByItems(): string
{
$orderedColumns = [];
@@ -444,11 +452,9 @@ class SqlWalker implements TreeWalker
/**
* Generates a discriminator column SQL condition for the class with the given DQL alias.
*
* @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
*
* @return string
* @psalm-param list<string> $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
*/
private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): string
{
$sqlParts = [];
@@ -490,8 +496,10 @@ class SqlWalker implements TreeWalker
*
* @return string The SQL query part to add to a query.
*/
private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
{
private function generateFilterConditionSQL(
ClassMetadata $targetEntity,
string $targetTableAlias
): string {
if (! $this->em->hasFilters()) {
return '';
}
@@ -519,7 +527,8 @@ class SqlWalker implements TreeWalker
$filterClauses = [];
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);
if ($filterExpr !== '') {
$filterClauses[] = '(' . $filterExpr . ')';
}
}
@@ -551,7 +560,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->walkOrderByClause($AST->orderByClause);
}
$orderBySql = $this->_generateOrderedCollectionOrderByItems();
$orderBySql = $this->generateOrderedCollectionOrderByItems();
if (! $AST->orderByClause && $orderBySql) {
$sql .= ' ORDER BY ' . $orderBySql;
}
@@ -659,7 +668,6 @@ class SqlWalker implements TreeWalker
{
$sql = '';
/** @var Query\AST\PathExpression $pathExpr */
switch ($pathExpr->type) {
case AST\PathExpression::TYPE_STATE_FIELD:
$fieldName = $pathExpr->field;
@@ -920,7 +928,7 @@ class SqlWalker implements TreeWalker
return $sql;
}
$classTableInheritanceJoins = $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
$classTableInheritanceJoins = $this->generateClassTableInheritanceJoins($class, $dqlAlias);
if (! $buildNestedJoins) {
return $sql . $classTableInheritanceJoins;
@@ -988,7 +996,7 @@ class SqlWalker implements TreeWalker
}
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
$discrSql = $this->generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
if ($discrSql) {
$conditions[] = $discrSql;
@@ -1043,7 +1051,7 @@ class SqlWalker implements TreeWalker
}
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
$discrSql = $this->generateDiscriminatorColumnConditionSQL([$joinedDqlAlias]);
if ($discrSql) {
$conditions[] = $discrSql;
@@ -1070,7 +1078,7 @@ class SqlWalker implements TreeWalker
$withCondition = $condExpr === null ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')');
if ($targetClass->isInheritanceTypeJoined()) {
$ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
$ctiJoins = $this->generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
// If we have WITH condition, we need to build nested joins for target class table and cti joins
if ($withCondition) {
$sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
@@ -1111,7 +1119,8 @@ class SqlWalker implements TreeWalker
{
$orderByItems = array_map([$this, 'walkOrderByItem'], $orderByClause->orderByItems);
if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
$collectionOrderByItems = $this->generateOrderedCollectionOrderByItems();
if ($collectionOrderByItems !== '') {
$orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
}
@@ -1177,7 +1186,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, ! $isUnconditionalJoin);
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL([$dqlAlias]);
$discrSql = $this->generateDiscriminatorColumnConditionSQL([$dqlAlias]);
if ($discrSql) {
$conditions[] = $discrSql;
@@ -1803,7 +1812,7 @@ class SqlWalker implements TreeWalker
public function walkWhereClause($whereClause)
{
$condSql = $whereClause !== null ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($this->rootAliases);
$discrSql = $this->generateDiscriminatorColumnConditionSQL($this->rootAliases);
if ($this->em->hasFilters()) {
$filterClauses = [];
@@ -1811,7 +1820,8 @@ class SqlWalker implements TreeWalker
$class = $this->queryComponents[$dqlAlias]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) {
$filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
if ($filterExpr) {
$filterClauses[] = $filterExpr;
}
}
@@ -2205,8 +2215,11 @@ class SqlWalker implements TreeWalker
$parameter = $this->query->getParameter($inputParam->name);
if ($parameter && Type::hasType($type = $parameter->getType())) {
return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform);
if ($parameter) {
$type = $parameter->getType();
if (Type::hasType($type)) {
return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform);
}
}
return '?';

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Interface for walkers of DQL ASTs (abstract syntax trees).
@@ -40,8 +41,14 @@ interface TreeWalker
* Returns internal queryComponents array.
*
* @return array<string, array<string, mixed>>
*
* @psalm-var array<string, array{metadata: ClassMetadata, token: array, relation: mixed[], parent: string}>
* @psalm-return array<string, array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }>
*/
public function getQueryComponents();

View File

@@ -21,13 +21,14 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\ClassMetadata;
use function array_diff;
use function array_keys;
/**
* An adapter implementation of the TreeWalker interface. The methods in this class
* are empty. This class exists as convenience for creating tree walkers.
* are empty. This class exists as convenience for creating tree walkers.
*/
abstract class TreeWalkerAdapter implements TreeWalker
{
@@ -48,7 +49,14 @@ abstract class TreeWalkerAdapter implements TreeWalker
/**
* The query components of the original query (the "symbol table") that was produced by the Parser.
*
* @var array
* @psalm-var array<string, array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }>
*/
private $_queryComponents;
@@ -85,7 +93,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
}
/**
* @return array
* {@inheritDoc}
*/
protected function _getQueryComponents()
{

View File

@@ -20,7 +20,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\ClassMetadata;
use function array_diff;
use function array_keys;
@@ -40,31 +40,25 @@ class TreeWalkerChain implements TreeWalker
*/
private $_walkers;
/**
* The original Query.
*
* @var AbstractQuery
*/
private $_query;
/**
* The ParserResult of the original query that was produced by the Parser.
*
* @var ParserResult
*/
private $_parserResult;
/**
* The query components of the original query (the "symbol table") that was produced by the Parser.
*
* @var array
* @var array<string, array<string, mixed>>
* @psalm-var array<string, array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }>
*/
private $_queryComponents;
/**
* Returns the internal queryComponents array.
*
* @return array
* {@inheritDoc}
*/
public function getQueryComponents()
{
@@ -92,8 +86,6 @@ class TreeWalkerChain implements TreeWalker
*/
public function __construct($query, $parserResult, array $queryComponents)
{
$this->_query = $query;
$this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents;
$this->_walkers = new TreeWalkerChainIterator($this, $query, $parserResult);
}

View File

@@ -56,7 +56,6 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
/**
* @return string|false
*
* @psalm-return class-string<TreeWalker>|false
*/
public function rewind()

View File

@@ -71,7 +71,7 @@ class QueryBuilder
/**
* The array of DQL parts collected.
*
* @var array
* @psalm-var array<string, mixed>
*/
private $_dqlParts = [
'distinct' => false,
@@ -130,7 +130,7 @@ class QueryBuilder
/**
* Keeps root entity alias names for join entities.
*
* @var array
* @psalm-var array<string, string>
*/
private $joinRootAliases = [];
@@ -327,16 +327,16 @@ class QueryBuilder
switch ($this->_type) {
case self::DELETE:
$dql = $this->_getDQLForDelete();
$dql = $this->getDQLForDelete();
break;
case self::UPDATE:
$dql = $this->_getDQLForUpdate();
$dql = $this->getDQLForUpdate();
break;
case self::SELECT:
default:
$dql = $this->_getDQLForSelect();
$dql = $this->getDQLForSelect();
break;
}
@@ -455,7 +455,6 @@ class QueryBuilder
* </code>
*
* @return mixed[]
*
* @psalm-return list<mixed>
*/
public function getRootAliases()
@@ -491,7 +490,6 @@ class QueryBuilder
* </code>
*
* @return mixed[]
*
* @psalm-return list<mixed>
*/
public function getAllAliases()
@@ -512,7 +510,6 @@ class QueryBuilder
* </code>
*
* @return mixed[]
*
* @psalm-return list<mixed>
*/
public function getRootEntities()
@@ -693,9 +690,9 @@ class QueryBuilder
* The available parts are: 'select', 'from', 'join', 'set', 'where',
* 'groupBy', 'having' and 'orderBy'.
*
* @param string $dqlPartName The DQL part name.
* @param object|array $dqlPart An Expr object.
* @param bool $append Whether to append (true) or replace (false).
* @param string $dqlPartName The DQL part name.
* @param bool $append Whether to append (true) or replace (false).
* @psalm-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart An Expr object.
*
* @return self
*/
@@ -1325,7 +1322,8 @@ class QueryBuilder
$visitor = new QueryExpressionVisitor($this->getAllAliases());
if ($whereExpression = $criteria->getWhereExpression()) {
$whereExpression = $criteria->getWhereExpression();
if ($whereExpression) {
$this->andWhere($visitor->dispatch($whereExpression));
foreach ($visitor->getParameters() as $parameter) {
$this->parameters->add($parameter);
@@ -1351,11 +1349,13 @@ class QueryBuilder
}
// Overwrite limits only if they was set in criteria
if (($firstResult = $criteria->getFirstResult()) !== null) {
$firstResult = $criteria->getFirstResult();
if ($firstResult !== null) {
$this->setFirstResult($firstResult);
}
if (($maxResults = $criteria->getMaxResults()) !== null) {
$maxResults = $criteria->getMaxResults();
if ($maxResults !== null) {
$this->setMaxResults($maxResults);
}
@@ -1377,7 +1377,7 @@ class QueryBuilder
/**
* Gets all query parts.
*
* @return array $dqlParts
* @psalm-return array<string, mixed> $dqlParts
*/
public function getDQLParts()
{
@@ -1387,34 +1387,34 @@ class QueryBuilder
/**
* @return string
*/
private function _getDQLForDelete()
private function getDQLForDelete()
{
return 'DELETE'
. $this->_getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
. $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
. $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
. $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
}
/**
* @return string
*/
private function _getDQLForUpdate()
private function getDQLForUpdate()
{
return 'UPDATE'
. $this->_getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
. $this->_getReducedDQLQueryPart('set', ['pre' => ' SET ', 'separator' => ', '])
. $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
. $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
. $this->getReducedDQLQueryPart('set', ['pre' => ' SET ', 'separator' => ', '])
. $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
}
/**
* @return string
*/
private function _getDQLForSelect()
private function getDQLForSelect()
{
$dql = 'SELECT'
. ($this->_dqlParts['distinct'] === true ? ' DISTINCT' : '')
. $this->_getReducedDQLQueryPart('select', ['pre' => ' ', 'separator' => ', ']);
. $this->getReducedDQLQueryPart('select', ['pre' => ' ', 'separator' => ', ']);
$fromParts = $this->getDQLPart('from');
$joinParts = $this->getDQLPart('join');
@@ -1438,21 +1438,18 @@ class QueryBuilder
}
$dql .= implode(', ', $fromClauses)
. $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->_getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ', 'separator' => ', '])
. $this->_getReducedDQLQueryPart('having', ['pre' => ' HAVING '])
. $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
. $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
. $this->getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ', 'separator' => ', '])
. $this->getReducedDQLQueryPart('having', ['pre' => ' HAVING '])
. $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
return $dql;
}
/**
* @param string $queryPartName
* @param array $options
*
* @return string
* @psalm-param array<string, mixed> $options
*/
private function _getReducedDQLQueryPart($queryPartName, $options = [])
private function getReducedDQLQueryPart(string $queryPartName, array $options = []): string
{
$queryPart = $this->getDQLPart($queryPartName);
@@ -1468,7 +1465,7 @@ class QueryBuilder
/**
* Resets DQL parts.
*
* @param array|null $parts
* @psalm-param list<string>|null $parts
*
* @return self
*/

View File

@@ -110,7 +110,8 @@ EOT
$databaseDriver
);
if (($namespace = $input->getOption('namespace')) !== null) {
$namespace = $input->getOption('namespace');
if ($namespace !== null) {
$databaseDriver->setNamespace($namespace);
}
}
@@ -151,7 +152,8 @@ EOT
$entityGenerator->setNumSpaces((int) $input->getOption('num-spaces'));
if (($extend = $input->getOption('extend')) !== null) {
$extend = $input->getOption('extend');
if ($extend !== null) {
$entityGenerator->setClassToExtend($extend);
}
}

View File

@@ -130,7 +130,8 @@ EOT
$entityGenerator->setNumSpaces((int) $input->getOption('num-spaces'));
$entityGenerator->setBackupExisting(! $input->getOption('no-backup'));
if (($extend = $input->getOption('extend')) !== null) {
$extend = $input->getOption('extend');
if ($extend !== null) {
$entityGenerator->setClassToExtend($extend);
}

View File

@@ -72,7 +72,8 @@ class GenerateProxiesCommand extends Command
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
// Process destination directory
if (($destPath = $input->getArgument('dest-path')) === null) {
$destPath = $input->getArgument('dest-path');
if ($destPath === null) {
$destPath = $em->getConfiguration()->getProxyDir();
}

View File

@@ -149,6 +149,7 @@ EOT
* Return all mapped entity class names
*
* @return string[]
* @psalm-return class-string[]
*/
private function getMappedEntities(EntityManagerInterface $entityManager): array
{
@@ -253,7 +254,6 @@ EOT
* @param mixed $value A Value to show
*
* @return string[]
*
* @psalm-return array{0: string, 1: string}
*/
private function formatField($label, $value): array
@@ -268,10 +268,9 @@ EOT
/**
* Format the association mappings
*
* @param array $propertyMappings
* @psalm-param array<string, array<string, mixed>> $propertyMappings
*
* @return string[][]
*
* @psalm-return list<array{0: string, 1: string}>
*/
private function formatMappings(array $propertyMappings): array
@@ -292,10 +291,9 @@ EOT
/**
* Format the entity listeners
*
* @param array $entityListeners
* @psalm-param list<object> $entityListeners
*
* @return string[]
*
* @psalm-return array{0: string, 1: string}
*/
private function formatEntityListeners(array $entityListeners): array

View File

@@ -35,6 +35,7 @@ use function assert;
use function constant;
use function defined;
use function is_numeric;
use function sprintf;
use function str_replace;
use function strtoupper;
@@ -71,7 +72,8 @@ class RunDqlCommand extends Command
$em = $this->getHelper('em')->getEntityManager();
assert($em instanceof EntityManagerInterface);
if (($dql = $input->getArgument('dql')) === null) {
$dql = $input->getArgument('dql');
if ($dql === null) {
throw new RuntimeException("Argument 'dql' is required in order to execute this command correctly.");
}
@@ -85,14 +87,16 @@ class RunDqlCommand extends Command
$hydrationMode = 'Doctrine\ORM\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName));
if (! defined($hydrationMode)) {
throw new RuntimeException(
"Hydration mode '$hydrationModeName' does not exist. It should be either: object. array, scalar or single-scalar."
);
throw new RuntimeException(sprintf(
"Hydration mode '%s' does not exist. It should be either: object. array, scalar or single-scalar.",
$hydrationModeName
));
}
$query = $em->createQuery($dql);
if (($firstResult = $input->getOption('first-result')) !== null) {
$firstResult = $input->getOption('first-result');
if ($firstResult !== null) {
if (! is_numeric($firstResult)) {
throw new LogicException("Option 'first-result' must contain an integer value");
}
@@ -100,7 +104,8 @@ class RunDqlCommand extends Command
$query->setFirstResult((int) $firstResult);
}
if (($maxResult = $input->getOption('max-result')) !== null) {
$maxResult = $input->getOption('max-result');
if ($maxResult !== null) {
if (! is_numeric($maxResult)) {
throw new LogicException("Option 'max-result' must contain an integer value");
}

View File

@@ -20,7 +20,7 @@
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -53,7 +53,7 @@ abstract class AbstractCommand extends Command
$emHelper = $this->getHelper('em');
$em = $emHelper->getEntityManager();
assert($em instanceof EntityManager);
assert($em instanceof EntityManagerInterface);
$metadatas = $em->getMetadataFactory()->getAllMetadata();

View File

@@ -63,22 +63,25 @@ class ValidateSchemaCommand extends Command
if ($input->getOption('skip-mapping')) {
$ui->text('<comment>[SKIPPED] The mapping was not checked.</comment>');
} elseif ($errors = $validator->validateMapping()) {
foreach ($errors as $className => $errorMessages) {
$ui->text(
sprintf(
'<error>[FAIL]</error> The entity-class <comment>%s</comment> mapping is invalid:',
$className
)
);
$ui->listing($errorMessages);
$ui->newLine();
}
++$exit;
} else {
$ui->success('The mapping files are correct.');
$errors = $validator->validateMapping();
if ($errors) {
foreach ($errors as $className => $errorMessages) {
$ui->text(
sprintf(
'<error>[FAIL]</error> The entity-class <comment>%s</comment> mapping is invalid:',
$className
)
);
$ui->listing($errorMessages);
$ui->newLine();
}
++$exit;
} else {
$ui->success('The mapping files are correct.');
}
}
$ui->section('Database');

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