diff --git a/composer.json b/composer.json index fa2ec60..73f58ee 100755 --- a/composer.json +++ b/composer.json @@ -35,20 +35,21 @@ "symfony/validator": "^7.0", "phpstan/phpstan-symfony": "^2", "assoconnect/php-quality-config": "^2.2", - "symfony/security-core": "^7.0" + "symfony/security-core": "^7.0", + "symfony/var-exporter": "^7.0" }, "require": { "php": "^8.4", "symfony/framework-bundle": "^7.0", - "doctrine/dbal": "^2.10|^3.0", + "doctrine/dbal": "^3.6|^4.0", "symfony/serializer": "^7.0", "symfony/property-access": "^7.0", - "doctrine/doctrine-bundle": "^2.11", - "doctrine/orm": "^2.9", + "doctrine/doctrine-bundle": "^2.12", + "doctrine/orm": "^2.20|^3.0", "moneyphp/money": "^3.2|^4.0", "ext-json": "*", "ramsey/uuid": "^4.3", - "ramsey/uuid-doctrine": "^1.4", + "ramsey/uuid-doctrine": "^1.4|^2.0", "thecodingmachine/safe": "^3.4" }, "config": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c65ef82..d761438 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,6 +7,12 @@ parameters: - 'Doctrine\Persistence\Mapping\MappingException' - 'ReflectionException' + # doctrine/common (ClassUtils) ships with ORM 2 only (^2.20|^3.0 are both supported), + # so each analysis run only matches part of the errors around it. + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - '#unknown class Doctrine\\Common\\Util\\ClassUtils#' + includes: - vendor/assoconnect/php-quality-config/phpstan.extension.neon - vendor/phpstan/phpstan-symfony/extension.neon diff --git a/rector.php b/rector.php index 391f508..62a7f69 100644 --- a/rector.php +++ b/rector.php @@ -14,4 +14,9 @@ ->withTypeCoverageLevel(0) ->withSets([ __DIR__ . '/vendor/assoconnect/php-quality-config/src/Rector/rules.php', + ]) + // Registered by php-quality-config but deprecated: rector >= 2.5.7 aborts when they match + ->withSkip([ + \Rector\CodingStyle\Rector\ArrowFunction\StaticArrowFunctionRector::class, + \Rector\CodingStyle\Rector\Closure\StaticClosureRector::class, ]); diff --git a/src/Entity/Log.php b/src/Entity/Log.php index 1023af2..ac6d937 100755 --- a/src/Entity/Log.php +++ b/src/Entity/Log.php @@ -7,6 +7,7 @@ use DateTimeImmutable; use Doctrine\Common\Util\ClassUtils; use Doctrine\ORM\Mapping as ORM; +use Doctrine\Persistence\Proxy; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Validator\Constraints as Assert; @@ -22,7 +23,7 @@ public function __construct( ?string $entityOldValue, string $requestTrace, ) { - $this->entityClass = ClassUtils::getRealClass($entity::class); + $this->entityClass = self::getRealClass($entity); $this->entityColumn = $entityColumn; $this->entityOldValue = $entityOldValue !== null ? mb_substr($entityOldValue, 0, Log::MAX_STRING_LENGTH) : null; $this->requestTrace = $requestTrace; @@ -127,4 +128,28 @@ public function getCreatedAt(): DateTimeImmutable { return $this->createdAt; } + + /** + * doctrine/common (ClassUtils) is no longer installed with ORM 3: its proxies implement + * Persistence\Proxy, and native lazy objects (ORM 3.5+) already report the real class. + * Excluded from coverage: only one branch can run for a given installed ORM major. + * + * @codeCoverageIgnore + * @return class-string + */ + private static function getRealClass(object $entity): string + { + if (class_exists(ClassUtils::class)) { + return ClassUtils::getRealClass($entity::class); + } + + if ($entity instanceof Proxy) { + $parent = get_parent_class($entity); + if (false !== $parent) { + return $parent; + } + } + + return $entity::class; + } } diff --git a/src/LogBundle.php b/src/LogBundle.php index 6f830d0..837fa3e 100755 --- a/src/LogBundle.php +++ b/src/LogBundle.php @@ -5,22 +5,10 @@ namespace AssoConnect\LogBundle; use AssoConnect\LogBundle\DependencyInjection\LogExtension; -use Doctrine\Common\EventSubscriber; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class LogBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function build(ContainerBuilder $container): void - { - parent::build($container); - $container->registerForAutoconfiguration(EventSubscriber::class) - ->addTag('doctrine.event_subscriber'); - } - public function getContainerExtension(): LogExtension { return new LogExtension(); diff --git a/tests/Factory/LogDataFactoryTest.php b/tests/Factory/LogDataFactoryTest.php index 8e4c0ba..a428315 100644 --- a/tests/Factory/LogDataFactoryTest.php +++ b/tests/Factory/LogDataFactoryTest.php @@ -70,7 +70,7 @@ public function testUpdatedEntityIsLogged(): void ->willReturn([$updatedAuthor = new Author()]); $unitOfWork->expects(self::once())->method('getScheduledEntityDeletions')->willReturn([]); - $unitOfWork->method('getEntityChangeSet')->with($updatedAuthor)->willReturn( + $unitOfWork->expects(self::once())->method('getEntityChangeSet')->with($updatedAuthor)->willReturn( [ 'email' => ['test@gmail.com'], 'registeredAt' => [new \DateTimeImmutable('2020-10-06')], diff --git a/tests/Serializer/LogSerializerTest.php b/tests/Serializer/LogSerializerTest.php index 85d0e59..2d1ef5d 100755 --- a/tests/Serializer/LogSerializerTest.php +++ b/tests/Serializer/LogSerializerTest.php @@ -17,7 +17,8 @@ use Doctrine\ORM\EntityManagerInterface; use Money\Currency; use Money\Money; -use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase as KernelTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class LogSerializerTest extends KernelTestCase { @@ -76,9 +77,7 @@ public function helperFormatEntity(AbstractEntity $entity): array ]; } - /** - * @dataProvider providerFormatValueAsString - */ + #[DataProvider('providerFormatValueAsString')] public function testFormatValueAsStringWorks(mixed $value, string $formatted): void { $formatter = new LogSerializer();