diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e76475b..b494d8d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,6 +1,13 @@ parameters: ignoreErrors: - - message: "#^Throwing checked exception InvalidArgumentException in yielding method is denied as it gets thrown upon Generator iteration$#" + message: '#^Call to an undefined method object\:\:getId\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Serializer/LogSerializer.php + + - + message: '#^Throwing checked exception InvalidArgumentException in yielding method is denied as it gets thrown upon Generator iteration$#' + identifier: shipmonk.checkedExceptionInYieldingMethod count: 2 path: tests/Serializer/LogSerializerTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 381c1b2..c65ef82 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,4 @@ parameters: - level: 5 paths: - src/ - tests/ diff --git a/src/Entity/Log.php b/src/Entity/Log.php index df4d3a8..cb46968 100755 --- a/src/Entity/Log.php +++ b/src/Entity/Log.php @@ -23,7 +23,7 @@ public function __construct( ) { $this->entityClass = $entity::class; $this->entityColumn = $entityColumn; - $this->entityOldValue = mb_substr($entityOldValue, 0, Log::MAX_STRING_LENGTH); + $this->entityOldValue = $entityOldValue !== null ? mb_substr($entityOldValue, 0, Log::MAX_STRING_LENGTH) : null; $this->requestTrace = $requestTrace; $this->createdAt = new DateTimeImmutable(); } diff --git a/src/Exception/UnsupportObjectException.php b/src/Exception/UnsupportObjectException.php index 2c472f5..257a254 100644 --- a/src/Exception/UnsupportObjectException.php +++ b/src/Exception/UnsupportObjectException.php @@ -6,7 +6,7 @@ class UnsupportObjectException extends \DomainException { - public function __construct($object, $code = 0, ?\Throwable $previous = null) + public function __construct(object $object, int $code = 0, ?\Throwable $previous = null) { $message = sprintf('Unhandled object of class %s', $object::class); parent::__construct($message, $code, $previous); diff --git a/src/Factory/LogDataFactory.php b/src/Factory/LogDataFactory.php index aa006b7..1025203 100644 --- a/src/Factory/LogDataFactory.php +++ b/src/Factory/LogDataFactory.php @@ -11,6 +11,10 @@ /** @phpstan-type LogData array{entity: object, entityColumn: string, entityOldValue: ?string} */ class LogDataFactory { + /** + * @param list $includedEntities + * @param list $excludedEntities + */ public function __construct( private readonly LogSerializer $formatter, private readonly array $includedEntities, @@ -54,7 +58,7 @@ public function createFromEvent(OnFlushEventArgs $eventArgs): iterable } } - private function isLoggable($entity): bool + private function isLoggable(object $entity): bool { if ($this->isSubClassFromList($entity, $this->excludedEntities)) { return false; @@ -63,7 +67,10 @@ private function isLoggable($entity): bool return [] === $this->includedEntities || $this->isSubClassFromList($entity, $this->includedEntities); } - private function isSubClassFromList($entity, array $classes): bool + /** + * @param list $classes + */ + private function isSubClassFromList(object $entity, array $classes): bool { foreach ($classes as $class) { if (is_a($entity, $class)) { @@ -75,7 +82,7 @@ private function isSubClassFromList($entity, array $classes): bool } /** @return LogData[] */ - private function getLogsForEntityFields($entity, EntityManagerInterface $entityManager): iterable + private function getLogsForEntityFields(object $entity, EntityManagerInterface $entityManager): iterable { $unitOfWork = $entityManager->getUnitOfWork(); diff --git a/src/Serializer/LogSerializer.php b/src/Serializer/LogSerializer.php index 7071b82..bc8ec05 100755 --- a/src/Serializer/LogSerializer.php +++ b/src/Serializer/LogSerializer.php @@ -47,12 +47,12 @@ public function formatEntity(EntityManagerInterface $entityManager, object $enti } } - return json_encode($data, JSON_PRETTY_PRINT); + return (string) json_encode($data, JSON_PRETTY_PRINT); } - public function formatValueAsString($value): string + public function formatValueAsString(mixed $value): string { - return json_encode($this->formatValue($value)); + return (string) json_encode($this->formatValue($value)); } /** @@ -71,13 +71,13 @@ private function formatField(object $entity, string $field): mixed /** * Returns a formatted value depending on the given value's type. */ - private function formatValue($value): mixed + private function formatValue(mixed $value): mixed { return match (gettype($value)) { 'string' => mb_substr($value, 0, Log::MAX_STRING_LENGTH), 'NULL', 'boolean', 'double', 'integer' => $value, 'object' => $this->formatObject($value), - 'array' => array_map(__METHOD__, $value), + 'array' => array_map($this->formatValue(...), $value), 'resource', 'resource (closed)', 'unknown type' => throw new \InvalidArgumentException('Unhandled type'), }; diff --git a/tests/Factory/LogDataFactoryTest.php b/tests/Factory/LogDataFactoryTest.php index 95d39e6..8e4c0ba 100644 --- a/tests/Factory/LogDataFactoryTest.php +++ b/tests/Factory/LogDataFactoryTest.php @@ -19,6 +19,7 @@ class LogDataFactoryTest extends KernelTestCase public function testExcludedEntityIsIgnored(): void { $em = self::getContainer()->get(EntityManagerInterface::class); + self::assertInstanceOf(EntityManagerInterface::class, $em); $em->persist(new Author()); $em->getUnitOfWork()->computeChangeSets(); @@ -31,6 +32,7 @@ public function testExcludedEntityIsIgnored(): void public function testNewEntityIsLogged(): void { $em = self::getContainer()->get(EntityManagerInterface::class); + self::assertInstanceOf(EntityManagerInterface::class, $em); $em->persist($createdAuthor = new Author()); $em->getUnitOfWork()->computeChangeSets(); @@ -94,6 +96,7 @@ public function testUpdatedEntityIsLogged(): void private function mockEntityManager(): array { $emReal = self::getContainer()->get(EntityManagerInterface::class); + self::assertInstanceOf(EntityManagerInterface::class, $emReal); $em = $this->createMock(EntityManagerInterface::class); $unitOfWork = $this->createMock(UnitOfWork::class); diff --git a/tests/Functional/Entity/Address.php b/tests/Functional/Entity/Address.php index e5ea238..c5569e9 100755 --- a/tests/Functional/Entity/Address.php +++ b/tests/Functional/Entity/Address.php @@ -9,6 +9,7 @@ #[ORM\Entity] class Address extends AbstractEntity { + /** @phpstan-ignore property.unusedType */ private ?string $streetName = null; public function __construct() diff --git a/tests/Functional/Entity/FunctionalLog.php b/tests/Functional/Entity/FunctionalLog.php index c41f356..d3805bb 100755 --- a/tests/Functional/Entity/FunctionalLog.php +++ b/tests/Functional/Entity/FunctionalLog.php @@ -22,6 +22,7 @@ public function __construct( string $requestTrace, ) { $this->id = Uuid::uuid1(); + /** @phpstan-ignore method.notFound */ $this->entityId = (string) $entity->getId(); parent::__construct( $entity, diff --git a/tests/Functional/Entity/Post.php b/tests/Functional/Entity/Post.php index d21fcd9..da9c897 100755 --- a/tests/Functional/Entity/Post.php +++ b/tests/Functional/Entity/Post.php @@ -21,7 +21,7 @@ public function __construct( } #[Assert\NotBlank] - protected $title; + protected ?string $title = null; #[ORM\ManyToOne(targetEntity: Author::class, inversedBy: 'posts')] protected Author $author; @@ -31,9 +31,11 @@ public function getAuthor(): Author return $this->author; } + /** @var ArrayCollection */ #[ORM\ManyToMany(targetEntity: Tag::class, inversedBy: 'posts')] protected ArrayCollection $tags; + /** @return Collection */ public function getTags(): Collection { return $this->tags; diff --git a/tests/Functional/Entity/Tag.php b/tests/Functional/Entity/Tag.php index 04cd666..7e589ec 100755 --- a/tests/Functional/Entity/Tag.php +++ b/tests/Functional/Entity/Tag.php @@ -16,6 +16,7 @@ public function __construct() $this->posts = new ArrayCollection(); } + /** @var ArrayCollection */ #[ORM\ManyToMany(Post::class, 'tags')] protected ArrayCollection $posts; diff --git a/tests/Serializer/LogSerializerTest.php b/tests/Serializer/LogSerializerTest.php index 45eff35..0db8f62 100755 --- a/tests/Serializer/LogSerializerTest.php +++ b/tests/Serializer/LogSerializerTest.php @@ -34,8 +34,10 @@ public function testFormatEntity(): void $post->addTag($tag); $entityManager = self::getContainer()->get(EntityManagerInterface::class); + self::assertInstanceOf(EntityManagerInterface::class, $entityManager); $formatter = self::getContainer()->get(LogSerializer::class); + self::assertInstanceOf(LogSerializer::class, $formatter); self::assertSame( json_encode(array_merge( $this->helperFormatEntity($author), @@ -64,6 +66,7 @@ public function testFormatEntity(): void ); } + /** @return array */ public function helperFormatEntity(AbstractEntity $entity): array { return [ @@ -76,13 +79,13 @@ public function helperFormatEntity(AbstractEntity $entity): array /** * @dataProvider providerFormatValueAsString */ - public function testFormatValueAsStringWorks($value, $formatted): void + public function testFormatValueAsStringWorks(mixed $value, string $formatted): void { $formatter = new LogSerializer(); self::assertSame($formatted, $formatter->formatValueAsString($value)); } - public function providerFormatValueAsString(): iterable + public static function providerFormatValueAsString(): iterable { yield [null, 'null']; yield [[null], '[null]']; diff --git a/tests/Subscriber/LoggerSubscriberTest.php b/tests/Subscriber/LoggerSubscriberTest.php index a64405b..17a91dc 100755 --- a/tests/Subscriber/LoggerSubscriberTest.php +++ b/tests/Subscriber/LoggerSubscriberTest.php @@ -66,7 +66,7 @@ public function testSubscriberPersistsLogs(): void $subscriber = new LoggerSubscriber( $logFactory, $logDataFactory, - realpath(__DIR__ . '/../..') + (string) realpath(__DIR__ . '/../..') ); $subscriber->onFlush($event);