From 1b3c3ebab1603afa8eb9afcdbe8270a1b4a007e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 06:12:55 +0000 Subject: [PATCH 1/4] build(deps-dev): update phpstan/phpstan requirement Updates the requirements on [phpstan/phpstan](https://github.com/phpstan/phpstan) to permit the latest version. - [Release notes](https://github.com/phpstan/phpstan/releases) - [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md) - [Commits](https://github.com/phpstan/phpstan/compare/1.10.59...1.10.66) --- updated-dependencies: - dependency-name: phpstan/phpstan dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3f3e1f3b..9fa22fcf 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "nette/tester": "~2.5", "mockery/mockery": ">=1.5.1", "phpstan/extension-installer": "1.3.1", - "phpstan/phpstan": "1.10.59", + "phpstan/phpstan": "1.10.66", "phpstan/phpstan-deprecation-rules": "1.1.4", "phpstan/phpstan-nette": "1.2.9", "phpstan/phpstan-mockery": "1.1.2", From 373806365620cfe92e6b13d88ddf6f40dea6916f Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Mon, 1 Apr 2024 23:48:11 +0200 Subject: [PATCH 2/4] type the modifiers argument properly for single/multi-column --- src/Collection/Functions/BaseCompareFunction.php | 6 +++--- .../Functions/CompareEqualsFunction.php | 16 ++++++++-------- .../CompareGreaterThanEqualsFunction.php | 6 +++++- .../Functions/CompareGreaterThanFunction.php | 6 +++++- .../Functions/CompareNotEqualsFunction.php | 14 +++++++------- .../CompareSmallerThanEqualsFunction.php | 6 +++++- .../Functions/CompareSmallerThanFunction.php | 6 +++++- .../Functions/FetchPropertyFunction.php | 11 ++++++----- .../Functions/Result/DbalExpressionResult.php | 4 ++-- 9 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/Collection/Functions/BaseCompareFunction.php b/src/Collection/Functions/BaseCompareFunction.php index 01154467..71949e87 100644 --- a/src/Collection/Functions/BaseCompareFunction.php +++ b/src/Collection/Functions/BaseCompareFunction.php @@ -74,7 +74,7 @@ public function processDbalExpression( $value = $args[1]; } - return $this->evaluateInDb($expression, $value, $expression->dbalModifier ?? '%any'); + return $this->evaluateInDb($expression, $value, $expression->dbalModifier); } @@ -82,11 +82,11 @@ abstract protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue /** - * @param literal-string $modifier + * @param literal-string|array|null $modifier */ abstract protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult; } diff --git a/src/Collection/Functions/CompareEqualsFunction.php b/src/Collection/Functions/CompareEqualsFunction.php index b57b0fd7..dcb73bc0 100644 --- a/src/Collection/Functions/CompareEqualsFunction.php +++ b/src/Collection/Functions/CompareEqualsFunction.php @@ -4,8 +4,8 @@ use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult; +use Nextras\Orm\Exception\InvalidArgumentException; use function count; -use function explode; use function in_array; use function is_array; @@ -25,7 +25,7 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { if (is_array($value)) { @@ -33,22 +33,20 @@ protected function evaluateInDb( // Multi-column primary key handling // Construct multiOr simplification as array{list, modifiers: list, values: list>} $args = $expression->getArgumentsForExpansion(); - if (count($args) === 2 && $args[0] === '%column' && is_array($args[1])) { + if (count($args) === 2 && $args[0] === '%column' && is_array($args[1]) && is_array($modifier)) { $columns = $args[1]; - $modifiers = array_map( - fn (string $modifier): ?string => strlen($modifier) === 0 ? null : $modifier, - explode(',', $modifier) - ); $data = []; foreach ($value as $dataSet) { $set = []; foreach ($dataSet as $i => $dataSetValue) { - $set[] = [$columns[$i], $dataSetValue, $modifiers[$i] ?? null]; + $set[] = [$columns[$i], $dataSetValue, $modifier[$i] ?? null]; } $data[] = $set; } return $expression->withArgs('%multiOr', [$data]); } else { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; if ($modifier !== '%any') $modifier .= '[]'; return $expression->append("IN $modifier", $value); } @@ -58,6 +56,8 @@ protected function evaluateInDb( } elseif ($value === null) { return $expression->append('IS NULL'); } else { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append("= $modifier", $value); } } diff --git a/src/Collection/Functions/CompareGreaterThanEqualsFunction.php b/src/Collection/Functions/CompareGreaterThanEqualsFunction.php index bf0b77d2..62b63c53 100644 --- a/src/Collection/Functions/CompareGreaterThanEqualsFunction.php +++ b/src/Collection/Functions/CompareGreaterThanEqualsFunction.php @@ -4,6 +4,8 @@ use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult; +use Nextras\Orm\Exception\InvalidArgumentException; +use function is_array; class CompareGreaterThanEqualsFunction extends BaseCompareFunction @@ -17,9 +19,11 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append(">= $modifier", $value); } } diff --git a/src/Collection/Functions/CompareGreaterThanFunction.php b/src/Collection/Functions/CompareGreaterThanFunction.php index 04300ec9..3dcb28f6 100644 --- a/src/Collection/Functions/CompareGreaterThanFunction.php +++ b/src/Collection/Functions/CompareGreaterThanFunction.php @@ -4,6 +4,8 @@ use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult; +use Nextras\Orm\Exception\InvalidArgumentException; +use function is_array; class CompareGreaterThanFunction extends BaseCompareFunction @@ -17,9 +19,11 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append("> $modifier", $value); } } diff --git a/src/Collection/Functions/CompareNotEqualsFunction.php b/src/Collection/Functions/CompareNotEqualsFunction.php index 87f32b05..ae1cd94c 100644 --- a/src/Collection/Functions/CompareNotEqualsFunction.php +++ b/src/Collection/Functions/CompareNotEqualsFunction.php @@ -28,7 +28,7 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { if (is_array($value)) { @@ -36,22 +36,20 @@ protected function evaluateInDb( // Multi-column primary key handling // Construct multiOr simplification as array{list, modifiers: list, values: list>} $args = $expression->getArgumentsForExpansion(); - if (count($args) === 2 && $args[0] === '%column' && is_array($args[1])) { + if (count($args) === 2 && $args[0] === '%column' && is_array($args[1]) && is_array($modifier)) { $columns = $args[1]; - $modifiers = array_map( - fn (string $modifier): ?string => strlen($modifier) === 0 ? null : $modifier, - explode(',', $modifier) - ); $data = []; foreach ($value as $dataSet) { $set = []; foreach ($dataSet as $i => $dataSetValue) { - $set[] = [$columns[$i], $dataSetValue, $modifiers[$i] ?? null]; + $set[] = [$columns[$i], $dataSetValue, $modifier[$i] ?? null]; } $data[] = $set; } return $expression->withArgs('NOT (%multiOr)', [$data]); } else { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; if ($modifier !== '%any') $modifier .= '[]'; return $expression->append("NOT IN $modifier", $value); } @@ -61,6 +59,8 @@ protected function evaluateInDb( } elseif ($value === null) { return $expression->append('IS NOT NULL'); } else { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append("!= $modifier", $value); } } diff --git a/src/Collection/Functions/CompareSmallerThanEqualsFunction.php b/src/Collection/Functions/CompareSmallerThanEqualsFunction.php index 1ea252a3..4150ef82 100644 --- a/src/Collection/Functions/CompareSmallerThanEqualsFunction.php +++ b/src/Collection/Functions/CompareSmallerThanEqualsFunction.php @@ -4,6 +4,8 @@ use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult; +use Nextras\Orm\Exception\InvalidArgumentException; +use function is_array; class CompareSmallerThanEqualsFunction extends BaseCompareFunction @@ -17,9 +19,11 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append("<= $modifier", $value); } } diff --git a/src/Collection/Functions/CompareSmallerThanFunction.php b/src/Collection/Functions/CompareSmallerThanFunction.php index cc3df3a9..84034f6e 100644 --- a/src/Collection/Functions/CompareSmallerThanFunction.php +++ b/src/Collection/Functions/CompareSmallerThanFunction.php @@ -4,6 +4,8 @@ use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult; +use Nextras\Orm\Exception\InvalidArgumentException; +use function is_array; class CompareSmallerThanFunction extends BaseCompareFunction @@ -17,9 +19,11 @@ protected function evaluateInPhp(mixed $sourceValue, mixed $targetValue): bool protected function evaluateInDb( DbalExpressionResult $expression, mixed $value, - string $modifier, + string|array|null $modifier, ): DbalExpressionResult { + if (is_array($modifier)) throw new InvalidArgumentException(); + $modifier = $modifier ?? '%any'; return $expression->append("< $modifier", $value); } } diff --git a/src/Collection/Functions/FetchPropertyFunction.php b/src/Collection/Functions/FetchPropertyFunction.php index 23bc951f..2ad482f8 100644 --- a/src/Collection/Functions/FetchPropertyFunction.php +++ b/src/Collection/Functions/FetchPropertyFunction.php @@ -202,7 +202,7 @@ private function processTokens( $propertyPrefixTokens = ""; $makeDistinct = false; - /** @var DbalTableJoin[] $joins */ + /** @var list $joins */ $joins = []; foreach ($tokens as $tokenIndex => $token) { @@ -242,7 +242,6 @@ private function processTokens( throw new InvalidArgumentException("Property expression '$propertyExpression' does not fetch specific property."); } - $modifier = ''; $column = $this->toColumnExpr( $currentEntityMetadata, $propertyMetadata, @@ -270,7 +269,7 @@ private function processTokens( /** * @param array $tokens - * @param DbalTableJoin[] $joins + * @param list $joins * @param Aggregator|null $aggregator * @param DbalMapper $currentMapper * @return array{string, IConventions, EntityMetadata, DbalMapper} @@ -368,6 +367,7 @@ private function processRelationship( /** + * @param literal-string|list|null $modifier * @return Fqn|list */ private function toColumnExpr( @@ -376,20 +376,21 @@ private function toColumnExpr( IConventions $conventions, string $alias, string $propertyPrefixTokens, - string &$modifier, + string|array|null &$modifier, ): Fqn|array { if ($propertyMetadata->isPrimary && $propertyMetadata->isVirtual) { // primary-proxy $primaryKey = $entityMetadata->getPrimaryKey(); if (count($primaryKey) > 1) { // composite primary key $pair = []; + /** @var list $modifiers */ $modifiers = []; foreach ($primaryKey as $columnName) { $columnName = $conventions->convertEntityToStorageKey($propertyPrefixTokens . $columnName); $pair[] = new Fqn(schema: $alias, name: $columnName); $modifiers[] = $conventions->getModifier($columnName); } - $modifier = implode(',', $modifiers); + $modifier = $modifiers; return $pair; } else { $propertyName = $primaryKey[0]; diff --git a/src/Collection/Functions/Result/DbalExpressionResult.php b/src/Collection/Functions/Result/DbalExpressionResult.php index 3ca87ccd..719dbe56 100644 --- a/src/Collection/Functions/Result/DbalExpressionResult.php +++ b/src/Collection/Functions/Result/DbalExpressionResult.php @@ -40,7 +40,7 @@ class DbalExpressionResult * @param bool $isHavingClause True if the expression represents HAVING clause instead of WHERE clause. * @param PropertyMetadata|null $propertyMetadata Reference to backing property of the expression. If null, the expression is no more a simple property expression. * @param (callable(mixed): mixed)|null $valueNormalizer Normalizes the value for better PHP comparison, it considers the backing property type. - * @param literal-string|null $dbalModifier Dbal modifier for particular column. Null if expression is a general expression. + * @param literal-string|list|null $dbalModifier Dbal modifier for particular column. Array if multi-column. Null value means expression is a general expression. */ public function __construct( public readonly string $expression, @@ -52,7 +52,7 @@ public function __construct( public readonly bool $isHavingClause = false, public readonly ?PropertyMetadata $propertyMetadata = null, ?callable $valueNormalizer = null, - public readonly ?string $dbalModifier = null, + public readonly string|array|null $dbalModifier = null, ) { $this->valueNormalizer = $valueNormalizer; From e8e7bcfc9675c720ac667256f1bbf389bc708632 Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Tue, 2 Apr 2024 18:10:15 +0200 Subject: [PATCH 3/4] check for invalid values for file dependencies --- src/Entity/Reflection/IMetadataParser.php | 2 +- src/Entity/Reflection/MetadataParser.php | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Entity/Reflection/IMetadataParser.php b/src/Entity/Reflection/IMetadataParser.php index 293cfb8f..09388988 100644 --- a/src/Entity/Reflection/IMetadataParser.php +++ b/src/Entity/Reflection/IMetadataParser.php @@ -10,5 +10,5 @@ interface IMetadataParser * @param class-string $entityClass * @param list|null $fileDependencies */ - public function parseMetadata(string $entityClass, ?array &$fileDependencies): EntityMetadata; + public function parseMetadata(string $entityClass, array|null &$fileDependencies): EntityMetadata; } diff --git a/src/Entity/Reflection/MetadataParser.php b/src/Entity/Reflection/MetadataParser.php index bc1afe38..24ca2bcc 100644 --- a/src/Entity/Reflection/MetadataParser.php +++ b/src/Entity/Reflection/MetadataParser.php @@ -91,7 +91,7 @@ public function addModifier(string $modifier, callable $processor) } - public function parseMetadata(string $entityClass, ?array &$fileDependencies): EntityMetadata + public function parseMetadata(string $entityClass, array|null &$fileDependencies): EntityMetadata { $this->reflection = new ReflectionClass($entityClass); $this->metadata = new EntityMetadata($entityClass); @@ -99,16 +99,17 @@ public function parseMetadata(string $entityClass, ?array &$fileDependencies): E $this->loadProperties($fileDependencies); $this->initPrimaryKey(); - $fileDependencies = array_unique($fileDependencies); + if ($fileDependencies !== null) { + $fileDependencies = array_values(array_unique($fileDependencies)); + } return $this->metadata; } /** - * @param string[] $fileDependencies - * @param list $fileDependencies + * @param list|null $fileDependencies */ - protected function loadProperties(?array &$fileDependencies): void + protected function loadProperties(array|null &$fileDependencies): void { $classTree = [$current = $this->reflection->name]; while (($current = get_parent_class($current)) !== false) { @@ -126,13 +127,15 @@ protected function loadProperties(?array &$fileDependencies): void foreach ($traits !== false ? $traits : [] as $traitName) { assert(trait_exists($traitName)); $reflectionTrait = new ReflectionClass($traitName); - $fileDependencies[] = $reflectionTrait->getFileName(); + $file = $reflectionTrait->getFileName(); + if ($file !== false) $fileDependencies[] = $file; $this->currentReflection = $reflectionTrait; $this->classPropertiesCache[$traitName] = $this->parseAnnotations($reflectionTrait, $methods); } $reflection = new ReflectionClass($class); - $fileDependencies[] = $reflection->getFileName(); + $file = $reflection->getFileName(); + if ($file !== false) $fileDependencies[] = $file; $this->currentReflection = $reflection; $this->classPropertiesCache[$class] = $this->parseAnnotations($reflection, $methods); } From 08bb9ffed3c15454d3d8e19c696a42c9f23ce805 Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Tue, 2 Apr 2024 19:44:05 +0200 Subject: [PATCH 4/4] fix types & phpdoc for RemovalHelper and reference arguments --- src/Repository/RemovalHelper.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Repository/RemovalHelper.php b/src/Repository/RemovalHelper.php index b293ce5e..88fbd9f4 100644 --- a/src/Repository/RemovalHelper.php +++ b/src/Repository/RemovalHelper.php @@ -19,8 +19,8 @@ class RemovalHelper { /** - * @param array> $queuePersist - * @param array $queueRemove + * @param array|IRelationshipCollection> $queuePersist + * @param array $queueRemove */ public static function getCascadeQueueAndSetNulls( IEntity $entity, @@ -51,7 +51,7 @@ public static function getCascadeQueueAndSetNulls( foreach ($prePersist as $value) { $queuePersist[spl_object_id($value)] = $value; } - $queueRemove[$entityHash] = true; + $queueRemove[$entityHash] = $entity; foreach ($pre as $value) { if ($value instanceof IEntity) { static::getCascadeQueueAndSetNulls($value, $model, true, $queuePersist, $queueRemove); @@ -62,6 +62,7 @@ public static function getCascadeQueueAndSetNulls( $queuePersist[spl_object_id($value)] = $value; } } + // re-enqueue to be at the last position unset($queueRemove[$entityHash]); $queueRemove[$entityHash] = $entity; unset($queuePersist[$entityHash]); @@ -126,7 +127,7 @@ public static function getRelationships(IEntity $entity): array /** * @param PropertyMetadata[] $metadata - * @param array> $pre + * @param list|IRelationshipCollection> $pre * @param array $queueRemove */ private static function setNulls( @@ -154,12 +155,15 @@ private static function setNulls( : null; if ($type === Relationship::MANY_HAS_MANY) { + /** @var ManyHasMany $property */ $property = $entity->getProperty($name); assert($property instanceof ManyHasMany); $pre[] = $property; if ($reverseProperty !== null) { foreach ($property as $reverseEntity) { - $pre[] = $reverseEntity->getProperty($reverseProperty->name); + /** @var ManyHasMany $reverseRelationship */ + $reverseRelationship = $reverseEntity->getProperty($reverseProperty->name); + $pre[] = $reverseRelationship; } } $property->set([]); @@ -172,7 +176,9 @@ private static function setNulls( // The reverse side is also being removed, do not set null to this relationship. continue; } - $pre[] = $reverseEntity->getProperty($reverseProperty->name); + /** @var HasOne $reverseRelationship */ + $reverseRelationship = $reverseEntity->getProperty($reverseProperty->name); + $pre[] = $reverseRelationship; $pre[] = $reverseEntity; } $property->set(null, true); @@ -184,6 +190,7 @@ private static function setNulls( $property = $entity->getProperty($name); assert($property instanceof IRelationshipCollection); foreach ($property as $subValue) { + assert($subValue instanceof IEntity); $pre[] = $subValue; } $property->set([]);