Skip to content

Commit 34396ee

Browse files
authored
Merge pull request #334 from W0rma/optional-annotations
Make doctrine/annotations an optional dependency
2 parents 73fcb54 + 0861943 commit 34396ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+938
-128
lines changed

.github/workflows/ci.yaml

+17-11
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,37 @@ jobs:
1717
fail-fast: false
1818
matrix:
1919
php-version:
20-
- "7.3"
21-
- "7.4"
22-
- "8.0"
2320
- "8.1"
2421
- "8.2"
2522
- "8.3"
2623
dependencies:
2724
- "highest"
2825
- "lowest"
26+
remove-annotations:
27+
- "yes"
28+
- "no"
2929
symfony-require:
3030
- "^3.0"
3131
- "^4.0"
3232
- "^5.0"
33+
- "^6.0"
3334
include:
3435
- php-version: 8.4
3536
symfony-require: "^5.0"
3637
composer-options: "--ignore-platform-req=php+" # TODO remove once phpspec/prophecy supports PHP 8.4
37-
- php-version: 8.0
38-
symfony-require: "^6.0"
39-
- php-version: 8.1
40-
symfony-require: "^6.0"
41-
- php-version: 8.2
42-
symfony-require: "^6.0"
43-
- php-version: 8.3
44-
symfony-require: "^6.0"
4538
- php-version: 8.4
4639
symfony-require: "^6.0"
4740
composer-options: "--ignore-platform-req=php+" # TODO remove once phpspec/prophecy supports PHP 8.4
4841
- php-version: 8.2
4942
symfony-require: "^7.0"
43+
- php-version: 8.2
44+
symfony-require: "^7.0"
45+
remove-annotations: "yes"
46+
- php-version: 8.3
47+
symfony-require: "^7.0"
5048
- php-version: 8.3
5149
symfony-require: "^7.0"
50+
remove-annotations: "yes"
5251
- php-version: 8.4
5352
symfony-require: "^7.0"
5453
composer-options: "--ignore-platform-req=php+" # TODO remove once phpspec/prophecy supports PHP 8.4
@@ -65,6 +64,13 @@ jobs:
6564
ini-values: "zend.assertions=1"
6665
tools: "flex"
6766

67+
- name: "Remove remove-annotations if required"
68+
if: "${{ matrix.remove-annotations == 'yes' }}"
69+
env:
70+
SYMFONY_REQUIRE: "${{ matrix.symfony-require }}"
71+
run: |
72+
composer remove --no-update --dev doctrine/annotations
73+
6874
- name: "Install dependencies with Composer"
6975
uses: "ramsey/composer-install@v2"
7076
env:

.github/workflows/coding-standards.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
matrix:
1818
php-version:
19-
- "7.4"
19+
- "8.1"
2020

2121
steps:
2222
- name: "Checkout"

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ This will resolve the latest stable version.
7373

7474
Otherwise, install the library and setup the autoloader yourself.
7575

