diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 8ea2727..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1,3 +0,0 @@ -coverage_clover: Tests/coverage/clover.xml -json_path: Tests/coverage/coveralls-upload.json -exclude_no_stmt: true diff --git a/.php_cs b/.php-cs-fixer.php similarity index 96% rename from .php_cs rename to .php-cs-fixer.php index cb74018..a52d786 100644 --- a/.php_cs +++ b/.php-cs-fixer.php @@ -218,8 +218,7 @@ private function pipedExec(string $command, array &$output = null, int &$returnV } } -/* Based on dev-master|^2.0 of php-cs-fixer */ -return Config::create() +return (new Config()) ->setUsingCache(true) ->setRiskyAllowed(true) ->setRules([ @@ -233,17 +232,21 @@ private function pipedExec(string $command, array &$output = null, int &$returnV 'no_blank_lines_before_namespace' => false, 'ordered_imports' => true, 'phpdoc_align' => false, - 'phpdoc_inline_tag' => false, + 'general_phpdoc_tag_rename' => false, 'phpdoc_order' => true, 'simplified_null_return' => false, - 'binary_operator_spaces' => [ - 'align_double_arrow' => false, - 'align_equals' => false - ], 'no_unused_imports' => true, 'declare_strict_types' => true, 'final_internal_class' => false, - 'general_phpdoc_annotation_remove' => ['author', 'copyright', 'category', 'version'], + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author', + 'copyright', + 'category', + 'version', + ], + 'case_sensitive' => false, + ], 'global_namespace_import' => ['import_classes' => null], 'list_syntax' => ['syntax' => 'short'], 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], // according to the documentation this is the default, but it ain't diff --git a/.travis.yml b/.travis.yml index 30d1173..e5ec67b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,13 +7,13 @@ cache: jobs: include: + - php: 7.2 + env: SYMFONY_VERSION=~5.4 - php: 7.4 - env: SYMFONY_VERSION=~4.4 - - php: 7.4 - env: SYMFONY_VERSION=~5.1 + env: SYMFONY_VERSION=~5.4 - dist: "jammy" php: 8.2 - env: SYMFONY_VERSION=~5.1 + env: SYMFONY_VERSION=~5.4 fast_finish: true before_install: @@ -21,6 +21,8 @@ before_install: - php composer-setup.php --version=1.10.22 - php -r "unlink('composer-setup.php');" - sudo mv composer.phar /usr/local/bin/composer + - curl -Os https://uploader.codecov.io/latest/linux/codecov + - chmod +x codecov install: - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; @@ -30,7 +32,5 @@ install: script: - vendor/bin/rector process --dry-run --no-progress-bar --ansi - vendor/bin/phpstan analyze --no-progress --ansi - - php vendor/bin/phpunit - -after_script: - - php vendor/bin/coveralls -v \ No newline at end of file + - XDEBUG_MODE=coverage php vendor/bin/phpunit + - ./codecov diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/README.md b/README.md index 193906d..93642ef 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ Bowl Of Soup Normalizer ===== [![Build Status](https://travis-ci.org/BowlOfSoup/NormalizerBundle.svg?branch=master)](https://travis-ci.org/BowlOfSoup/NormalizerBundle) -[![Coverage Status](https://coveralls.io/repos/github/BowlOfSoup/NormalizerBundle/badge.svg?branch=master)](https://coveralls.io/github/BowlOfSoup/NormalizerBundle?branch=master) +[![codecov](https://codecov.io/gh/BowlOfSoup/NormalizerBundle/branch/master/graph/badge.svg?token=2OW4EWvMUD)](https://codecov.io/gh/BowlOfSoup/NormalizerBundle) [![PHP Version](https://img.shields.io/badge/php-7.2.x%20--%208.2.x-blue.svg)](https://www.php.net/) -[![Symfony Version](https://img.shields.io/badge/symfony-4.4.x%20--%205.1.x-blue.svg)](https://symfony.com/) - +[![Symfony Version](https://img.shields.io/badge/symfony-5.4.x-blue.svg)](https://symfony.com/) Installation ----- @@ -52,3 +51,39 @@ Why use this normalizer and not ... - It's designed with speed in mind. Not packed with features for which you don't use half of it - It has proven itself in a complex application with 15.000+ daily end users +Development +----- +The following CI tools can be used to check for code quality before pushing code: + +### Rector +Rector can be used to automated code upgrades and refactoring. Try a dry-run first! +```bash +vendor/bin/rector process --dry-run --no-progress-bar --ansi +``` + +### PHPStan +PHPStan is a static code analysis tool that focuses on finding errors in the code. +Fixing the outcome of PHPStan prevents possible bugs and errors. +```bash +vendor/bin/phpstan +``` + +### PHPUnit +Speaks for itself, code should be tested. Run with coverage (output = tests/coverage): +```bash +XDEBUG_MODE=coverage php -dzend_extension=xdebug.so vendor/bin/phpunit +``` +Or without coverage: +```bash +vendor/bin/phpunit +``` + +**Code coverage** `master`: + + + +### Code style fixer +Have php-cs-fixer automatically fix styling. +```bash +vendor/bin/php-cs-fixer fix +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 431761b..1b495e8 100644 --- a/composer.json +++ b/composer.json @@ -15,21 +15,19 @@ "ext-simplexml": "*", "ext-libxml": "*", "ext-dom": "*", - "doctrine/annotations": "1.*", + "doctrine/annotations": "~1.13.0", "doctrine/cache": "~1.0", "php": ">=7.2", - "symfony/framework-bundle": "~4.4|~5.0", - "symfony/translation": "~4.4|~5.0" + "symfony/framework-bundle": "~5.4", + "symfony/translation": "~5.4" }, "require-dev": { - "doctrine/common": "~2.12", + "doctrine/common": "~3.0", "doctrine/collections": "1.*", - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.5", - "friendsofphp/php-cs-fixer": "^2.10", + "phpunit/phpunit": "^8.0", + "friendsofphp/php-cs-fixer": "^3.0", "rector/rector": "^0.18.6", "phpstan/phpstan": "^1.10", - "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-symfony": "^1.3" }, "autoload": { @@ -48,7 +46,7 @@ }, "symfony": { "allow-contrib": false, - "require": "4.4.* || 5.1.*" + "require": "5.4.*" } } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d9714f5..6a69d63 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 1 + level: 3 paths: - 'src/' - 'tests/' @@ -7,5 +7,4 @@ parameters: tmpDir: var/cache/phpstan includes: - - vendor/phpstan/phpstan-mockery/extension.neon - vendor/phpstan/phpstan-symfony/extension.neon diff --git a/phpunit.xml b/phpunit.xml index aaf03ad..384633c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,30 +1,30 @@ - - - tests - - + + + tests + + - - - src - - BowlOfSoupNormalizerBundle.php - src/DependencyInjection - src/EventListener - src/Exception - src/Model - src/Resources - tests - vendor - - - + + + src + + src/BowlOfSoupNormalizerBundle.php + src/DependencyInjection + src/EventListener + src/Exception + src/Model + src/Resources + tests + vendor + + + - - - - - + + + + + diff --git a/rector.php b/rector.php index 01878f6..236dc40 100644 --- a/rector.php +++ b/rector.php @@ -19,7 +19,7 @@ $rectorConfig->sets([ LevelSetList::UP_TO_PHP_72, - SymfonyLevelSetList::UP_TO_SYMFONY_51, + SymfonyLevelSetList::UP_TO_SYMFONY_54, ]); $rectorConfig->importNames(true, false); diff --git a/src/Annotation/AbstractAnnotation.php b/src/Annotation/AbstractAnnotation.php index f8aef1f..9e807cf 100644 --- a/src/Annotation/AbstractAnnotation.php +++ b/src/Annotation/AbstractAnnotation.php @@ -1,5 +1,7 @@ hasCorrectType($propertyOptions['type'], $property) + if (isset($propertyOptions['type']) + && !$this->hasCorrectType($propertyOptions['type'], $property) ) { throw new \InvalidArgumentException(sprintf(static::EXCEPTION_TYPE, $propertyName, $annotation)); } - if (isset($propertyOptions['assert']) && - !$this->hasValidAssertion($propertyOptions['assert'], $property) + if (isset($propertyOptions['assert']) + && !$this->hasValidAssertion($propertyOptions['assert'], $property) ) { throw new \InvalidArgumentException(sprintf(static::EXCEPTION_TYPE_SUPPORTED, $property, $annotation)); } diff --git a/src/Annotation/Normalize.php b/src/Annotation/Normalize.php index e1d071d..eb7727f 100644 --- a/src/Annotation/Normalize.php +++ b/src/Annotation/Normalize.php @@ -1,11 +1,14 @@ ['type' => 'integer'], ]; - /** @var string */ - private $name; + /** @var string|null */ + private $name = null; - /** @var string */ - private $format; + /** @var string|null */ + private $format = null; - /** @var string */ - private $callback; + /** @var string|null */ + private $callback = null; /** @var bool */ private $normalizeCallbackResult = false; @@ -37,11 +40,11 @@ class Normalize extends AbstractAnnotation /** @var bool */ private $skipEmpty = false; - /** @var int */ - private $maxDepth; + /** @var int|null */ + private $maxDepth = null; - /** @var string */ - protected $type; + /** @var string|null */ + protected $type = null; public function __construct(array $properties) { @@ -95,7 +98,7 @@ public function getMaxDepth(): ?int return $this->maxDepth; } - public function getType(): string + public function getType(): ?string { return $this->type; } diff --git a/src/Annotation/Serialize.php b/src/Annotation/Serialize.php index be13e86..cfeaf1d 100644 --- a/src/Annotation/Serialize.php +++ b/src/Annotation/Serialize.php @@ -1,24 +1,27 @@ ['type' => 'array'], 'wrapElement' => ['type' => 'string'], - 'sortProperties' => ['type' => 'boolean'] + 'sortProperties' => ['type' => 'boolean'], ]; - /** @var string */ - private $wrapElement; + /** @var string|null */ + private $wrapElement = null; /** @var bool */ private $sortProperties = false; diff --git a/src/Annotation/Translate.php b/src/Annotation/Translate.php index 868f9fa..53e7ba0 100644 --- a/src/Annotation/Translate.php +++ b/src/Annotation/Translate.php @@ -6,11 +6,12 @@ /** * @Annotation + * * @Target({"PROPERTY","METHOD"}) */ class Translate extends AbstractAnnotation { - /** @var array */ + /** @var array|array[] */ private $supportedProperties = [ 'group' => ['type' => 'array'], 'domain' => ['type' => 'string'], @@ -18,10 +19,10 @@ class Translate extends AbstractAnnotation ]; /** @var string|null */ - private $domain; + private $domain = null; - /** @var string|null */ - private $locale; + /** @var null */ + private $locale = null; public function __construct(array $properties) { diff --git a/src/DependencyInjection/BowlOfSoupNormalizerExtension.php b/src/DependencyInjection/BowlOfSoupNormalizerExtension.php index 8f40f12..b8b3ceb 100644 --- a/src/DependencyInjection/BowlOfSoupNormalizerExtension.php +++ b/src/DependencyInjection/BowlOfSoupNormalizerExtension.php @@ -1,5 +1,7 @@ parameterRegisterAnnotations = $parameterRegisterAnnotations; } - /** - * @inheritdoc - */ public static function getSubscribedEvents(): array { return [ diff --git a/src/Exception/BosNormalizerException.php b/src/Exception/BosNormalizerException.php index 0ed807b..45099a6 100644 --- a/src/Exception/BosNormalizerException.php +++ b/src/Exception/BosNormalizerException.php @@ -1,5 +1,7 @@ wrapElement = $wrapElement; } - /** - * @inheritdoc - */ public function populateFromAnnotation(Serialize $serializeAnnotation): void { $this->wrapElement = $serializeAnnotation->getWrapElement(); diff --git a/src/Service/Encoder/EncoderFactory.php b/src/Service/Encoder/EncoderFactory.php index c4e7ff6..f93a1da 100644 --- a/src/Service/Encoder/EncoderFactory.php +++ b/src/Service/Encoder/EncoderFactory.php @@ -1,5 +1,7 @@ ' . '<' . $this->wrapElement . '>wrapElement . '>' + '<' . $this->wrapElement . '>wrapElement . '>' ); try { diff --git a/src/Service/Extractor/AnnotationExtractor.php b/src/Service/Extractor/AnnotationExtractor.php index 4d90999..3cb6abf 100644 --- a/src/Service/Extractor/AnnotationExtractor.php +++ b/src/Service/Extractor/AnnotationExtractor.php @@ -25,7 +25,7 @@ class AnnotationExtractor public function __construct(string $cacheDir = null, bool $debugMode = false) { if (null !== $cacheDir) { - $cacheDir = $cacheDir . '/annotations'; + $cacheDir .= '/annotations'; $this->createDirectory($cacheDir); if ($this->directoryExits($cacheDir)) { diff --git a/src/Service/Extractor/MethodExtractor.php b/src/Service/Extractor/MethodExtractor.php index 0bf2b8b..aaa0dab 100644 --- a/src/Service/Extractor/MethodExtractor.php +++ b/src/Service/Extractor/MethodExtractor.php @@ -38,7 +38,9 @@ public function getMethods($object): array foreach ($methods as $key => $method) { $id = $method->class . ':' . $method->name; if (isset($uniqueMethods[$id])) { + // @codeCoverageIgnoreStart unset($methods[$key]); + // @codeCoverageIgnoreEnd } $uniqueMethods[$id] = true; } diff --git a/src/Service/Extractor/PropertyExtractor.php b/src/Service/Extractor/PropertyExtractor.php index 7795416..5a6757c 100644 --- a/src/Service/Extractor/PropertyExtractor.php +++ b/src/Service/Extractor/PropertyExtractor.php @@ -19,8 +19,6 @@ class PropertyExtractor * Get all properties for a given class. * * @param object|string $object - * - * @throws \ReflectionException */ public function getProperties($object): array { diff --git a/src/Service/Normalize/AbstractNormalizer.php b/src/Service/Normalize/AbstractNormalizer.php index 18235db..482238c 100644 --- a/src/Service/Normalize/AbstractNormalizer.php +++ b/src/Service/Normalize/AbstractNormalizer.php @@ -10,14 +10,15 @@ use BowlOfSoup\NormalizerBundle\Model\ObjectCache; use BowlOfSoup\NormalizerBundle\Service\Extractor\AnnotationExtractor; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; +use BowlOfSoup\NormalizerBundle\Service\Normalizer; use BowlOfSoup\NormalizerBundle\Service\ObjectHelper; use Doctrine\Common\Collections\Collection; use Symfony\Contracts\Translation\TranslatorInterface; abstract class AbstractNormalizer { - /** @var \BowlOfSoup\NormalizerBundle\Service\Normalizer */ - protected $sharedNormalizer; + /** @var \BowlOfSoup\NormalizerBundle\Service\Normalizer|null */ + protected $sharedNormalizer = null; /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor */ protected $classExtractor; @@ -28,11 +29,11 @@ abstract class AbstractNormalizer /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\AnnotationExtractor */ protected $annotationExtractor; - /** @var string */ - protected $group; + /** @var string|null */ + protected $group = null; - /** @var int */ - protected $maxDepth; + /** @var int|null */ + protected $maxDepth = null; /** @var array */ protected $processedDepthObjects = []; @@ -40,8 +41,8 @@ abstract class AbstractNormalizer /** @var int */ protected $processedDepth = 0; - /** @var \BowlOfSoup\NormalizerBundle\Model\Store[] */ - protected $nameAndClassStore; + /** @var \BowlOfSoup\NormalizerBundle\Model\Store[]|array|null */ + protected $nameAndClassStore = null; public function __construct( ClassExtractor $classExtractor, @@ -83,7 +84,7 @@ protected function getValueForMaxDepth(object $object) { $value = $this->classExtractor->getId($object); if (null === $value) { - throw new BosNormalizerException('Maximal depth reached, but no identifier found. ' . 'Prevent this by adding a getId() method to ' . get_class($object)); + throw new BosNormalizerException('Maximal depth reached, but no identifier found. Prevent this by adding a getId() method to ' . get_class($object)); } return $value; @@ -240,9 +241,9 @@ protected function handleCallbackResult($propertyValue, Normalize $propertyAnnot } /** - * @param \BowlOfSoup\NormalizerBundle\Annotation\Translate[]|array + * @param \BowlOfSoup\NormalizerBundle\Annotation\Translate[] $translateAnnotations */ - protected function getTranslationAnnotation(array $translateAnnotations, $emptyGroup = false): ?Translate + protected function getTranslationAnnotation(array $translateAnnotations, bool $emptyGroup = false): ?Translate { if (empty($translateAnnotations)) { return null; @@ -251,7 +252,6 @@ protected function getTranslationAnnotation(array $translateAnnotations, $emptyG $group = ($emptyGroup) ? null : $this->group; $translationAnnotation = null; - /** @var \BowlOfSoup\NormalizerBundle\Annotation\Translate $translateAnnotation */ foreach ($translateAnnotations as $translateAnnotation) { if (!$translateAnnotation->isGroupValidForConstruct($group)) { continue; diff --git a/src/Service/Normalizer.php b/src/Service/Normalizer.php index cd16121..3b97145 100644 --- a/src/Service/Normalizer.php +++ b/src/Service/Normalizer.php @@ -1,5 +1,7 @@ cleanUpSession(); + return $this->normalizeData($data, $group); } @@ -98,7 +101,7 @@ private function normalizeData($data, ?string $group): array foreach ($data as $item) { $normalizedData[] = $this->normalizeData($item, $group); } - } else if (is_object($data)) { + } elseif (is_object($data)) { $normalizedData = $this->normalizeObject($data, $group); } else { throw new BosNormalizerException('Can only normalize an object or an array of objects. Input contains: ' . gettype($data)); diff --git a/src/Service/ObjectHelper.php b/src/Service/ObjectHelper.php index f73d9c9..dd01380 100644 --- a/src/Service/ObjectHelper.php +++ b/src/Service/ObjectHelper.php @@ -7,7 +7,7 @@ class ObjectHelper { /** - * @param object $object + * @param mixed $object * * @return int|string */ @@ -15,7 +15,7 @@ public static function getObjectIdentifier($object) { $objectId = self::getObjectId($object); - return $objectId ?? static::hashObject($object); + return $objectId ?? self::hashObject($object); } /** @@ -42,17 +42,15 @@ private static function hashObject($object, string $algorithm = 'md5'): ?string return null; } - $serializedObject = static::serializeObject($object); + $serializedObject = self::serializeObject($object); return null !== $serializedObject ? hash($algorithm, $serializedObject) : null; } /** - * @param object $object - * * @codeCoverageIgnore */ - private static function serializeObject(object $object) + private static function serializeObject(object $object): ?string { try { return serialize($object); diff --git a/tests/ArraySubset.php b/tests/ArraySubset.php index 6bf6d7e..25436ec 100644 --- a/tests/ArraySubset.php +++ b/tests/ArraySubset.php @@ -19,16 +19,12 @@ * * @codeCoverageIgnore */ -final class ArraySubset extends Constraint +class ArraySubset extends Constraint { - /** - * @var iterable - */ + /** @var iterable */ private $subset; - /** - * @var bool - */ + /** @var bool */ private $strict; public function __construct(iterable $subset, bool $strict = false) @@ -59,7 +55,7 @@ public static function assert($subset, $array, bool $checkForObjectIdentity = fa throw InvalidArgumentException::create(2, 'array or ArrayAccess'); } - $constraint = new static($subset, $checkForObjectIdentity); + $constraint = new self($subset, $checkForObjectIdentity); Assert::assertThat($array, $constraint, $message); } @@ -77,10 +73,10 @@ public static function assert($subset, $array, bool $checkForObjectIdentity = fa * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function evaluate($other, string $description = '', bool $returnResult = false) + public function evaluate($other, string $description = '', bool $returnResult = false): ?bool { - //type cast $other & $this->subset as an array to allow - //support in standard array functions. + // type cast $other & $this->subset as an array to allow + // support in standard array functions. $other = $this->toArray($other); $this->subset = $this->toArray($this->subset); @@ -106,6 +102,8 @@ public function evaluate($other, string $description = '', bool $returnResult = $this->fail($other, $description, $f); } + + return null; } /** diff --git a/tests/NormalizerTestTrait.php b/tests/NormalizerTestTrait.php index b066fdd..2d435cb 100644 --- a/tests/NormalizerTestTrait.php +++ b/tests/NormalizerTestTrait.php @@ -33,7 +33,7 @@ trait NormalizerTestTrait /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\AnnotationExtractor|\PHPUnit\Framework\MockObject\Stub\Stub */ protected $annotationExtractor; - /** @var \Symfony\Contracts\Translation\TranslatorInterface|\PHPUnit\Framework\MockObject\Stub\Stub */ + /** @var \Symfony\Contracts\Translation\TranslatorInterface|\PHPUnit\Framework\MockObject\Stub\Stub|\PHPUnit\Framework\MockObject\MockObject */ protected $translator; public function getNormalizer(): Normalizer @@ -44,7 +44,7 @@ public function getNormalizer(): Normalizer $annotationExtractor = $this->annotationExtractor ?? new AnnotationExtractor(); - /** @var \PHPUnit\Framework\MockObject\MockBuilder $translationMockBuilder */ + /** @var \PHPUnit\Framework\MockObject\MockBuilder|\PHPUnit\Framework\MockObject\MockObject $translationMockBuilder */ $translationMockBuilder = $this->getMockBuilder(TranslatorInterface::class) ->disableOriginalConstructor(); diff --git a/tests/Service/Encoder/EncoderJsonTest.php b/tests/Service/Encoder/EncoderJsonTest.php index fd0287e..49c2a4b 100644 --- a/tests/Service/Encoder/EncoderJsonTest.php +++ b/tests/Service/Encoder/EncoderJsonTest.php @@ -1,5 +1,7 @@ getMockBuilder(EncoderJson::class) ->disableOriginalConstructor() - ->setMethods(['jsonLastErrorMsgExists']); + ->onlyMethods(['jsonLastErrorMsgExists']); - /** @var \BowlOfSoup\NormalizerBundle\Service\Encoder\EncoderJson $encoderJson */ + /** @var \BowlOfSoup\NormalizerBundle\Service\Encoder\EncoderJson|\PHPUnit\Framework\MockObject\MockObject $encoderJson */ $encoderJson = $mockBuilder->getMock(); $encoderJson ->expects($this->any()) diff --git a/tests/Service/Encoder/EncoderXmlTest.php b/tests/Service/Encoder/EncoderXmlTest.php index 9748328..99014c7 100644 --- a/tests/Service/Encoder/EncoderXmlTest.php +++ b/tests/Service/Encoder/EncoderXmlTest.php @@ -83,7 +83,7 @@ public function testExceptionInXmlLoop(): void $mockBuilder = $this ->getMockBuilder(EncoderXml::class) ->disableOriginalConstructor() - ->setMethods(['arrayToXml']); + ->onlyMethods(['arrayToXml']); $encoderXml = $mockBuilder->getMock(); $encoderXml ->expects($this->any()) diff --git a/tests/Service/Extractor/AnnotationExtractorTest.php b/tests/Service/Extractor/AnnotationExtractorTest.php index 9866053..6be2764 100644 --- a/tests/Service/Extractor/AnnotationExtractorTest.php +++ b/tests/Service/Extractor/AnnotationExtractorTest.php @@ -16,13 +16,8 @@ class AnnotationExtractorTest extends TestCase { /** @var string */ - private const ANNOTATION_NORMALIZE = Normalize::class; + protected const ANNOTATION_NORMALIZE = Normalize::class; - /** - * @testdox Extracting class annotations. - * - * @throws \ReflectionException - */ public function testExtractClassAnnotation(): void { $annotation = new Normalize([]); @@ -31,11 +26,11 @@ public function testExtractClassAnnotation(): void $someClass = new SomeClass(); $reflectedClass = new \ReflectionClass($someClass); - /** @var \Doctrine\Common\Annotations\AnnotationReader $mockAnnotationReader */ + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ $mockAnnotationReader = $this ->getMockBuilder(AnnotationReader::class) ->disableOriginalConstructor() - ->setMethods(['getClassAnnotations']) + ->onlyMethods(['getClassAnnotations']) ->getMock(); $mockAnnotationReader ->expects($this->once()) @@ -48,9 +43,31 @@ public function testExtractClassAnnotation(): void $classExtractor->getAnnotationsForClass(static::ANNOTATION_NORMALIZE, $someClass); } - /** - * @testdox Extracting class annotations, but no class (object) given. - */ + public function testExtractClassAnnotationWithException(): void + { + $someClass = new SomeClass(); + $reflectedClass = new \ReflectionClass($someClass); + + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ + $mockAnnotationReader = $this + ->getMockBuilder(AnnotationReader::class) + ->disableOriginalConstructor() + ->onlyMethods(['getClassAnnotations']) + ->getMock(); + $mockAnnotationReader + ->expects($this->once()) + ->method('getClassAnnotations') + ->with($this->equalTo($reflectedClass)) + ->willThrowException(new \InvalidArgumentException('message')); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('BowlOfSoup\NormalizerBundle\Tests\assets\SomeClass: message'); + + $classExtractor = new AnnotationExtractor(); + $classExtractor->setAnnotationReader($mockAnnotationReader); + $classExtractor->getAnnotationsForClass(static::ANNOTATION_NORMALIZE, $someClass); + } + public function testExtractClassAnnotationNoClassGiven(): void { /** @var \Doctrine\Common\Annotations\AnnotationReader $mockAnnotationReader */ @@ -64,9 +81,6 @@ public function testExtractClassAnnotationNoClassGiven(): void $this->assertIsArray($classExtractor->getAnnotationsForClass(static::ANNOTATION_NORMALIZE, [])); } - /** - * @testdox Extracting method annotations. - */ public function testExtractMethodAnnotations(): void { $annotation = new Normalize([]); @@ -76,17 +90,17 @@ public function testExtractMethodAnnotations(): void $methodExtractor = $this ->getMockBuilder(MethodExtractor::class) ->disableOriginalConstructor() - ->setMethods(null) + ->addMethods([]) ->getMock(); $methods = $methodExtractor->getMethods($someClass); $annotationResult = [$annotation]; - /** @var \Doctrine\Common\Annotations\AnnotationReader $mockAnnotationReader */ + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ $mockAnnotationReader = $this ->getMockBuilder(AnnotationReader::class) ->disableOriginalConstructor() - ->setMethods(['getMethodAnnotations']) + ->onlyMethods(['getMethodAnnotations']) ->getMock(); $mockAnnotationReader ->expects($this->once()) @@ -101,9 +115,41 @@ public function testExtractMethodAnnotations(): void ArraySubset::assert([$annotation], $result); } - /** - * @testdox Extracting property annotations. - */ + public function testExtractMethodAnnotationsWithException(): void + { + $annotation = new Normalize([]); + $someClass = new SomeClass(); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor|\PHPUnit\Framework\MockObject\Stub\Stub $methodExtractor */ + $methodExtractor = $this + ->getMockBuilder(MethodExtractor::class) + ->disableOriginalConstructor() + ->addMethods([]) + ->getMock(); + $methods = $methodExtractor->getMethods($someClass); + + $annotationResult = [$annotation]; + + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ + $mockAnnotationReader = $this + ->getMockBuilder(AnnotationReader::class) + ->disableOriginalConstructor() + ->onlyMethods(['getMethodAnnotations']) + ->getMock(); + $mockAnnotationReader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->with($this->equalTo($methods[0])) + ->willThrowException(new \InvalidArgumentException('message')); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('BowlOfSoup\NormalizerBundle\Tests\assets\SomeClass (getProperty32): message'); + + $methodExtractor = new AnnotationExtractor(); + $methodExtractor->setAnnotationReader($mockAnnotationReader); + $methodExtractor->getAnnotationsForMethod(get_class($annotation), $methods[0]); + } + public function testExtractPropertyAnnotations(): void { $annotation = new Normalize([]); @@ -113,17 +159,17 @@ public function testExtractPropertyAnnotations(): void $propertyExtractor = $this ->getMockBuilder(PropertyExtractor::class) ->disableOriginalConstructor() - ->setMethods(null) + ->addMethods([]) ->getMock(); $properties = $propertyExtractor->getProperties($someClass); $annotationResult = [$annotation]; - /** @var \Doctrine\Common\Annotations\AnnotationReader $mockAnnotationReader */ + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ $mockAnnotationReader = $this ->getMockBuilder(AnnotationReader::class) ->disableOriginalConstructor() - ->setMethods(['getPropertyAnnotations']) + ->onlyMethods(['getPropertyAnnotations']) ->getMock(); $mockAnnotationReader ->expects($this->once()) @@ -137,4 +183,39 @@ public function testExtractPropertyAnnotations(): void ArraySubset::assert([$annotation], $result); } + + public function testExtractPropertyAnnotationsWithException(): void + { + $annotation = new Normalize([]); + $someClass = new SomeClass(); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor|\PHPUnit\Framework\MockObject\Stub\Stub $propertyExtractor */ + $propertyExtractor = $this + ->getMockBuilder(PropertyExtractor::class) + ->disableOriginalConstructor() + ->addMethods([]) + ->getMock(); + $properties = $propertyExtractor->getProperties($someClass); + + $annotationResult = [$annotation]; + + /** @var \Doctrine\Common\Annotations\AnnotationReader|\PHPUnit\Framework\MockObject\MockObject $mockAnnotationReader */ + $mockAnnotationReader = $this + ->getMockBuilder(AnnotationReader::class) + ->disableOriginalConstructor() + ->onlyMethods(['getPropertyAnnotations']) + ->getMock(); + $mockAnnotationReader + ->expects($this->once()) + ->method('getPropertyAnnotations') + ->with($this->equalTo($properties[0])) + ->willThrowException(new \InvalidArgumentException('message')); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('BowlOfSoup\NormalizerBundle\Tests\assets\SomeClass (property32): message'); + + $propertyExtractor = new AnnotationExtractor(); + $propertyExtractor->setAnnotationReader($mockAnnotationReader); + $propertyExtractor->getAnnotationsForProperty(get_class($annotation), $properties[0]); + } } diff --git a/tests/Service/Extractor/MethodExtractorTest.php b/tests/Service/Extractor/MethodExtractorTest.php index 236e3d3..b78d964 100644 --- a/tests/Service/Extractor/MethodExtractorTest.php +++ b/tests/Service/Extractor/MethodExtractorTest.php @@ -10,7 +10,7 @@ class MethodExtractorTest extends TestCase { - /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \PHPUnit\Framework\MockObject\MockObject|(\BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor&\PHPUnit\Framework\MockObject\MockObject) */ private $methodExtractor; protected function setUp(): void @@ -18,7 +18,7 @@ protected function setUp(): void $this->methodExtractor = $this ->getMockBuilder(MethodExtractor::class) ->disableOriginalConstructor() - ->setMethods(null) + ->addMethods([]) ->getMock(); } @@ -27,7 +27,9 @@ protected function setUp(): void */ public function testGetMethodsForNothing(): void { - $result = $this->methodExtractor->getMethods('foo'); + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor $methodExtractor */ + $methodExtractor = $this->methodExtractor; + $result = $methodExtractor->getMethods('foo'); $this->assertEmpty($result); $this->assertIsArray($result); @@ -39,7 +41,10 @@ public function testGetMethodsForNothing(): void public function testGetMethods() { $someClass = new SomeClass(); - $methods = $this->methodExtractor->getMethods($someClass); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\MethodExtractor $methodExtractor */ + $methodExtractor = $this->methodExtractor; + $methods = $methodExtractor->getMethods($someClass); $this->assertCount(8, $methods); $method = $methods[0]; diff --git a/tests/Service/Extractor/PropertyExtractorTest.php b/tests/Service/Extractor/PropertyExtractorTest.php index b2bfe2d..a0e103b 100644 --- a/tests/Service/Extractor/PropertyExtractorTest.php +++ b/tests/Service/Extractor/PropertyExtractorTest.php @@ -6,15 +6,14 @@ use BowlOfSoup\NormalizerBundle\Exception\BosNormalizerException; use BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor; +use BowlOfSoup\NormalizerBundle\Tests\assets\Person; use BowlOfSoup\NormalizerBundle\Tests\assets\ProxyObject; -use BowlOfSoup\NormalizerBundle\Annotation\Normalize; -use BowlOfSoup\NormalizerBundle\Tests\ArraySubset; use BowlOfSoup\NormalizerBundle\Tests\assets\SomeClass; use PHPUnit\Framework\TestCase; class PropertyExtractorTest extends TestCase { - /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor|\PHPUnit\Framework\MockObject\MockObject */ + /** @var (\BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor&\PHPUnit\Framework\MockObject\MockObject)|\PHPUnit\Framework\MockObject\MockObject */ private $propertyExtractor; protected function setUp(): void @@ -22,7 +21,7 @@ protected function setUp(): void $this->propertyExtractor = $this ->getMockBuilder(PropertyExtractor::class) ->disableOriginalConstructor() - ->setMethods(null) + ->addMethods([]) ->getMock(); } @@ -31,7 +30,9 @@ protected function setUp(): void */ public function testGetMethodsForNothing(): void { - $result = $this->propertyExtractor->getProperties('foo'); + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $result = $propertyExtractor->getProperties('foo'); $this->assertEmpty($result); $this->assertIsArray($result); @@ -46,10 +47,11 @@ public function testGetProperties() $stubPropertyExtractor = $this ->getMockBuilder(PropertyExtractor::class) ->disableOriginalConstructor() - ->setMethods(null) + ->addMethods([]) ->getMock(); $someClass = new SomeClass(); + $properties = $stubPropertyExtractor->getProperties($someClass); $this->assertCount(5, $properties); @@ -87,10 +89,13 @@ public function testGetProperties() public function testGetPropertyValue(): void { $someClass = new SomeClass(); - $properties = $this->propertyExtractor->getProperties($someClass); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $properties = $propertyExtractor->getProperties($someClass); foreach ($properties as $property) { if ('property53' === $property->getName()) { - $result = $this->propertyExtractor->getPropertyValue($someClass, $property); + $result = $propertyExtractor->getPropertyValue($someClass, $property); $this->assertSame('string', $result); } @@ -106,10 +111,13 @@ public function testGetPropertyValueForceGetMethodNoMethodAvailableDoctrineProxy $this->expectExceptionMessage('Unable to initiate Doctrine proxy, not get() method found for property proxyProperty'); $proxyObject = new ProxyObject(); - $properties = $this->propertyExtractor->getProperties($proxyObject); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $properties = $propertyExtractor->getProperties($proxyObject); foreach ($properties as $property) { if ('proxyProperty' === $property->getName()) { - $this->propertyExtractor->getPropertyValue( + $propertyExtractor->getPropertyValue( $proxyObject, $property ); @@ -125,10 +133,13 @@ public function testGetPropertyDoctrineProxyForceGetMethodAssertIdInteger(): voi $result = null; $proxyObject = new ProxyObject(); - $properties = $this->propertyExtractor->getProperties($proxyObject); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $properties = $propertyExtractor->getProperties($proxyObject); foreach ($properties as $property) { if ('id' === $property->getName()) { - $result = $this->propertyExtractor->getPropertyValue( + $result = $propertyExtractor->getPropertyValue( $proxyObject, $property ); @@ -138,13 +149,48 @@ public function testGetPropertyDoctrineProxyForceGetMethodAssertIdInteger(): voi $this->assertSame(123, $result); } + public function testGetPropertyForceGetMethodBecauseOfException(): void + { + $person = new Person(); + $person->setSurName('BowlOfSoup'); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + + $reflectionPropertyMock = $this + ->getMockBuilder(\ReflectionProperty::class) + ->disableOriginalConstructor() + ->onlyMethods(['getValue', 'getName']) + ->getMock(); + $reflectionPropertyMock + ->expects($this->once()) + ->method('getName') + ->withAnyParameters() + ->willReturn('SurName'); + $reflectionPropertyMock + ->expects($this->once()) + ->method('getValue') + ->withAnyParameters() + ->willThrowException(new \ReflectionException('foo')); + + $result = $propertyExtractor->getPropertyValue( + $person, + $reflectionPropertyMock + ); + + $this->assertSame('BowlOfSoup', $result); + } + /** * @testdox Get a value for a property by specifying method. */ public function testGetPropertyValueByMethod(): void { $someClass = new SomeClass(); - $result = $this->propertyExtractor->getPropertyValueByMethod($someClass, 'getProperty32'); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $result = $propertyExtractor->getPropertyValueByMethod($someClass, 'getProperty32'); $this->assertSame(123, $result); } @@ -155,7 +201,10 @@ public function testGetPropertyValueByMethod(): void public function testGetPropertyValueByMethodNoMethodAvailable(): void { $someClass = new SomeClass(); - $result = $this->propertyExtractor->getPropertyValueByMethod($someClass, 'getProperty53'); + + /** @var \BowlOfSoup\NormalizerBundle\Service\Extractor\PropertyExtractor $propertyExtractor */ + $propertyExtractor = $this->propertyExtractor; + $result = $propertyExtractor->getPropertyValueByMethod($someClass, 'getProperty53'); $this->assertNull($result); } diff --git a/tests/Service/NormalizerTest.php b/tests/Service/NormalizerTest.php index 8a36e5f..f943a4d 100644 --- a/tests/Service/NormalizerTest.php +++ b/tests/Service/NormalizerTest.php @@ -6,6 +6,7 @@ use BowlOfSoup\NormalizerBundle\Exception\BosNormalizerException; use BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor; +use BowlOfSoup\NormalizerBundle\Service\Normalizer; use BowlOfSoup\NormalizerBundle\Tests\ArraySubset; use BowlOfSoup\NormalizerBundle\Tests\assets\Address; use BowlOfSoup\NormalizerBundle\Tests\assets\Group; @@ -14,6 +15,7 @@ use BowlOfSoup\NormalizerBundle\Tests\assets\Person; use BowlOfSoup\NormalizerBundle\Tests\assets\ProxyObject; use BowlOfSoup\NormalizerBundle\Tests\assets\ProxySocial; +use BowlOfSoup\NormalizerBundle\Tests\assets\ProxySocialNotInitialized; use BowlOfSoup\NormalizerBundle\Tests\assets\Social; use BowlOfSoup\NormalizerBundle\Tests\assets\SomeClass; use BowlOfSoup\NormalizerBundle\Tests\assets\TelephoneNumbers; @@ -176,7 +178,7 @@ public function testNormalizeCircularReferenceNoFallback(): void /* @var \BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor $classExtractor */ $this->classExtractor = $this ->getMockBuilder(ClassExtractor::class) - ->setMethods(['getId']) + ->onlyMethods(['getId']) ->getMock(); $this->classExtractor ->expects($this->any()) @@ -200,7 +202,7 @@ public function testNormalizeCircularReferenceNoFallbackOnMethods(): void /* @var \BowlOfSoup\NormalizerBundle\Service\Extractor\ClassExtractor $classExtractor */ $this->classExtractor = $this ->getMockBuilder(ClassExtractor::class) - ->setMethods(['getId']) + ->onlyMethods(['getId']) ->getMock(); $this->classExtractor ->expects($this->any()) @@ -511,6 +513,16 @@ public function testNormalizeProxyWithMethods(): void ], $this->normalizer->normalize($socialProxy, 'proxy-method')); } + public function testNormalizeNotInitializedProxyWithMethods(): void + { + $socialProxy = new ProxySocialNotInitialized(); + $socialProxy->setFacebook('foo'); + + $this->assertSame([ + 'facebook' => 'foo', + ], $this->normalizer->normalize($socialProxy, 'proxy-method')); + } + private function getDummyDataSet(): Person { $groupCollection = new ArrayCollection(); diff --git a/tests/Service/SerializerTest.php b/tests/Service/SerializerTest.php index db2cc69..ceb7f05 100644 --- a/tests/Service/SerializerTest.php +++ b/tests/Service/SerializerTest.php @@ -6,6 +6,8 @@ use BowlOfSoup\NormalizerBundle\Service\Encoder\EncoderFactory; use BowlOfSoup\NormalizerBundle\Service\Encoder\EncoderJson; +use BowlOfSoup\NormalizerBundle\Service\Normalizer; +use BowlOfSoup\NormalizerBundle\Service\Serializer; use BowlOfSoup\NormalizerBundle\Tests\assets\Person; use BowlOfSoup\NormalizerBundle\Tests\assets\Social; use BowlOfSoup\NormalizerBundle\Tests\SerializerTestTrait; diff --git a/tests/Service/UnknownPropertyTest.php b/tests/Service/UnknownPropertyTest.php index b5ff7c2..826588c 100644 --- a/tests/Service/UnknownPropertyTest.php +++ b/tests/Service/UnknownPropertyTest.php @@ -5,6 +5,8 @@ namespace BowlOfSoup\NormalizerBundle\Tests\Service; use BowlOfSoup\NormalizerBundle\Service\Encoder\EncoderFactory; +use BowlOfSoup\NormalizerBundle\Service\Normalizer; +use BowlOfSoup\NormalizerBundle\Service\Serializer; use BowlOfSoup\NormalizerBundle\Tests\assets\UnknownPropertyNormalizeMethod; use BowlOfSoup\NormalizerBundle\Tests\assets\UnknownPropertyNormalizeProperty; use BowlOfSoup\NormalizerBundle\Tests\assets\UnknownPropertySerialize; diff --git a/tests/assets/AbstractClass.php b/tests/assets/AbstractClass.php index 5ed82ad..c088f0a 100644 --- a/tests/assets/AbstractClass.php +++ b/tests/assets/AbstractClass.php @@ -1,9 +1,12 @@