Skip to content

Commit e1c57a3

Browse files
authored
support php 8.1 (#23)
1 parent 8bbd719 commit e1c57a3

File tree

12 files changed

+207
-34
lines changed

12 files changed

+207
-34
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
php-version: ['7.2', '7.3', '7.4', '8.0']
17+
php-version: ['7.2', '7.3', '7.4', '8.0', '8.1']
1818
include:
1919
- php-version: '7.2'
2020
composer-flags: '--prefer-stable --prefer-lowest'
@@ -35,7 +35,7 @@ jobs:
3535
- name: Install dependencies
3636
run: composer update ${{ matrix.composer-flags }} --prefer-dist --no-interaction
3737
- name: PHPStan
38-
if: ${{ matrix.php-version == '8.0' }}
38+
if: ${{ matrix.php-version == '8.1' }}
3939
run: composer phpstan-all
4040
- name: Run tests
4141
run: composer phpunit

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Changelog
22

3-
# 0.4.2
3+
# 0.5.0
44

55
* Support JMS Serializer `ReadOnlyProperty` in addition to `ReadOnly` to be compatible with serializer 3.14 and newer.
6+
* Support PHP 8.1 (which makes ReadOnly a reserved keyword)
67

78
# 0.4.1
89

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
parameters:
22
level: 5
3+
dynamicConstantNames:
4+
- PHP_VERSION_ID
35
paths:
46
- src/
7+
excludePaths:
8+
- src/ModelParser/JMSParserLegacy.php

phpstan.tests.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ parameters:
22
level: 1
33
paths:
44
- tests/
5+
excludePaths:
6+
- tests/ModelParser/JMSParserTestLegacy.php
7+
# looks like phpstan php parser has not implemented union types yet
8+
- tests/ModelParser/Fixtures/IntersectionTypeDeclarationModel.php

src/ModelParser/JMSParser.php

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
namespace Liip\MetadataParser\ModelParser;
66

7+
/*
8+
* We need different class definitions for PHP 8.1 and newer, and one for versions older than 8.1.
9+
* There was a JMS annotation "ReadOnly", but readonly became a reserved keyword in PHP 8.1.
10+
* For PHP 8.0 and older, we must still support the ReadOnly annotation, even with older versions of JMS serializer where ReadOnly does not yet extend ReadOnlyProperty.
11+
* For PHP 8.1, our code can not mention ReadOnly. If we do, we get a parse error.
12+
*/
13+
714
use Doctrine\Common\Annotations\AnnotationException;
815
use Doctrine\Common\Annotations\Reader;
916
use JMS\Serializer\Annotation\Accessor;
@@ -12,8 +19,6 @@
1219
use JMS\Serializer\Annotation\ExclusionPolicy;
1320
use JMS\Serializer\Annotation\Groups;
1421
use JMS\Serializer\Annotation\PostDeserialize;
15-
use JMS\Serializer\Annotation\ReadOnly;
16-
use JMS\Serializer\Annotation\ReadOnlyProperty;
1722
use JMS\Serializer\Annotation\SerializedName;
1823
use JMS\Serializer\Annotation\Since;
1924
use JMS\Serializer\Annotation\Type;
@@ -41,8 +46,10 @@
4146
* Parse JMSSerializer annotations.
4247
*
4348
* Run this parser *after* the PHPDoc parser as JMS annotations are more precise.
49+
*
50+
* @internal
4451
*/
45-
final class JMSParser implements ModelParserInterface
52+
abstract class BaseJMSParser implements ModelParserInterface
4653
{
4754
private const ACCESS_ORDER_CUSTOM = 'custom';
4855

@@ -59,7 +66,7 @@ final class JMSParser implements ModelParserInterface
5966
/**
6067
* @var JMSTypeParser
6168
*/
62-
private $jmsTypeParser;
69+
protected $jmsTypeParser;
6370

6471
public function __construct(Reader $annotationsReader)
6572
{
@@ -218,9 +225,22 @@ private function gatherClassAnnotations(\ReflectionClass $reflectionClass): arra
218225
return $map;
219226
}
220227

228+
/**
229+
* If the annotation is about readonly information, update the $property accordingly and return true.
230+
*
231+
* This check is extracted to have different implementations for PHP 8.1 and newer, and for older PHP versions.
232+
* If the method returns true, we don't try to match the annotation type.
233+
*
234+
* @return bool whether $annotation was a readonly information
235+
*/
236+
abstract protected function parsePropertyAnnotationsReadOnly(object $annotation, PropertyVariationMetadata $property): bool;
237+
221238
private function parsePropertyAnnotations(RawClassMetadata $classMetadata, PropertyVariationMetadata $property, array $annotations): void
222239
{
223240
foreach ($annotations as $annotation) {
241+
if ($this->parsePropertyAnnotationsReadOnly($annotation, $property)) {
242+
continue;
243+
}
224244
switch (true) {
225245
case $annotation instanceof Type:
226246
if (null === $annotation->name) {
@@ -266,11 +286,6 @@ private function parsePropertyAnnotations(RawClassMetadata $classMetadata, Prope
266286
$property->setVersionRange($property->getVersionRange()->withUntil($annotation->version));
267287
break;
268288

269-
case $annotation instanceof ReadOnlyProperty:
270-
case $annotation instanceof ReadOnly: // Deprecated since JMS serializer 3.14 - keep until we conflict with jms serializer < 3.14
271-
$property->setReadOnly($annotation->readOnly);
272-
break;
273-
274289
case $annotation instanceof VirtualProperty:
275290
// we handle this separately
276291
case $annotation instanceof SerializedName:
@@ -408,3 +423,9 @@ private function getMethodName(array $annotations, \ReflectionMethod $reflMethod
408423
return $name;
409424
}
410425
}
426+
427+
if (PHP_VERSION_ID > 80100) {
428+
require 'JMSParser81.php';
429+
} else {
430+
require 'JMSParserLegacy.php';
431+
}

src/ModelParser/JMSParser81.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Liip\MetadataParser\ModelParser;
6+
7+
use JMS\Serializer\Annotation\ReadOnlyProperty;
8+
use Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata;
9+
10+
/**
11+
* Version for PHP 8.1+ without the ReadOnly annotation.
12+
*/
13+
final class JMSParser extends BaseJMSParser
14+
{
15+
protected function parsePropertyAnnotationsReadOnly(object $annotation, PropertyVariationMetadata $property): bool
16+
{
17+
if ($annotation instanceof ReadOnlyProperty) {
18+
$property->setReadOnly($annotation->readOnly);
19+
20+
return true;
21+
}
22+
23+
return false;
24+
}
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Liip\MetadataParser\ModelParser;
6+
7+
use JMS\Serializer\Annotation\ReadOnly;
8+
use JMS\Serializer\Annotation\ReadOnlyProperty;
9+
use Liip\MetadataParser\ModelParser\RawMetadata\PropertyVariationMetadata;
10+
11+
/**
12+
* Version for PHP 8.0 and older, still supporting the ReadOnly annotation
13+
*/
14+
final class JMSParser extends BaseJMSParser
15+
{
16+
protected function parsePropertyAnnotationsReadOnly(object $annotation, PropertyVariationMetadata $property): bool
17+
{
18+
// ReadOnly is deprecated since JMS serializer 3.14.
19+
// In older versions, ReadOnlyProperty does not exist and we need to explicitly check for ReadOnly until we mark this library as conflicting with jms serializer < 3.14
20+
if ($annotation instanceof ReadOnlyProperty
21+
|| $annotation instanceof ReadOnly
22+
) {
23+
$property->setReadOnly($annotation->readOnly);
24+
25+
return true;
26+
}
27+
28+
return false;
29+
}
30+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Tests\Liip\MetadataParser\ModelParser\Fixtures;
4+
5+
use Liip\MetadataParser\ModelParser\ReflectionParser;
6+
use Tests\Liip\MetadataParser\ModelParser\ReflectionParserTest;
7+
8+
class IntersectionTypeDeclarationModel
9+
{
10+
protected ReflectionParserTest&ReflectionParser $property1;
11+
}

tests/ModelParser/JMSParserTest.php

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@
2525

2626
/**
2727
* @small
28+
*
29+
* We need 2 versions of this test to avoid parser errors about ReadOnly
2830
*/
29-
class JMSParserTest extends TestCase
31+
abstract class AbstractJMSParserTest extends TestCase
3032
{
3133
/**
3234
* @var JMSParser
3335
*/
34-
private $parser;
36+
protected $parser;
3537

3638
protected function setUp(): void
3739
{
@@ -260,24 +262,7 @@ public function testInheritedProperty(): void
260262
$this->assertPropertyType(PropertyTypePrimitive::class, 'string|null', true, $property->getType());
261263
}
262264

263-
public function testReadOnly(): void
264-
{
265-
$c = new class() {
266-
/**
267-
* @JMS\ReadOnly
268-
*/
269-
private $property;
270-
};
271-
272-
$classMetadata = new RawClassMetadata(\get_class($c));
273-
$this->parser->parse($classMetadata);
274-
275-
$props = $classMetadata->getPropertyCollections();
276-
$this->assertCount(1, $props, 'Number of properties should match');
277-
278-
$this->assertPropertyCollection('property', 1, $props[0]);
279-
$this->assertPropertyVariation('property', false, true, $props[0]->getVariations()[0]);
280-
}
265+
abstract public function testReadOnlyProperty(): void;
281266

282267
public function testSerializedName(): void
283268
{
@@ -1304,13 +1289,13 @@ public function bar(): void
13041289
$this->assertSame(['foo', 'bar'], $classMetadata->getPostDeserializeMethods());
13051290
}
13061291

1307-
private function assertPropertyCollection(string $serializedName, int $variations, PropertyCollection $prop): void
1292+
protected function assertPropertyCollection(string $serializedName, int $variations, PropertyCollection $prop): void
13081293
{
13091294
$this->assertSame($serializedName, $prop->getSerializedName(), 'Serialized name of property should match');
13101295
$this->assertCount($variations, $prop->getVariations(), "Number of variations of property {$serializedName} should match");
13111296
}
13121297

1313-
private function assertPropertyVariation(string $name, bool $public, bool $readOnly, PropertyVariationMetadata $property): void
1298+
protected function assertPropertyVariation(string $name, bool $public, bool $readOnly, PropertyVariationMetadata $property): void
13141299
{
13151300
$this->assertSame($name, $property->getName(), 'Name of property should match');
13161301
$this->assertSame($public, $property->isPublic(), "Public flag of property {$name} should match");
@@ -1330,3 +1315,9 @@ private function assertPropertyAccessor(?string $getterMethod, ?string $setterMe
13301315
$this->assertSame($setterMethod, $accessor->getSetterMethod(), 'Setter method of property should match');
13311316
}
13321317
}
1318+
1319+
if (PHP_VERSION_ID > 80100) {
1320+
require 'JMSParserTest81.php';
1321+
} else {
1322+
require 'JMSParserTestLegacy.php';
1323+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Liip\MetadataParser\ModelParser;
6+
7+
use JMS\Serializer\Annotation as JMS;
8+
use Liip\MetadataParser\ModelParser\RawMetadata\RawClassMetadata;
9+
10+
/**
11+
* @small
12+
*/
13+
class JMSParserTest extends AbstractJMSParserTest
14+
{
15+
public function testReadOnlyProperty(): void
16+
{
17+
$c = new class() {
18+
/**
19+
* @JMS\ReadOnlyProperty()
20+
*/
21+
private $property;
22+
};
23+
24+
$classMetadata = new RawClassMetadata(\get_class($c));
25+
$this->parser->parse($classMetadata);
26+
27+
$props = $classMetadata->getPropertyCollections();
28+
$this->assertCount(1, $props, 'Number of properties should match');
29+
30+
$this->assertPropertyCollection('property', 1, $props[0]);
31+
$this->assertPropertyVariation('property', false, true, $props[0]->getVariations()[0]);
32+
}
33+
}

0 commit comments

Comments
 (0)