Skip to content

Commit

Permalink
Merge 2b12e86 into c715918
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Dec 1, 2020
2 parents c715918 + 2b12e86 commit d5da49f
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 49 deletions.
32 changes: 16 additions & 16 deletions .github/workflows/ci.yml
Expand Up @@ -28,7 +28,7 @@ jobs:
php-version: ${{ matrix.php }}
extensions: intl, bcmath, curl, openssl, mbstring
ini-values: memory_limit=-1
tools: flex, pecl, composer, flex, php-cs-fixer
tools: pecl, composer, php-cs-fixer
coverage: none
- name: Run PHP-CS-Fixer fix
run: php-cs-fixer fix --dry-run --diff --ansi
Expand All @@ -51,7 +51,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex, phpstan
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
- name: Run PHPStan analysis
env:
SYMFONY_PHPUNIT_VERSION: '9.4'
run: phpstan analyse --no-interaction --no-progress --no-interaction --ansi
run: ./vendor/bin/phpstan analyse --no-interaction --no-progress --no-interaction --ansi

phpunit:
name: PHPUnit (PHP ${{ matrix.php }})
Expand All @@ -112,7 +112,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: pcov
ini-values: memory_limit=-1
Expand Down Expand Up @@ -207,7 +207,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite
coverage: pcov
ini-values: memory_limit=-1
Expand Down Expand Up @@ -318,7 +318,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -358,7 +358,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -408,7 +408,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_pgsql
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -456,7 +456,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_mysql
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -502,7 +502,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -558,7 +558,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, mongodb
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -604,7 +604,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -646,7 +646,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -706,7 +706,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -770,7 +770,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite
coverage: none
ini-values: memory_limit=-1
Expand Down Expand Up @@ -813,7 +813,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex, pecl, composer, flex
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite
coverage: none
ini-values: memory_limit=-1
Expand Down
2 changes: 1 addition & 1 deletion .php_cs.dist
Expand Up @@ -30,14 +30,14 @@ return PhpCsFixer\Config::create()
'@Symfony' => true,
'@Symfony:risky' => true,
'single_line_comment_style' => false, // Temporary fix for compatibility with PHP 8 attributes, see https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5284
'comment_to_phpdoc' => true, // Temporary fix for compatibility with PHP 8 attributes, see https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5284
'align_multiline_comment' => [
'comment_type' => 'phpdocs_like',
],
'array_indentation' => true,
'array_syntax' => [
'syntax' => 'short',
],
'comment_to_phpdoc' => true,
'compact_nullable_typehint' => true,
'doctrine_annotation_array_assignment' => [
'operator' => '=',
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Expand Up @@ -21,6 +21,7 @@ parameters:
- tests/Bridge/NelmioApiDoc/*
- src/Bridge/FosUser/*
# BC layer
- tests/Bridge/Symfony/Bundle/DependencyInjection/Compiler/AnnotationFilterPassTest.php
- tests/Annotation/ApiResourceTest.php
- tests/Annotation/ApiPropertyTest.php
- tests/Metadata/Resource/Factory/AnnotationResourceMetadataFactoryTest.php
Expand Down
51 changes: 36 additions & 15 deletions src/Annotation/ApiFilter.php
Expand Up @@ -24,6 +24,7 @@
* @Annotation
* @Target({"PROPERTY", "CLASS"})
*/
#[\Attribute(\Attribute::TARGET_PROPERTY|\Attribute::TARGET_CLASS|\Attribute::IS_REPEATABLE)]
final class ApiFilter
{
/**
Expand Down Expand Up @@ -51,25 +52,45 @@ final class ApiFilter
*/
public $arguments = [];

public function __construct($options = [])
{
if (!\is_string($options['value'] ?? null)) {
throw new InvalidArgumentException('This annotation needs a value representing the filter class.');
}

if (!is_a($options['value'], FilterInterface::class, true)) {
throw new InvalidArgumentException(sprintf('The filter class "%s" does not implement "%s". Did you forget a use statement?', $options['value'], FilterInterface::class));
}
/**
* @param string $filterClass
* @param string $id
* @param string $strategy
*/
public function __construct(
$filterClass,
?string $id = null,
?string $strategy = null,
array $properties = [],
array $arguments = []
) {
if (\is_array($filterClass)) { /** @phpstan-ignore-line Doctrine annotations */
$options = $filterClass;
$this->filterClass = $options['value'] ?? null;
unset($options['value']);

$this->filterClass = $options['value'];
unset($options['value']);
foreach ($options as $key => $value) {
if (!property_exists($this, $key)) {
throw new InvalidArgumentException(sprintf('Property "%s" does not exist on the ApiFilter annotation.', $key));
}

foreach ($options as $key => $value) {
if (!property_exists($this, $key)) {
throw new InvalidArgumentException(sprintf('Property "%s" does not exist on the ApiFilter annotation.', $key));
$this->{$key} = $value;
}
} else {
// PHP attribute
$this->filterClass = $filterClass;
$this->id = $id;
$this->strategy = $strategy;
$this->properties = $properties;
$this->arguments = $arguments;
}

if (!\is_string($this->filterClass)) {
throw new InvalidArgumentException('This annotation needs a value representing the filter class.');
}

$this->{$key} = $value;
if (!is_a($this->filterClass, FilterInterface::class, true)) {
throw new InvalidArgumentException(sprintf('The filter class "%s" does not implement "%s". Did you forget a use statement?', $this->filterClass, FilterInterface::class));
}
}
}
Expand Up @@ -58,7 +58,9 @@ public function process(ContainerBuilder $container): void
*/
private function createFilterDefinitions(\ReflectionClass $resourceReflectionClass, ContainerBuilder $container): void
{
$this->reader ?? $this->reader = $container->get('annotation_reader');
if (null === $this->reader) {
$this->reader = $container->has('annotation_reader') ? $container->get('annotation_reader') : null;
}

foreach ($this->readFilterAnnotations($resourceReflectionClass, $this->reader) as $id => [$arguments, $filterClass]) {
if ($container->has($id)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Bundle/Test/ApiTestAssertionsTrait.php
Expand Up @@ -152,7 +152,7 @@ private static function getSchemaFactory(): SchemaFactoryInterface
{
try {
/** @var SchemaFactoryInterface $schemaFactory */
$schemaFactory = static::$container->get('api_platform.json_schema.schema_factory');
$schemaFactory = static::$container->get('api_platform.json_schema.schema_factory'); // @phpstan-ignore-line
} catch (ServiceNotFoundException $e) {
throw new \LogicException('You cannot use the resource JSON Schema assertions if the "api_platform.swagger.versions" config is null or empty.');
}
Expand Down
1 change: 0 additions & 1 deletion src/GraphQl/Type/TypeBuilder.php
Expand Up @@ -127,7 +127,6 @@ public function getResourceObjectType(?string $resourceClass, ResourceMetadata $
}

$fieldsBuilder = $this->fieldsBuilderLocator->get('api_platform.graphql.fields_builder');

$fields = $fieldsBuilder->getResourceObjectTypeFields($resourceClass, $resourceMetadata, $input, $queryName, $mutationName, $subscriptionName, $depth, $ioMetadata);

if ($input && null !== $mutationName && null !== $mutationArgs = $resourceMetadata->getGraphql()[$mutationName]['args'] ?? null) {
Expand Down
2 changes: 1 addition & 1 deletion src/JsonSchema/TypeFactory.php
Expand Up @@ -126,7 +126,7 @@ private function getClassType(?string $className, string $format, ?bool $readabl
return ['type' => 'string'];
}

if ($this->isResourceClass($className) && true !== $readableLink) {
if (true !== $readableLink && $this->isResourceClass($className)) {
return [
'type' => 'string',
'format' => 'iri-reference',
Expand Down
Expand Up @@ -31,7 +31,7 @@ final class AnnotationResourceFilterMetadataFactory implements ResourceMetadataF
private $reader;
private $decorated;

public function __construct(Reader $reader, ResourceMetadataFactoryInterface $decorated = null)
public function __construct(?Reader $reader = null, ResourceMetadataFactoryInterface $decorated = null)
{
$this->reader = $reader;
$this->decorated = $decorated;
Expand Down
23 changes: 17 additions & 6 deletions src/Util/AnnotationFilterExtractorTrait.php
Expand Up @@ -28,12 +28,23 @@ trait AnnotationFilterExtractorTrait
/**
* Filters annotations to get back only ApiFilter annotations.
*
* @param array $miscAnnotations class or property annotations
* @param \ReflectionClass|\ReflectionProperty $reflector
*
* @return \Iterator only ApiFilter annotations
*/
private function getFilterAnnotations(array $miscAnnotations): \Iterator
private function getFilterAnnotations(\Reflector $reflector, ?Reader $reader = null): \Iterator
{
if (\PHP_VERSION_ID >= 80000 && $attributes = $reflector->getAttributes(ApiFilter::class)) {
foreach ($attributes as $attribute) {
yield $attribute->newInstance();
}
}

if (null === $reader) {
return;
}

$miscAnnotations = $reflector instanceof \ReflectionClass ? $reader->getClassAnnotations($reflector) : $reader->getPropertyAnnotations($reflector);
foreach ($miscAnnotations as $miscAnnotation) {
if (ApiFilter::class === \get_class($miscAnnotation)) {
yield $miscAnnotation;
Expand Down Expand Up @@ -80,11 +91,11 @@ private function getFilterProperties(ApiFilter $filterAnnotation, \ReflectionCla
*
* @return array Key is the filter id. It has two values, properties and the ApiFilter instance
*/
private function readFilterAnnotations(\ReflectionClass $reflectionClass, Reader $reader): array
private function readFilterAnnotations(\ReflectionClass $reflectionClass, Reader $reader = null): array
{
$filters = [];

foreach ($this->getFilterAnnotations($reader->getClassAnnotations($reflectionClass)) as $filterAnnotation) {
foreach ($this->getFilterAnnotations($reflectionClass, $reader) as $filterAnnotation) {
$filterClass = $filterAnnotation->filterClass;
$id = $this->generateFilterId($reflectionClass, $filterClass, $filterAnnotation->id);

Expand All @@ -98,7 +109,7 @@ private function readFilterAnnotations(\ReflectionClass $reflectionClass, Reader
}

foreach ($reflectionClass->getProperties() as $reflectionProperty) {
foreach ($this->getFilterAnnotations($reader->getPropertyAnnotations($reflectionProperty)) as $filterAnnotation) {
foreach ($this->getFilterAnnotations($reflectionProperty, $reader) as $filterAnnotation) {
$filterClass = $filterAnnotation->filterClass;
$id = $this->generateFilterId($reflectionClass, $filterClass, $filterAnnotation->id);

Expand Down Expand Up @@ -130,7 +141,7 @@ private function readFilterAnnotations(\ReflectionClass $reflectionClass, Reader
*
* @param \ReflectionClass $reflectionClass the reflection class of a Resource
* @param string $filterClass the filter class
* @param string $filterId the filter id
* @param string|null $filterId the filter id
*/
private function generateFilterId(\ReflectionClass $reflectionClass, string $filterClass, string $filterId = null): string
{
Expand Down
24 changes: 20 additions & 4 deletions tests/Annotation/ApiFilterTest.php
Expand Up @@ -28,32 +28,48 @@ public function testInvalidConstructor()
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('This annotation needs a value representing the filter class.');

new ApiFilter();
new ApiFilter(null); // @phpstan-ignore-line
}

public function testInvalidFilter()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The filter class "ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Entity\\Dummy" does not implement "ApiPlatform\\Core\\Api\\FilterInterface". Did you forget a use statement?');

new ApiFilter(['value' => Dummy::class]);
new ApiFilter(['value' => Dummy::class]); // @phpstan-ignore-line
}

public function testInvalidProperty()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Property "foo" does not exist on the ApiFilter annotation.');

new ApiFilter(['value' => DummyFilter::class, 'foo' => 'bar']);
new ApiFilter(['value' => DummyFilter::class, 'foo' => 'bar']); // @phpstan-ignore-line
}

public function testAssignation()
{
$resource = new ApiFilter(['value' => DummyFilter::class, 'strategy' => 'test', 'properties' => ['one', 'two'], 'arguments' => ['args']]);
$resource = new ApiFilter(['value' => DummyFilter::class, 'strategy' => 'test', 'properties' => ['one', 'two'], 'arguments' => ['args']]); // @phpstan-ignore-line

$this->assertEquals($resource->filterClass, DummyFilter::class);
$this->assertEquals($resource->strategy, 'test');
$this->assertEquals($resource->properties, ['one', 'two']);
$this->assertEquals($resource->arguments, ['args']);
}

/**
* @requires PHP 8.0
*/
public function testAssignationAttribute()
{
$filter = eval(<<<'PHP'
return new \ApiPlatform\Core\Annotation\ApiFilter(\ApiPlatform\Core\Tests\Fixtures\DummyFilter::class, strategy: 'test', properties: ['one', 'two'], arguments: ['args']);
PHP
);

$this->assertEquals($filter->filterClass, DummyFilter::class);
$this->assertEquals($filter->strategy, 'test');
$this->assertEquals($filter->properties, ['one', 'two']);
$this->assertEquals($filter->arguments, ['args']);
}
}

0 comments on commit d5da49f

Please sign in to comment.