diff --git a/composer.json b/composer.json index e1d673d..8c42e1e 100644 --- a/composer.json +++ b/composer.json @@ -29,11 +29,11 @@ "codeception/module-asserts": "^3.0", "codeception/module-doctrine": "^3.1", "codeception/module-symfony": "*", - "doctrine/dbal": "^3.4", + "doctrine/dbal": "^3.4|^4.0", "doctrine/doctrine-bundle": "^2.4", "doctrine/lexer": "^1.2|^2.0|^3.0", "doctrine/orm": "^2.13|^3.0", - "doctrine/persistence": "^2.0|^3.0", + "doctrine/persistence": "^2.0|^3.0|^4.0", "fsi/files": "^2.0.4|^3.0", "guzzlehttp/psr7": "^2.0", "monolog/monolog": "^1.25", @@ -41,12 +41,12 @@ "oneup/flysystem-bundle": "^4.4", "php-http/guzzle7-adapter": "^1.0", "php-http/httplug-bundle": "^1.20", - "phpstan/phpstan": "^1.10.39", - "phpstan/phpstan-beberlei-assert": "^1.0", - "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan": "^2.1.46", + "phpstan/phpstan-beberlei-assert": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^9.5", "psr/http-client": "^1.0", - "squizlabs/php_codesniffer": "^3.7", + "squizlabs/php_codesniffer": "^4.0", "symfony/asset": "^4.4.30|^5.4|^6.0|^7.0", "symfony/clock": "^6.4|^7.0", "symfony/config": "^4.4.30|^5.4|^6.0|^7.0", diff --git a/phpstan.neon b/phpstan.neon index 0dca399..42ad403 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,6 +12,7 @@ parameters: excludePaths: - 'tests/_support/_generated' - 'tests/_support/project/var' + - 'tests/_support/project/config' - 'tests/_support/UnitTester.php' ignoreErrors: - @@ -26,7 +27,27 @@ parameters: message: '#^Call to an undefined method Doctrine\\ORM\\Event\\\w+::getEntityManager\(\)\.$#' path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' reportUnmatched: false + - + message: '#^Call to function method_exists\(\) with Doctrine\\ORM\\Event\\\w+ and ''getObjectManager'' will always evaluate to true\.$#' + path: 'src/Integration/Doctrine/ORM/Subscriber/EntitySubscriber.php' + reportUnmatched: false - message: '#.*Symfony\\Component\\Routing\\RouteCollectionBuilder.*#' path: '%currentWorkingDirectory%/tests/_support/project/src/Kernel.php' reportUnmatched: false + - + message: '#Doctrine\\ORM\\Mapping\\EmbeddedClassMapping( not found)?#' + path: '%currentWorkingDirectory%/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php' + reportUnmatched: false + - + identifier: 'trait.unused' + path: '%currentWorkingDirectory%/src/Integration/Doctrine/ORM/ProxyTrait.php' + reportUnmatched: false + - + identifier: 'property.unusedType' + path: 'tests/_support/project/src/Entity' + reportUnmatched: false + - + identifier: 'arrayValues.list' + path: 'tests/_support/Module/DbModule.php' + reportUnmatched: false diff --git a/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php b/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php index ffba185..65099b2 100644 --- a/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php +++ b/src/Integration/Doctrine/ORM/Entity/EmptyEmbeddableVerifier.php @@ -13,6 +13,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\EmbeddedClassMapping; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -21,6 +22,9 @@ use function mb_strlen; use function uksort; +/** + * @phpstan-type EmbeddedMappingArray array{class: class-string, declaredField?: string|null, originalField: string} + */ final class EmptyEmbeddableVerifier { private static ?PropertyAccessorInterface $propertyAccessor = null; @@ -36,27 +40,41 @@ public static function areAllEmpty(EntityManagerInterface $manager, object $tran $allEmpty = true; foreach ($embeddedClassesData as $fieldName => $embeddedData) { /** @var class-string $embeddableClass */ - $embeddableClass = $embeddedData['class']; + $embeddableClass = (true === $embeddedData instanceof EmbeddedClassMapping) + ? $embeddedData->class + : $embeddedData['class']; $embeddedMeta = $manager->getClassMetadata($embeddableClass); $embeddedParentMeta = $classMetadata; $embeddedParentObject = $translation; + $embeddedDeclaredField = (true === $embeddedData instanceof EmbeddedClassMapping) + ? $embeddedData->declaredField + : ($embeddedData['declaredField'] ?? null); - if (true === isset($embeddedData['declaredField'])) { + if (null !== $embeddedDeclaredField) { $embeddedParentObject = self::getPropertyAccessor()->getValue( $translation, - $embeddedData['declaredField'] + $embeddedDeclaredField ); if (null === $embeddedParentObject) { continue; } - $embeddedParentData = $embeddedClassesData[$embeddedData['declaredField']]; - $embeddedParentMeta = $manager->getClassMetadata($embeddedParentData['class']); - $fieldName = $embeddedData['originalField']; + $embeddedParentData = $embeddedClassesData[$embeddedDeclaredField]; + /** @var class-string $embeddedParentClass */ + $embeddedParentClass = (true === $embeddedParentData instanceof EmbeddedClassMapping) + ? $embeddedParentData->class + : $embeddedParentData['class']; + $embeddedParentMeta = $manager->getClassMetadata($embeddedParentClass); + $fieldName = (true === $embeddedData instanceof EmbeddedClassMapping) + ? $embeddedData->originalField + : $embeddedData['originalField']; } - $embeddedObject = $embeddedParentMeta->getFieldValue($embeddedParentObject, $fieldName); + $embeddedObject = null; + if (null !== $fieldName) { + $embeddedObject = $embeddedParentMeta->getFieldValue($embeddedParentObject, $fieldName); + } if (null === $embeddedObject) { continue; } @@ -78,11 +96,14 @@ public static function getEmbeddableClasses(EntityManagerInterface $manager, obj $embeddedClassesData = self::getAndSortEmbeddedDataByNestingLevel( self::getTranslationMetadata($manager, $translation) ); - - return array_map( - static fn($embeddedData): string => $embeddedData['class'], - $embeddedClassesData - ); + /** @var callable(EmbeddedClassMapping|EmbeddedMappingArray): class-string $embeddedMappingMapper */ + $embeddedMappingMapper = static function (EmbeddedClassMapping|array $embeddedData): string { + return (true === $embeddedData instanceof EmbeddedClassMapping) + ? $embeddedData->class + : $embeddedData['class']; + }; + + return array_map($embeddedMappingMapper, $embeddedClassesData); } /** @@ -109,11 +130,11 @@ static function (bool $accumulator, string $fieldName) use ($classMetadata, $emb /** * @param ClassMetadata $classMetadata - * @return array + * @return array */ private static function getAndSortEmbeddedDataByNestingLevel(ClassMetadata $classMetadata): array { - /** @var array $data */ + /** @var array $data */ $data = $classMetadata->embeddedClasses; // Example data ["fieldName" => [], "fieldName.nestedFieldName" => []] uksort( diff --git a/src/Integration/Symfony/TranslatableBundle.php b/src/Integration/Symfony/TranslatableBundle.php index c51f686..1a23789 100644 --- a/src/Integration/Symfony/TranslatableBundle.php +++ b/src/Integration/Symfony/TranslatableBundle.php @@ -34,7 +34,7 @@ public function build(ContainerBuilder $container): void } } - public function getContainerExtension(): ?ExtensionInterface + public function getContainerExtension(): ExtensionInterface { if (false === $this->extension instanceof TranslatableExtension) { $this->extension = new TranslatableExtension(); diff --git a/src/PropertyConfiguration.php b/src/PropertyConfiguration.php index 7c98c8b..5889ad2 100644 --- a/src/PropertyConfiguration.php +++ b/src/PropertyConfiguration.php @@ -121,9 +121,6 @@ private function getPropertyReflection(): ReflectionProperty } $this->propertyReflection = $reflectionClass->getProperty($this->propertyName); - if (PHP_VERSION_ID < 80500) { - $this->propertyReflection->setAccessible(true); - } } while ($reflectionClass = $reflectionClass->getParentClass()); } @@ -140,11 +137,7 @@ private function getPropertyReflection(): ReflectionProperty */ private function handleUninitializedProperty(ReflectionProperty $propertyReflection) { - if ( - true === method_exists($propertyReflection, 'hasDefaultValue') - && true === method_exists($propertyReflection, 'getDefaultValue') - && true === $propertyReflection->hasDefaultValue() - ) { + if (true === $propertyReflection->hasDefaultValue()) { return $propertyReflection->getDefaultValue(); } diff --git a/tests/_support/Module/DbModule.php b/tests/_support/Module/DbModule.php index 953160e..20ae529 100644 --- a/tests/_support/Module/DbModule.php +++ b/tests/_support/Module/DbModule.php @@ -36,7 +36,7 @@ public function _beforeSuite($settings = []): void /** @var EntityManagerInterface $manager */ $manager = $symfony->_getEntityManager(); - (new SchemaTool($manager))->createSchema($manager->getMetadataFactory()->getAllMetadata()); + (new SchemaTool($manager))->createSchema(array_values($manager->getMetadataFactory()->getAllMetadata())); } /** diff --git a/tests/_support/project/src/Kernel.php b/tests/_support/project/src/Kernel.php index ded800d..a82c26d 100644 --- a/tests/_support/project/src/Kernel.php +++ b/tests/_support/project/src/Kernel.php @@ -64,7 +64,7 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa { $configDirectory = $this->getProjectDir() . '/config'; $container->addResource(new FileResource($configDirectory . '/bundles.php')); - $container->setParameter('container.dumper.inline_class_loader', PHP_VERSION_ID < 70400 || $this->debug); + $container->setParameter('container.dumper.inline_class_loader', $this->debug); $container->setParameter('container.dumper.inline_factories', true); if (InstalledVersions::getVersion('symfony/framework-bundle') >= '5.0.0') { diff --git a/tests/_support/project/templates/Form/formTheme.html.twig b/tests/_support/project/templates/Form/formTheme.html.twig index eb71cdd..7bc90fc 100644 --- a/tests/_support/project/templates/Form/formTheme.html.twig +++ b/tests/_support/project/templates/Form/formTheme.html.twig @@ -25,10 +25,8 @@ {% endblock %} {% block collection_prototype -%} - {% apply spaceless %} - {% set form = prototype %} - {{ block('collection_item') }} - {% endapply %} + {% set form = prototype %} + {{ block('collection_item') }} {%- endblock %} {% block collection_item %}