Compare commits

...

20 Commits

Author SHA1 Message Date
Claudio Zizza 4b88ce787d Introduce __unserialize behaviour in docs (#9390)
* Introduce __unserialize behaviour in docs

* Mention deprecation of Serializable interface

* Add link to __unserialize method
2022-01-30 22:47:06 +01:00
Grégoire Paris f9c3470a8d Adapt test logic to PHP and SQLite II (#9442)
In a88242ee6c, testDateSub() was modified
in order to satisfy different opinions to the question "What is now
minus one month", that has different answers for different pieces of
software on March 30th.

Today, we are January 30th, we need to do the same for testDateAdd().
Hopefully this is the last time we hear about this.
2022-01-30 17:48:10 +01:00
Grégoire Paris c1b131b67e Merge pull request #9440 from sir-kain/php-8.1-ci
Added php 8.1 to CI
2022-01-30 09:55:04 +01:00
Grégoire Paris 16b82ea061 Use the identify generator strategy
It is a better default, and should fix tests for PostgreSQL
2022-01-29 11:33:13 +01:00
Waly f8f370ace6 Added php 8.1 to CI 2022-01-28 22:55:25 +00:00
Alexander M. Turek d5c69fb73f Psalm 4.19.0, PHPStan 1.4.3 (#9438) 2022-01-28 21:54:10 +00:00
Alexander M. Turek 93f9eb7af2 Ignore PHPUnit result cache everywhere (#9425) 2022-01-24 12:35:44 +01:00
HypeMC 6d5da83c68 Add support for PHP 8.1 enums in embedded classes (#9419) 2022-01-23 23:56:36 +01:00
jworman 5f01dd8d09 Added class-string typehint on $targetEntity (#9415) 2022-01-23 20:09:41 +01:00
Benjamin Cremer b596e6a665 Allow DiscriminatorColumn with length=0 (#9410) 2022-01-21 10:27:29 +01:00
Alexander M. Turek 79d3cf5880 Move UnderscoreNamingStrategyTest to correct namespace (#9414) 2022-01-20 20:49:11 +01:00
Alexander M. Turek d7b7c28ae5 Fix type on loadCacheEntry (#9398) 2022-01-18 22:49:52 +01:00
Alexander M. Turek d6fd510c49 Update baselines for DBAL 3.3 (#9393) 2022-01-18 09:13:14 +01:00
olsavmic a2a7d5bb01 Accessing private properties and methods from the same class is forbidden (#9311)
Resolves issue https://github.com/doctrine/common/issues/934

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Fix review issues
2022-01-17 23:15:31 +01:00
Vadim Borodavko 223b2650c4 Expose enumType to DBAL to make native DB Enum possible (#9382) 2022-01-17 10:39:16 +01:00
Vadim Borodavko 01c1644d9c Allow using Enum from different namespace than Entity (#9384) 2022-01-16 13:08:30 +01:00
Sukhdev Mohan 3eff2d4b3f Corrected ORM version and added missing dependency (#9386)
* Corrected ORM version and added missing dependency

Noticed that the version wasn't updated, pointing to 2.11.0 instead of 2.10.2. 
Also when following this tutotial ran into missing dependency for "doctrine/annotation" so added that too.

* Tutorial: Bump DBAL, YAML and Cache

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2022-01-16 02:28:30 +01:00
Alexander M. Turek 9ddf8b96f8 PHPStan 1.4.0 (#9385) 2022-01-16 01:22:41 +01:00
Benjamin Eberlei 3d00fa817a [GH-9380] Bugfix: Delegate ReflectionEnumProperty::getAttributes(). (#9381)
* [GH-9380] Bugfix: Delegate ReflectionEnumProperty::getAttributes().

* [GH-9380] Add test for retrieving attributes via enum property.

* [GH-9380] Add test for retrieving attributes via enum property.

* [GH-9380] Call parent ReflectionProperty ctor for best behavior.

* Update tests/Doctrine/Tests/ORM/Functional/EnumTest.php

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

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2022-01-16 00:06:59 +01:00
Andrii Dembitskyi c0a1404e4c Add detach as of list cascade-all operations (#9357) 2022-01-12 22:33:11 +01:00
33 changed files with 435 additions and 82 deletions
+3 -3
View File
@@ -78,7 +78,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
postgres-version:
@@ -139,7 +139,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
mariadb-version:
@@ -205,7 +205,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
mysql-version:
+1 -1
View File
@@ -15,5 +15,5 @@ vendor/
/tests/Doctrine/Performance/history.db
/.phpcs-cache
composer.lock
/.phpunit.result.cache
.phpunit.result.cache
/*.phpunit.xml
+2 -2
View File
@@ -43,12 +43,12 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "1.3.3",
"phpstan/phpstan": "1.4.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.18.1"
"vimeo/psalm": "4.19.0"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 2.0"
@@ -0,0 +1,74 @@
Accessing private/protected properties/methods of the same class from different instance
========================================================================================
.. sectionauthor:: Michael Olsavsky (olsavmic)
As explained in the :doc:`restrictions for entity classes in the manual <../reference/architecture>`,
it is dangerous to access private/protected properties of different entity instance of the same class because of lazy loading.
The proxy instance that's injected instead of the real entity may not be initialized yet
and therefore not contain expected data which may result in unexpected behavior.
That's a limitation of current proxy implementation - only public methods automatically initialize proxies.
It is usually preferable to use a public interface to manipulate the object from outside the `$this`
context but it may not be convenient in some cases. The following example shows how to do it safely.
Safely accessing private properties from different instance of the same class
-----------------------------------------------------------------------------
To safely access private property of different instance of the same class, make sure to initialise
the proxy before use manually as follows:
.. code-block:: php
<?php
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Entity
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Entity")
* @ORM\JoinColumn(nullable=false)
*/
private self $parent;
/**
* @ORM\Column(type="string", nullable=false)
*/
private string $name;
// ...
public function doSomethingWithParent()
{
// Always initializing the proxy before use
if ($this->parent instanceof Proxy) {
$this->parent->__load();
}
// Accessing the `$this->parent->name` property without loading the proxy first
// may throw error in case the Proxy has not been initialized yet.
$this->parent->name;
}
public function doSomethingWithAnotherInstance(self $instance)
{
// Always initializing the proxy before use
if ($instance instanceof Proxy) {
$instance->__load();
}
// Accessing the `$instance->name` property without loading the proxy first
// may throw error in case the Proxy has not been initialized yet.
$instance->name;
}
// ...
}
+10 -4
View File
@@ -82,9 +82,13 @@ be any regular PHP class observing the following restrictions:
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
- An entity class must not implement ``__wakeup`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
Also consider implementing
`Serializable <https://php.net/manual/en/class.serializable.php>`_
instead.
You can also consider implementing
`Serializable <https://php.net/manual/en/class.serializable.php>`_,
but be aware that it is deprecated since PHP 8.1. We do not recommend its usage.
- PHP 7.4 introduces :doc:`the new magic method <https://php.net/manual/en/language.oop5.magic.php#object.unserialize>`
``__unserialize``, which changes the execution priority between
``__wakeup`` and itself when used. This can cause unexpected behaviour in
an Entity.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
property with the same name. That is, if B inherits from A then B
@@ -93,6 +97,7 @@ be any regular PHP class observing the following restrictions:
- An entity cannot make use of func_get_args() to implement variable parameters.
Generated proxies do not support this for performance reasons and your code might
actually fail to work when violating this restriction.
- Entity cannot access private/protected properties/methods of another entity of the same class or :doc:`do so safely <../cookbook/accessing-private-properties-of-the-same-class-from-different-instance>`.
Entities support inheritance, polymorphic associations, and
polymorphic queries. Both abstract and concrete classes can be
@@ -161,7 +166,8 @@ possible for ``__sleep`` to return names of private properties in
parent classes. On the other hand it is not a solution for proxy
objects to implement ``Serializable`` because Serializable does not
work well with any potential cyclic object references (at least we
did not find a way yet, if you did, please contact us).
did not find a way yet, if you did, please contact us). The
``Serializable`` interface is also deprecated beginning with PHP 8.1.
The EntityManager
~~~~~~~~~~~~~~~~~
+1
View File
@@ -694,6 +694,7 @@ specified by their respective tags:
- ``<cascade-merge />``
- ``<cascade-remove />``
- ``<cascade-refresh />``
- ``<cascade-detach />``
Join Column Element
~~~~~~~~~~~~~~~~~~~
+5 -4
View File
@@ -81,10 +81,11 @@ that directory with the following contents:
{
"require": {
"doctrine/orm": "^2.10.2",
"doctrine/dbal": "^3.1.1",
"symfony/yaml": "2.*",
"symfony/cache": "^5.3"
"doctrine/orm": "^2.11.0",
"doctrine/dbal": "^3.2",
"doctrine/annotations": "1.13.2",
"symfony/yaml": "^5.4",
"symfony/cache": "^5.4"
},
"autoload": {
"psr-0": {"": "src/"}
+1 -1
View File
@@ -24,7 +24,7 @@ interface EntityHydrator
* @param ClassMetadata $metadata The entity metadata.
* @param EntityCacheKey $key The entity cache key.
* @param EntityCacheEntry $entry The entity cache entry.
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
* @param object|null $entity The entity to load the cache into. If not specified, a new entity is created.
*
* @return object|null
*/
+12 -2
View File
@@ -1056,9 +1056,19 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
$childProperty = $this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']);
assert($childProperty !== null);
if (isset($mapping['enumType'])) {
$childProperty = new ReflectionEnumProperty(
$childProperty,
$mapping['enumType']
);
}
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
$parentReflFields[$mapping['declaredField']],
$this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']),
$childProperty,
$mapping['originalClass']
);
continue;
@@ -1517,7 +1527,7 @@ class ClassMetadataInfo implements ClassMetadata
! isset($mapping['type'])
&& ($type instanceof ReflectionNamedType)
) {
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName(), false)) {
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) {
$mapping['enumType'] = $type->getName();
$reflection = new ReflectionEnum($type->getName());
@@ -15,13 +15,13 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_CLASS)]
final class DiscriminatorColumn implements Annotation
{
/** @var string */
/** @var string|null */
public $name;
/** @var string */
/** @var string|null */
public $type;
/** @var int */
/** @var int|null */
public $length;
/**
@@ -281,7 +281,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
[
'name' => $discrColumnAnnot->name,
'type' => $discrColumnAnnot->type ?: 'string',
'length' => $discrColumnAnnot->length ?: 255,
'length' => $discrColumnAnnot->length ?? 255,
'columnDefinition' => $discrColumnAnnot->columnDefinition,
]
);
+3 -2
View File
@@ -16,7 +16,7 @@ use Doctrine\Deprecations\Deprecation;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ManyToMany implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string|null */
@@ -43,7 +43,8 @@ final class ManyToMany implements Annotation
public $indexBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $targetEntity = null,
+3 -2
View File
@@ -15,7 +15,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ManyToOne implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string[]|null */
@@ -33,7 +33,8 @@ final class ManyToOne implements Annotation
public $inversedBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $targetEntity = null,
+3 -2
View File
@@ -18,7 +18,7 @@ final class OneToMany implements Annotation
/** @var string */
public $mappedBy;
/** @var string */
/** @var class-string|null */
public $targetEntity;
/** @var array<string> */
@@ -39,7 +39,8 @@ final class OneToMany implements Annotation
public $indexBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $mappedBy = null,
+2 -1
View File
@@ -15,7 +15,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class OneToOne implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string|null */
@@ -39,6 +39,7 @@ final class OneToOne implements Annotation
public $orphanRemoval = false;
/**
* @param class-string|null $targetEntity
* @param array<string>|null $cascade
*/
public function __construct(
@@ -29,6 +29,11 @@ class ReflectionEnumProperty extends ReflectionProperty
{
$this->originalReflectionProperty = $originalReflectionProperty;
$this->enumType = $enumType;
parent::__construct(
$originalReflectionProperty->getDeclaringClass()->getName(),
$originalReflectionProperty->getName()
);
}
/**
@@ -481,6 +481,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->get($offset);
+9 -3
View File
@@ -781,12 +781,18 @@ class SchemaTool
*/
private function gatherColumnOptions(array $mapping): array
{
if (! isset($mapping['options'])) {
$mappingOptions = $mapping['options'] ?? [];
if (isset($mapping['enumType'])) {
$mappingOptions['enumType'] = $mapping['enumType'];
}
if (empty($mappingOptions)) {
return [];
}
$options = array_intersect_key($mapping['options'], array_flip(self::KNOWN_COLUMN_OPTIONS));
$options['customSchemaOptions'] = array_diff_key($mapping['options'], $options);
$options = array_intersect_key($mappingOptions, array_flip(self::KNOWN_COLUMN_OPTIONS));
$options['customSchemaOptions'] = array_diff_key($mappingOptions, $options);
return $options;
}
+2
View File
@@ -269,9 +269,11 @@
<rule ref="Generic.WhiteSpace.ScopeIndent.Incorrect">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Suit.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Unit.php</exclude-pattern>
</rule>
<rule ref="Generic.WhiteSpace.ScopeIndent.IncorrectExact">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Suit.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Unit.php</exclude-pattern>
</rule>
</ruleset>
+1 -15
View File
@@ -270,11 +270,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Array \\(array\\<class\\-string, object\\>\\) does not accept key string\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapEmbedded\\(\\)\\.$#"
count: 1
@@ -725,11 +720,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php
-
message: "#^Array \\(array\\<class\\-string, string\\>\\) does not accept key string\\.$#"
count: 1
path: lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
-
message: "#^Parameter \\#3 \\$hints of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:hydrateAll\\(\\) expects array\\<string, string\\>, array\\<string, Doctrine\\\\ORM\\\\PersistentCollection\\|true\\> given\\.$#"
count: 1
@@ -1000,11 +990,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php
-
message: "#^Array \\(array\\<string, array\\<int, string\\>\\|string\\>\\) does not accept key int\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/SqlWalker.php
-
message: "#^Call to function is_string\\(\\) with Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node will always evaluate to false\\.$#"
count: 2
@@ -1864,3 +1849,4 @@ parameters:
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$subClasses\\.$#"
count: 1
path: lib/Doctrine/ORM/Utility/HierarchyDiscriminatorResolver.php
+65 -13
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.18.1@dda05fa913f4dc6eb3386f2f7ce5a45d37a71bcb">
<files psalm-version="4.19.0@a2ad69ae4f5ab1f7d225a8dc4e2ec2d9415ed599">
<file src="lib/Doctrine/ORM/AbstractQuery.php">
<DeprecatedClass occurrences="1">
<code>IterableResult</code>
@@ -508,6 +508,12 @@
<code>$entity</code>
</PossiblyNullArgument>
</file>
<file src="lib/Doctrine/ORM/Id/SequenceGenerator.php">
<MethodSignatureMustProvideReturnType occurrences="2">
<code>serialize</code>
<code>unserialize</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Id/TableGenerator.php">
<PossiblyFalseOperand occurrences="3">
<code>$currentLevel</code>
@@ -703,8 +709,9 @@
<DeprecatedConstant occurrences="1">
<code>self::GENERATOR_TYPE_UUID</code>
</DeprecatedConstant>
<DeprecatedMethod occurrences="1">
<DeprecatedMethod occurrences="2">
<code>canEmulateSchemas</code>
<code>canRequireSQLConversion</code>
</DeprecatedMethod>
<DeprecatedProperty occurrences="4">
<code>$this-&gt;columnNames</code>
@@ -736,6 +743,9 @@
<code>$className</code>
<code>$this-&gt;namespace . '\\' . $className</code>
</LessSpecificReturnStatement>
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<MissingClosureParamType occurrences="2">
<code>$joinColumn</code>
<code>$joinColumn</code>
@@ -762,7 +772,7 @@
<code>$fieldName</code>
<code>$fieldName</code>
</ParamNameMismatch>
<PossiblyNullArgument occurrences="9">
<PossiblyNullArgument occurrences="8">
<code>$class</code>
<code>$className</code>
<code>$entityResult['entityClass']</code>
@@ -771,7 +781,6 @@
<code>$parentReflFields[$embeddedClass['declaredField']]</code>
<code>$parentReflFields[$mapping['declaredField']]</code>
<code>$queryMapping['resultClass']</code>
<code>$this-&gt;getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField'])</code>
</PossiblyNullArgument>
<PossiblyNullPropertyFetch occurrences="2">
<code>$embeddable-&gt;reflClass-&gt;name</code>
@@ -861,11 +870,8 @@
</MissingClosureParamType>
</file>
<file src="lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php">
<PossiblyNullPropertyAssignmentValue occurrences="4">
<PossiblyNullPropertyAssignmentValue occurrences="1">
<code>$columnDefinition</code>
<code>$length</code>
<code>$name</code>
<code>$type</code>
</PossiblyNullPropertyAssignmentValue>
</file>
<file src="lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php">
@@ -1244,11 +1250,10 @@
</MissingConstructor>
</file>
<file src="lib/Doctrine/ORM/Mapping/OneToMany.php">
<PossiblyNullPropertyAssignmentValue occurrences="4">
<PossiblyNullPropertyAssignmentValue occurrences="3">
<code>$cascade</code>
<code>$indexBy</code>
<code>$mappedBy</code>
<code>$targetEntity</code>
</PossiblyNullPropertyAssignmentValue>
</file>
<file src="lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php">
@@ -2042,6 +2047,9 @@
<DocblockTypeContradiction occurrences="1">
<code>is_array($obj)</code>
</DocblockTypeContradiction>
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<RedundantConditionGivenDocblockType occurrences="1">
<code>is_object($obj)</code>
</RedundantConditionGivenDocblockType>
@@ -2235,22 +2243,46 @@
<ArgumentTypeCoercion occurrences="1">
<code>$this-&gt;parts</code>
</ArgumentTypeCoercion>
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<PossiblyInvalidCast occurrences="1">
<code>$this-&gt;parts[0]</code>
</PossiblyInvalidCast>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Comparison.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Composite.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<PossiblyInvalidCast occurrences="2">
<code>$part</code>
<code>$this-&gt;parts[0]</code>
</PossiblyInvalidCast>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/From.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Func.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/GroupBy.php">
<NonInvariantDocblockPropertyType occurrences="1">
<code>$parts</code>
</NonInvariantDocblockPropertyType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Join.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<PossiblyNullArgument occurrences="1">
<code>$this-&gt;conditionType</code>
</PossiblyNullArgument>
@@ -2260,6 +2292,16 @@
<code>$parts</code>
</NonInvariantDocblockPropertyType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Math.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/OrderBy.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ORM/Query/Expr/Orx.php">
<NonInvariantDocblockPropertyType occurrences="2">
<code>$allowedClasses</code>
@@ -2273,6 +2315,9 @@
</NonInvariantDocblockPropertyType>
</file>
<file src="lib/Doctrine/ORM/Query/Filter/SQLFilter.php">
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<MissingClosureParamType occurrences="1">
<code>$value</code>
</MissingClosureParamType>
@@ -2507,9 +2552,9 @@
</PropertyNotSetInConstructor>
</file>
<file src="lib/Doctrine/ORM/Query/QueryExpressionVisitor.php">
<RedundantCondition occurrences="1">
<RedundantConditionGivenDocblockType occurrences="1">
<code>Comparison::EQ</code>
</RedundantCondition>
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Query/ResultSetMapping.php">
<PropertyTypeCoercion occurrences="3">
@@ -2526,6 +2571,9 @@
<code>$renameMode</code>
<code>$renameMode</code>
</ArgumentTypeCoercion>
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<PossiblyNullPropertyFetch occurrences="1">
<code>$classMetadata-&gt;reflClass-&gt;name</code>
</PossiblyNullPropertyFetch>
@@ -2903,6 +2951,9 @@
<InvalidScalarArgument occurrences="1">
<code>$key</code>
</InvalidScalarArgument>
<MethodSignatureMustProvideReturnType occurrences="1">
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
<PossiblyFalseArgument occurrences="2">
<code>$spacePos</code>
<code>$spacePos</code>
@@ -3236,8 +3287,9 @@
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php">
<ArgumentTypeCoercion occurrences="1">
<ArgumentTypeCoercion occurrences="2">
<code>$metadata-&gt;changeTrackingPolicy</code>
<code>$simpleXml-&gt;asXML()</code>
</ArgumentTypeCoercion>
<DeprecatedClass occurrences="1">
<code>AbstractExporter</code>
-12
View File
@@ -90,18 +90,6 @@
<file name="lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
</errorLevel>
</MissingDependency>
<ParadoxicalCondition>
<errorLevel type="suppress">
<!-- See https://github.com/vimeo/psalm/issues/3381 -->
<file name="lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php"/>
</errorLevel>
</ParadoxicalCondition>
<NullArgument>
<errorLevel type="suppress">
<!-- See https://github.com/vimeo/psalm/issues/5920 -->
<file name="lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php"/>
</errorLevel>
</NullArgument>
<RedundantCastGivenDocblockType>
<errorLevel type="suppress">
<!-- Can be removed once the "getMaxResults" methods of those classes have native parameter types -->
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\Enums;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Embedded;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
#[Entity]
class Product
{
#[Id, GeneratedValue, Column(type: 'integer')]
public int $id;
#[Embedded(class: Quantity::class)]
public Quantity $quantity;
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\Enums;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Embeddable;
#[Embeddable]
class Quantity
{
#[Column(type: 'integer')]
public int $value;
#[Column(type: 'string', enumType: Unit::class)]
public Unit $unit;
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\Enums;
enum Unit: string
{
case Gram = 'g';
case Meter = 'm';
}
@@ -13,7 +13,7 @@ use Doctrine\ORM\Mapping\Table;
#[Entity, Table(name: 'author')]
class Author
{
#[Column, Id, GeneratedValue]
#[Column, Id, GeneratedValue(strategy: 'IDENTITY')]
private readonly int $id;
#[Column]
@@ -17,7 +17,7 @@ use Doctrine\ORM\Mapping\Table;
#[Entity, Table(name: 'book')]
class Book
{
#[Column, Id, GeneratedValue]
#[Column, Id, GeneratedValue(strategy: 'IDENTITY')]
private readonly int $id;
#[Column]
@@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping\Table;
#[Entity, Table(name: 'simple_book')]
class SimpleBook
{
#[Column, Id, GeneratedValue]
#[Column, Id, GeneratedValue(strategy: 'IDENTITY')]
private readonly int $id;
#[Column]
@@ -4,12 +4,16 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\Tests\Models\Enums\Card;
use Doctrine\Tests\Models\Enums\Product;
use Doctrine\Tests\Models\Enums\Quantity;
use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\Models\Enums\TypedCard;
use Doctrine\Tests\Models\Enums\Unit;
use Doctrine\Tests\OrmFunctionalTestCase;
use function dirname;
@@ -22,11 +26,11 @@ class EnumTest extends OrmFunctionalTestCase
{
public function setUp(): void
{
parent::setUp();
$this->_em = $this->getEntityManager(null, new AttributeDriver([dirname(__DIR__, 2) . '/Models/Enums']));
$this->_schemaTool = new SchemaTool($this->_em);
parent::setUp();
if ($this->isSecondLevelCacheEnabled) {
$this->markTestSkipped();
}
@@ -101,4 +105,34 @@ EXCEPTION
TypedCard::class => [TypedCard::class],
];
}
public function testItAllowsReadingAttributes(): void
{
$metadata = $this->_em->getClassMetadata(Card::class);
$property = $metadata->getReflectionProperty('suit');
$attributes = $property->getAttributes();
$this->assertCount(1, $attributes);
$this->assertEquals(Column::class, $attributes[0]->getName());
}
public function testEnumMappingWithEmbeddable(): void
{
$this->setUpEntitySchema([Product::class]);
$product = new Product();
$product->quantity = new Quantity();
$product->quantity->value = 10;
$product->quantity->unit = Unit::Gram;
$this->_em->persist($product);
$this->_em->flush();
$this->_em->clear();
$fetchedProduct = $this->_em->find(Product::class, $product->id);
$this->assertInstanceOf(Unit::class, $fetchedProduct->quantity->unit);
$this->assertEquals(Unit::Gram, $fetchedProduct->quantity->unit);
}
}
@@ -312,8 +312,18 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
self::assertArrayHasKey('now', $result);
self::assertArrayHasKey('add', $result);
$now = new DateTimeImmutable($result['now']);
$inOneUnit = $now->modify(sprintf('+%d %s', $amount, $unit));
if (
$unit === 'month'
&& $inOneUnit->format('m') === $now->modify('+2 month')->format('m')
&& ! $this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform
) {
$inOneUnit = new DateTimeImmutable('last day of next month');
}
self::assertEqualsWithDelta(
(new DateTimeImmutable($result['now']))->modify(sprintf('+%d %s', $amount, $unit)),
$inOneUnit,
new DateTimeImmutable($result['add']),
$delta
);
@@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\Cache;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\DiscriminatorColumn;
use Doctrine\ORM\Mapping\DiscriminatorMap;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Entity;
@@ -33,6 +34,7 @@ use Doctrine\Tests\Models\DDC1872\DDC1872ExampleEntityWithOverride;
use Doctrine\Tests\Models\DirectoryTree\Directory;
use Doctrine\Tests\Models\DirectoryTree\File;
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
use Generator;
class AnnotationDriverTest extends AbstractMappingDriverTest
{
@@ -274,6 +276,30 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
self::assertArrayHasKey('example_trait_bar_id', $metadataWithoutOverride->associationMappings['bar']['joinColumnFieldNames']);
self::assertArrayHasKey('example_entity_overridden_bar_id', $metadataWithOverride->associationMappings['bar']['joinColumnFieldNames']);
}
/**
* @psalm-param class-string $class
*
* @dataProvider provideDiscriminatorColumnTestcases
*/
public function testLengthForDiscriminatorColumn(string $class, int $expectedLength): void
{
$factory = $this->createClassMetadataFactory();
$metadata = $factory->getMetadataFor($class);
self::assertNotNull($metadata->discriminatorColumn);
self::assertArrayHasKey('length', $metadata->discriminatorColumn);
self::assertSame($expectedLength, $metadata->discriminatorColumn['length']);
}
public function provideDiscriminatorColumnTestcases(): Generator
{
yield [DiscriminatorColumnWithNullLength::class, 255];
yield [DiscriminatorColumnWithNoLength::class, 255];
yield [DiscriminatorColumnWithZeroLength::class, 0];
yield [DiscriminatorColumnWithNonZeroLength::class, 60];
}
}
/**
@@ -438,3 +464,83 @@ class AnnotationSLCFoo
*/
public $id;
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(
* name="type",
* type="string",
* length=0,
* columnDefinition="enum('region','airport','station','poi') NOT NULL",
* ),
* @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"})
*/
class DiscriminatorColumnWithZeroLength
{
/**
* @var int
* @Id
* @Column
*/
public $id;
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(
* name="type",
* type="string",
* ),
* @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"})
*/
class DiscriminatorColumnWithNoLength
{
/**
* @var int
* @Id
* @Column
*/
public $id;
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(
* name="type",
* type="string",
* length=60,
* ),
* @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"})
*/
class DiscriminatorColumnWithNonZeroLength
{
/**
* @var int
* @Id
* @Column
*/
public $id;
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(
* name="type",
* type="string",
* length=null,
* ),
* @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"})
*/
class DiscriminatorColumnWithNullLength
{
/**
* @var int
* @Id
* @Column
*/
public $id;
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Tests\Mapping;
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\Deprecations\Deprecation;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
@@ -15,16 +15,14 @@ final class UnderscoreNamingStrategyTest extends TestCase
{
use VerifyDeprecations;
/** @test */
public function checkDeprecationMessage(): void
public function testDeprecationMessage(): void
{
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/7908');
new UnderscoreNamingStrategy(CASE_LOWER, false);
}
/** @test */
public function checkNoDeprecationMessageWhenNumberAwareEnabled(): void
public function testNoDeprecationMessageWhenNumberAwareEnabled(): void
{
$before = Deprecation::getTriggeredDeprecations()['https://github.com/doctrine/orm/pull/7908'] ?? 0;
@@ -35,6 +35,8 @@ use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedChildClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedIdentityClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedRootClass;
use Doctrine\Tests\Models\Enums\Card;
use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\Models\Forum\ForumAvatar;
use Doctrine\Tests\Models\Forum\ForumUser;
use Doctrine\Tests\Models\NullDefault\NullDefaultColumn;
@@ -189,6 +191,23 @@ class SchemaToolTest extends OrmTestCase
self::assertSame([], $customSchemaOptions);
}
/**
* @requires PHP 8.1
*/
public function testEnumTypeAddedToCustomSchemaOptions(): void
{
$em = $this->getTestEntityManager();
$schemaTool = new SchemaTool($em);
$customSchemaOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(Card::class)])
->getTable('Card')
->getColumn('suit')
->getCustomSchemaOptions();
self::assertArrayHasKey('enumType', $customSchemaOptions);
self::assertSame(Suit::class, $customSchemaOptions['enumType']);
}
/**
* @group DDC-3671
*/