Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 2.5 #3291

Merged
merged 9 commits into from
Nov 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ executors:
type: string
docker:
- image: circleci/php:<< parameters.php_version >>-node
environment:
SYMFONY_REQUIRE: ^3.4 || ^4.0 || ^5.0

jobs:
php-cs-fixer:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ env:
EXT_MONGODB_VERSION: '1.6.0'
EXT_PCOV_VERSION: '1.0.6'
LEGACY: '0'
SYMFONY_REQUIRE: ^3.4 || ^4.0 || ^5.0

jobs:
phpunit:
Expand Down
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
language: php

env:
global:
- SYMFONY_REQUIRE='^3.4 || ^4.0 || ^5.0'

cache:
directories:
- $HOME/.composer/cache
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* MongoDB: Possibility to add execute options (aggregate command fields) for a resource, like `allowDiskUse` (#3144)
* GraphQL: Allow to format GraphQL errors based on exceptions (#3063)
* GraphQL: Add page-based pagination (#3175)
* OpenAPI: Add PHP default values to the documentation (#2386)
* OpenAPI: Add PHP default values to the documentation (#2386)

## 2.5.3

* Compatibility with Symfony 5
* GraphQL: Fix `hasNextPage` when `offset > itemsPerPage`

## 2.5.2

Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
"phpdocumentor/type-resolver": "^0.3 || ^0.4",
"phpspec/prophecy": "^1.8",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.11.10",
"phpstan/phpstan-doctrine": "^0.11.5",
"phpstan/phpstan-phpunit": "^0.11.2",
"phpstan/phpstan-shim": "^0.11.10",
"phpstan/phpstan-symfony": "^0.11.6",
"phpunit/phpunit": "^7.5.2 || ^8.0",
"psr/log": "^1.0",
Expand Down Expand Up @@ -128,6 +128,9 @@
"extra": {
"branch-alias": {
"dev-master": "2.6.x-dev"
},
"symfony": {
"require": "^3.4 || ^4.0 || ^5.0"
}
}
}
24 changes: 5 additions & 19 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -107,28 +107,14 @@ parameters:
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\(AbstractFilter|FilterInterface)::apply\(\) invoked with 5 parameters, 3-4 required\.#'
- '#Method ApiPlatform\\Core\\PathResolver\\OperationPathResolverInterface::resolveOperationPath\(\) invoked with 4 parameters, 3 required\.#'

# Expected, due to forward compatibility
-
message: '#Class Symfony\\Component\\ErrorHandler\\ErrorRenderer\\ErrorRendererInterface not found\.#'
path: %currentWorkingDirectory%/tests/Fixtures/app/AppKernel.php
- '#Class Symfony\\Component\\ErrorHandler\\Exception\\FlattenException not found\.#'
-
message: '#Class Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener not found\.#'
path: %currentWorkingDirectory%/src/EventListener/ExceptionListener.php
-
message: '#Instantiated class Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener not found\.#'
path: %currentWorkingDirectory%/src/EventListener/ExceptionListener.php
# Expected, due to backward compatibility
-
message: '#Parameter \$exception of method ApiPlatform\\Core\\Action\\ExceptionAction::__invoke\(\) has invalid typehint type Symfony\\Component\\ErrorHandler\\Exception\\FlattenException\.#'
path: %currentWorkingDirectory%/src/Action/ExceptionAction.php
- '#Call to method get(Class|Headers|StatusCode)\(\) on an unknown class Symfony\\Component\\ErrorHandler\\Exception\\FlattenException\.#'
message: "#Call to function method_exists\\(\\) with ApiPlatform\\\\Core\\\\JsonApi\\\\Serializer\\\\ItemNormalizer and 'setCircularReferenc…' will always evaluate to false\\.#"
path: %currentWorkingDirectory%/tests/JsonApi/Serializer/ItemNormalizerTest.php

# Expected, due to forward compatibility
-
message: "#Call to function method_exists\\(\\) with 'Symfony\\\\\\\\Component.+' and 'getThrowable' will always evaluate to false\\.#"
paths:
- %currentWorkingDirectory%/tests/Bridge/Symfony/Validator/EventListener/ValidationExceptionListenerTest.php
- %currentWorkingDirectory%/tests/EventListener/ExceptionListenerTest.php
-
message: '#Instanceof between bool\|float\|int|null and ArrayObject will always evaluate to false\.#'
paths:
- %currentWorkingDirectory%/src/JsonApi/Serializer/ItemNormalizer.php
- %currentWorkingDirectory%/src/Serializer/AbstractItemNormalizer.php
2 changes: 2 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@
<service id="api_platform.listener.exception" class="ApiPlatform\Core\EventListener\ExceptionListener">
<argument>api_platform.action.exception</argument>
<argument type="service" id="logger" on-invalid="null" />
<argument>false</argument>
<argument type="service" id="exception_listener" on-invalid="null" />

<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" priority="-96" />
<tag name="monolog.logger" channel="request" />
Expand Down
10 changes: 3 additions & 7 deletions src/EventListener/ExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\EventListener\ErrorListener;
use Symfony\Component\HttpKernel\EventListener\ExceptionListener as BaseExceptionListener;
use Symfony\Component\HttpKernel\EventListener\ExceptionListener as LegacyExceptionListener;

/**
* Handles requests errors.
Expand All @@ -29,13 +29,9 @@ final class ExceptionListener
{
private $exceptionListener;

public function __construct($controller, LoggerInterface $logger = null, $debug = false)
public function __construct($controller, LoggerInterface $logger = null, $debug = false, ErrorListener $errorListener = null)
{
if (class_exists(ErrorListener::class)) {
$this->exceptionListener = new ErrorListener($controller, $logger, $debug);
} else {
$this->exceptionListener = new BaseExceptionListener($controller, $logger, $debug);
}
$this->exceptionListener = $errorListener ? new ErrorListener($controller, $logger, $debug) : new LegacyExceptionListener($controller, $logger, $debug);
}

public function onKernelException(ExceptionEvent $event): void
Expand Down
3 changes: 2 additions & 1 deletion src/GraphQl/Resolver/Stage/SerializeStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
$data['pageInfo']['startCursor'] = base64_encode((string) $offset);
$end = $offset + $nbPageItems - 1;
$data['pageInfo']['endCursor'] = base64_encode((string) ($end >= 0 ? $end : 0));
$data['pageInfo']['hasNextPage'] = (float) $offset + $collection->getItemsPerPage() * $collection->getCurrentPage() < $totalItems;
$itemsPerPage = $collection->getItemsPerPage();
$data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
$data['pageInfo']['hasPreviousPage'] = $offset > 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
return $this->iriConverter->getIriFromItem($object);
};
}
if (!interface_exists(AdvancedNameConverterInterface::class)) {
if (!interface_exists(AdvancedNameConverterInterface::class) && method_exists($this, 'setCircularReferenceHandler')) {
$this->setCircularReferenceHandler($defaultContext['circular_reference_handler']);
}

Expand Down
7 changes: 4 additions & 3 deletions tests/EventListener/ExceptionListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\EventListener\ErrorListener;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
Expand All @@ -44,7 +45,7 @@ public function testOnKernelException(Request $request)
$eventProphecy->getKernel()->willReturn($kernel);
$eventProphecy->setResponse(Argument::type(Response::class))->shouldBeCalled();

$listener = new ExceptionListener('foo:bar');
$listener = new ExceptionListener('foo:bar', null, false, class_exists(ErrorListener::class) ? $this->prophesize(ErrorListener::class)->reveal() : null);
$listener->onKernelException($eventProphecy->reveal());
}

Expand All @@ -62,7 +63,7 @@ public function testDoNothingWhenNotAnApiCall()
$eventProphecy->getRequest()->willReturn(new Request());
$eventProphecy->setResponse(Argument::type(Response::class))->shouldNotBeCalled();

$listener = new ExceptionListener('foo:bar');
$listener = new ExceptionListener('foo:bar', null, false, class_exists(ErrorListener::class) ? $this->prophesize(ErrorListener::class)->reveal() : null);
$listener->onKernelException($eventProphecy->reveal());
}

Expand All @@ -75,7 +76,7 @@ public function testDoNothingWhenHtmlRequested()
$eventProphecy->getRequest()->willReturn($request);
$eventProphecy->setResponse(Argument::type(Response::class))->shouldNotBeCalled();

$listener = new ExceptionListener('foo:bar');
$listener = new ExceptionListener('foo:bar', null, false, class_exists(ErrorListener::class) ? $this->prophesize(ErrorListener::class)->reveal() : null);
$listener->onKernelException($eventProphecy->reveal());
}
}
2 changes: 1 addition & 1 deletion tests/GraphQl/Resolver/Stage/SerializeStageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public function applyCollectionWithPaginationProvider(): array
'paginator' => [new ArrayPaginator([new \stdClass(), new \stdClass(), new \stdClass()], 0, 2), [], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MA=='], ['node' => ['normalized_item'], 'cursor' => 'MQ==']], 'pageInfo' => ['startCursor' => 'MA==', 'endCursor' => 'MQ==', 'hasNextPage' => true, 'hasPreviousPage' => false]]],
'paginator with after cursor' => [new ArrayPaginator([new \stdClass(), new \stdClass(), new \stdClass()], 1, 2), ['after' => 'MA=='], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ=='], ['node' => ['normalized_item'], 'cursor' => 'Mg==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'Mg==', 'hasNextPage' => false, 'hasPreviousPage' => true]]],
'paginator with bad after cursor' => [new ArrayPaginator([], 0, 0), ['after' => '-'], null, \UnexpectedValueException::class, 'Cursor - is invalid'],
'paginator with before cursor' => [new ArrayPaginator([new \stdClass(), new \stdClass(), new \stdClass()], 1, 1), ['before' => 'Mg=='], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'MQ==', 'hasNextPage' => false, 'hasPreviousPage' => true]]],
'paginator with before cursor' => [new ArrayPaginator([new \stdClass(), new \stdClass(), new \stdClass()], 1, 1), ['before' => 'Mg=='], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'MQ==', 'hasNextPage' => true, 'hasPreviousPage' => true]]],
'paginator with bad before cursor' => [new ArrayPaginator([], 0, 0), ['before' => '-'], null, \UnexpectedValueException::class, 'Cursor - is invalid'],
'paginator with last' => [new ArrayPaginator([new \stdClass(), new \stdClass(), new \stdClass()], 1, 2), ['last' => 2], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ=='], ['node' => ['normalized_item'], 'cursor' => 'Mg==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'Mg==', 'hasNextPage' => false, 'hasPreviousPage' => true]]],
];
Expand Down
12 changes: 6 additions & 6 deletions tests/JsonApi/Serializer/ItemNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,17 +256,17 @@ public function testNormalizeCircularReference()
$normalizer->setSerializer($this->prophesize(SerializerInterface::class)->reveal());

$circularReferenceLimit = 2;
if (interface_exists(AdvancedNameConverterInterface::class)) {
if (!interface_exists(AdvancedNameConverterInterface::class) && method_exists($normalizer, 'setCircularReferenceLimit')) {
$normalizer->setCircularReferenceLimit($circularReferenceLimit);

$context = [
'circular_reference_limit' => $circularReferenceLimit,
'circular_reference_limit_counters' => [spl_object_hash($circularReferenceEntity) => 2],
'circular_reference_limit' => [spl_object_hash($circularReferenceEntity) => 2],
'cache_error' => function () {},
];
} else {
$normalizer->setCircularReferenceLimit($circularReferenceLimit);

$context = [
'circular_reference_limit' => [spl_object_hash($circularReferenceEntity) => 2],
'circular_reference_limit' => $circularReferenceLimit,
'circular_reference_limit_counters' => [spl_object_hash($circularReferenceEntity) => 2],
'cache_error' => function () {},
];
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Serializer/AbstractItemNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public function testNormalize()
]);
$normalizer->setSerializer($serializerProphecy->reveal());

if (!interface_exists(AdvancedNameConverterInterface::class)) {
if (!interface_exists(AdvancedNameConverterInterface::class) && method_exists($normalizer, 'setIgnoredAttributes')) {
$normalizer->setIgnoredAttributes(['alias']);
}

Expand Down