76+
If you want to use [**annotations**](#annotations) for configuration you need
77+
to install the `doctrine/annotations` package:
78+
79+
```sh
80+
composer require doctrine/annotations
81+
```
82+
83+
If your app uses PHP 8.1 or higher it is recommended to use native PHP
84+
attributes.
85+
In this case you don't need to install the Doctrine package.
86+
7687
### Working With Symfony
7788

7889
There is a bundle for that! Install the

composer.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@
1717
}
1818
],
1919
"require": {
20-
"php": "^7.2 | ^8.0",
21-
"doctrine/annotations": "^1.13.2 || ^2.0",
20+
"php": "^8.1",
2221
"jms/metadata": "^2.0",
2322
"jms/serializer": "^3.18.2",
24-
"symfony/expression-language": "~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0"
23+
"symfony/expression-language": "^3.4.47 || ~4.0 || ~5.0 || ~6.0 || ~7.0"
2524
},
2625
"require-dev": {
27-
"phpunit/phpunit": "^7 | ^9.5.10",
26+
"phpunit/phpunit": "^9.5.10",
27+
"doctrine/annotations": "^1.13.2 || ^2.0",
2828
"doctrine/coding-standard": "^12.0",
2929
"doctrine/persistence": "^1.3.4 | ^2.0 | ^3.0",
3030
"pagerfanta/core": "^2.4 || ^3.0",
3131
"phpdocumentor/type-resolver": "^1.5.1",
3232
"phpspec/prophecy-phpunit": "^2.0.1",
3333
"phpspec/prophecy": "^1.16",
34-
"symfony/routing": "~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0",
35-
"symfony/yaml": "~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0",
34+
"symfony/routing": "^3.4.47 || ~4.0 || ~5.0 || ~6.0 || ~7.0",
35+
"symfony/yaml": "^3.4.47 || ~4.0 || ~5.0 || ~6.0 || ~7.0",
3636
"twig/twig": "^1.43 || ^2.13 || ^3.0"
3737
},
3838
"suggest": {

phpcs.xml.dist

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@
4848
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
4949
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
5050
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
51+
52+
<!-- TODO Apply sniffs if possible -->
53+
<exclude name="SlevomatCodingStandard.Classes.RequireConstructorPropertyPromotion.RequiredConstructorPropertyPromotion"/>
54+
<exclude name="SlevomatCodingStandard.TypeHints.UnionTypeHintFormat.DisallowedShortNullable"/>
55+
<exclude name="SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration.MissingTrailingComma"/>
56+
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
57+
<exclude name="SlevomatCodingStandard.TypeHints.UnionTypeHintFormat.DisallowedShortNullable"/>
58+
<exclude name="SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall"/>
59+
<exclude name="SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch.NonCapturingCatchRequired"/>
5160
</rule>
5261

5362
<rule ref="SlevomatCodingStandard.ControlStructures.RequireYodaComparison"/>

src/Configuration/Annotation/Embedded.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ class Embedded
3939
public $exclusion = null;
4040

4141
/**
42-
* @param string|array $content
42+
* @param array|string|null $values
43+
* @param array|string|null $content
4344
*/
44-
public function __construct(array $values = [], $content = null, ?string $type = null, ?string $xmlElementName = null, ?Exclusion $exclusion = null)
45+
public function __construct($values = [], $content = null, ?string $type = null, ?string $xmlElementName = null, ?Exclusion $exclusion = null)
4546
{
4647
$this->loadAnnotationParameters(get_defined_vars());
4748
}

src/Configuration/Annotation/Exclusion.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ final class Exclusion
4444
*/
4545
public $excludeIf = null;
4646

47-
public function __construct(array $values = [], ?array $groups = null, ?string $sinceVersion = null, ?string $untilVersion = null, ?int $maxDepth = null, ?string $excludeIf = null)
47+
/**
48+
* @param array|null $values
49+
*/
50+
public function __construct($values = [], ?array $groups = null, ?string $sinceVersion = null, ?string $untilVersion = null, ?int $maxDepth = null, ?string $excludeIf = null)
4851
{
4952
$this->loadAnnotationParameters(get_defined_vars());
5053
}

src/Configuration/Annotation/Relation.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ final class Relation
4444
public $exclusion = null;
4545

4646
/**
47+
* @param array|string|null $values
4748
* @param string|Route $href
4849
* @param string|Embedded $embedded
4950
*/
50-
public function __construct(array $values = [], ?string $name = null, $href = null, $embedded = null, array $attributes = [], ?Exclusion $exclusion = null)
51+
public function __construct($values = [], ?string $name = null, $href = null, $embedded = null, array $attributes = [], ?Exclusion $exclusion = null)
5152
{
5253
$this->loadAnnotationParameters(get_defined_vars());
5354
}

src/Configuration/Annotation/RelationProvider.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ class RelationProvider
2020
*/
2121
public $name;
2222

23-
public function __construct(array $values = [], ?string $name = null)
23+
/**
24+
* @param array|string|null $values
25+
*/
26+
public function __construct($values = [], ?string $name = null)
2427
{
2528
$this->loadAnnotationParameters(get_defined_vars());
2629
}

src/Configuration/Annotation/Route.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ class Route
3838
public $generator = null;
3939

4040
/**
41+
* @param array|string|null $values
4142
* @param array|string $parameters
4243
* @param bool|string $absolute
4344
*/
44-
public function __construct(array $values = [], ?string $name = null, $parameters = null, $absolute = false, ?string $generator = null)
45+
public function __construct($values = [], ?string $name = null, $parameters = null, $absolute = false, ?string $generator = null)
4546
{
4647
$this->loadAnnotationParameters(get_defined_vars());
4748
}

src/HateoasBuilder.php

+13-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\Common\Annotations\FileCacheReader;
99
use Hateoas\Configuration\Metadata\ConfigurationExtensionInterface;
1010
use Hateoas\Configuration\Metadata\Driver\AnnotationDriver;
11+
use Hateoas\Configuration\Metadata\Driver\AttributeDriver;
1112
use Hateoas\Configuration\Metadata\Driver\ExtensionDriver;
1213
use Hateoas\Configuration\Metadata\Driver\XmlDriver;
1314
use Hateoas\Configuration\Metadata\Driver\YamlDriver;
@@ -385,33 +386,31 @@ public function replaceMetadataDir(string $dir, string $namespacePrefix = ''): H
385386

386387
private function buildMetadataFactory(): MetadataFactoryInterface
387388
{
389+
$expressionEvaluator = $this->getExpressionEvaluator();
390+
391+
$typeParser = new Parser();
392+
388393
$annotationReader = $this->annotationReader;
394+
$drivers = [new AttributeDriver($expressionEvaluator, $this->chainProvider, $typeParser)];
389395

390-
if (null === $annotationReader) {
396+
if (null === $annotationReader && class_exists(AnnotationReader::class)) {
391397
$annotationReader = new AnnotationReader();
392398

393399
if (null !== $this->cacheDir) {
394400
$this->createDir($this->cacheDir . '/annotations');
395401
$annotationReader = new FileCacheReader($annotationReader, $this->cacheDir . '/annotations', $this->debug);
396402
}
397-
}
398403

399-
$expressionEvaluator = $this->getExpressionEvaluator();
400-
401-
$typeParser = new Parser();
404+
$drivers[] = new AnnotationDriver($annotationReader, $expressionEvaluator, $this->chainProvider, $typeParser);
405+
}
402406

403407
if (!empty($this->metadataDirs)) {
404-
$fileLocator = new FileLocator($this->metadataDirs);
405-
$metadataDriver = new DriverChain([
406-
new YamlDriver($fileLocator, $expressionEvaluator, $this->chainProvider, $typeParser),
407-
new XmlDriver($fileLocator, $expressionEvaluator, $this->chainProvider, $typeParser),
408-
new AnnotationDriver($annotationReader, $expressionEvaluator, $this->chainProvider, $typeParser),
409-
]);
410-
} else {
411-
$metadataDriver = new AnnotationDriver($annotationReader, $expressionEvaluator, $this->chainProvider, $typeParser);
408+
$fileLocator = new FileLocator($this->metadataDirs);
409+
$drivers[] = new YamlDriver($fileLocator, $expressionEvaluator, $this->chainProvider, $typeParser);
410+
$drivers[] = new XmlDriver($fileLocator, $expressionEvaluator, $this->chainProvider, $typeParser);
412411
}
413412

414-
$metadataDriver = new ExtensionDriver($metadataDriver, $this->configurationExtensions);
413+
$metadataDriver = new ExtensionDriver(new DriverChain($drivers), $this->configurationExtensions);
415414
$metadataFactory = new MetadataFactory($metadataDriver, null, $this->debug);
416415
$metadataFactory->setIncludeInterfaces($this->includeInterfaceMetadata);
417416

src/Representation/AbstractSegmentedRepresentation.php

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/**
1010
* @Serializer\ExclusionPolicy("all")
1111
*/
12+
#[Serializer\ExclusionPolicy('all')]
1213
abstract class AbstractSegmentedRepresentation extends RouteAwareRepresentation
1314
{
1415
/**
@@ -18,6 +19,9 @@ abstract class AbstractSegmentedRepresentation extends RouteAwareRepresentation
1819
*
1920
* @var int
2021
*/
22+
#[Serializer\Expose]
23+
#[Serializer\Type('integer')]
24+
#[Serializer\XmlAttribute]
2125
private $limit;
2226

2327
/**
@@ -27,6 +31,9 @@ abstract class AbstractSegmentedRepresentation extends RouteAwareRepresentation
2731
*
2832
* @var int
2933
*/
34+
#[Serializer\Expose]
35+
#[Serializer\Type('integer')]
36+
#[Serializer\XmlAttribute]
3037
private $total;
3138

3239
/**

src/Representation/CollectionRepresentation.php

+8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
* embedded = @Hateoas\Embedded("expr(object.getResources())")
1717
* )
1818
*/
19+
#[Serializer\ExclusionPolicy('all')]
20+
#[Serializer\XmlRoot('collection')]
21+
#[Hateoas\Relation(
22+
'items',
23+
embedded: new Hateoas\Embedded(
24+
content: 'expr(object.getResources())',
25+
),
26+
)]
1927
class CollectionRepresentation
2028
{
2129
/**

src/Representation/OffsetRepresentation.php

+46
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,50 @@
5454
* )
5555
* )
5656
*/
57+
#[Serializer\ExclusionPolicy('all')]
58+
#[Serializer\XmlRoot('collection')]
59+
#[Serializer\AccessorOrder(order: 'custom', custom: ['offset', 'limit', 'total'])]
60+
#[Hateoas\Relation(
61+
'first',
62+
href: new Hateoas\Route(
63+
'expr(object.getRoute())',
64+
parameters: 'expr(object.getParameters(0))',
65+
absolute: 'expr(object.isAbsolute())',
66+
),
67+
)]
68+
#[Hateoas\Relation(
69+
'last',
70+
href: new Hateoas\Route(
71+
'expr(object.getRoute())',
72+
parameters: 'expr(object.getParameters((object.getTotal() - 1) - (object.getTotal() - 1) % object.getLimit()))',
73+
absolute: 'expr(object.isAbsolute())',
74+
),
75+
exclusion: new Hateoas\Exclusion(
76+
excludeIf: 'expr(object.getTotal() === null)',
77+
)
78+
)]
79+
#[Hateoas\Relation(
80+
'next',
81+
href: new Hateoas\Route(
82+
name: 'expr(object.getRoute())',
83+
parameters: 'expr(object.getParameters(object.getOffset() + object.getLimit()))',
84+
absolute: 'expr(object.isAbsolute())'
85+
),
86+
exclusion: new Hateoas\Exclusion(
87+
excludeIf: 'expr(object.getTotal() !== null && (object.getOffset() + object.getLimit()) >= object.getTotal())',
88+
),
89+
)]
90+
#[Hateoas\Relation(
91+
'previous',
92+
href: new Hateoas\Route(
93+
'expr(object.getRoute())',
94+
parameters: 'expr(object.getParameters((object.getOffset() > object.getLimit()) ? object.getOffset() - object.getLimit() : 0))',
95+
absolute: 'expr(object.isAbsolute())',
96+
),
97+
exclusion: new Hateoas\Exclusion(
98+
excludeIf: 'expr(! object.getOffset())',
99+
),
100+
)]
57101
class OffsetRepresentation extends AbstractSegmentedRepresentation
58102
{
59103
/**
@@ -62,6 +106,8 @@ class OffsetRepresentation extends AbstractSegmentedRepresentation
62106
*
63107
* @var int
64108
*/
109+
#[Serializer\Expose]
110+
#[Serializer\XmlAttribute]
65111
private $offset;
66112

67113
/**

0 commit comments

Comments
 (0)