Skip to content

Commit

Permalink
Don't return a metadata object unless there is explicit annotation or…
Browse files Browse the repository at this point in the history
… attribute configuration, add the NullDriver to the default driver chain to ensure a minimally configured metadata object is created
  • Loading branch information
mbabker committed Jun 13, 2023
1 parent bb647e8 commit a41e7cd
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 21 deletions.
9 changes: 7 additions & 2 deletions src/Builder/DefaultDriverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use JMS\Serializer\Metadata\Driver\AttributeDriver;
use JMS\Serializer\Metadata\Driver\DefaultValuePropertyDriver;
use JMS\Serializer\Metadata\Driver\EnumPropertiesDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Metadata\Driver\TypedPropertiesDriver;
use JMS\Serializer\Metadata\Driver\XmlDriver;
use JMS\Serializer\Metadata\Driver\YamlDriver;
Expand Down Expand Up @@ -56,11 +57,13 @@ public function enableEnumSupport(bool $enableEnumSupport = true): void

public function createDriver(array $metadataDirs, Reader $annotationReader): DriverInterface
{
$driver = new DriverChain();

if (PHP_VERSION_ID >= 80000) {
$annotationReader = new AttributeDriver\AttributeReader($annotationReader);
$driver->addDriver(new AttributeDriver($this->propertyNamingStrategy, $this->typeParser, $this->expressionEvaluator));
}

$driver = new AnnotationDriver($annotationReader, $this->propertyNamingStrategy, $this->typeParser);
$driver->addDriver(new AnnotationDriver($annotationReader, $this->propertyNamingStrategy, $this->typeParser));

if (!empty($metadataDirs)) {
$fileLocator = new FileLocator($metadataDirs);
Expand All @@ -71,6 +74,8 @@ public function createDriver(array $metadataDirs, Reader $annotationReader): Dri
]);
}

$driver->addDriver(new NullDriver($this->propertyNamingStrategy));

if ($this->enableEnumSupport) {
$driver = new EnumPropertiesDriver($driver);
}
Expand Down
12 changes: 12 additions & 0 deletions src/Metadata/Driver/AnnotationOrAttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public function __construct(PropertyNamingStrategyInterface $namingStrategy, ?Pa

public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadata
{
$configured = false;

$classMetadata = new ClassMetadata($name = $class->name);
$fileResource = $class->getFilename();

Expand All @@ -86,6 +88,8 @@ public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadat
$readOnlyClass = false;

foreach ($this->getClassAnnotations($class) as $annot) {
$configured = true;

if ($annot instanceof ExclusionPolicy) {
$exclusionPolicy = $annot->policy;
} elseif ($annot instanceof XmlRoot) {
Expand Down Expand Up @@ -135,6 +139,8 @@ public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadat
$methodAnnotations = $this->getMethodAnnotations($method);

foreach ($methodAnnotations as $annot) {
$configured = true;

if ($annot instanceof PreSerialize) {
$classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
continue 2;
Expand Down Expand Up @@ -174,6 +180,8 @@ public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadat
$propertyAnnotations = $propertiesAnnotations[$propertyKey];

foreach ($propertyAnnotations as $annot) {
$configured = true;

if ($annot instanceof Since) {
$propertyMetadata->sinceVersion = $annot->version;
} elseif ($annot instanceof Until) {
Expand Down Expand Up @@ -274,6 +282,10 @@ public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadat
}
}

if (!$configured) {
return null;
}

return $classMetadata;
}

Expand Down
26 changes: 26 additions & 0 deletions src/Metadata/Driver/NullDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@
namespace JMS\Serializer\Metadata\Driver;

use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
use Metadata\ClassMetadata as BaseClassMetadata;
use Metadata\Driver\DriverInterface;

class NullDriver implements DriverInterface
{
/**
* @var PropertyNamingStrategyInterface
*/
private $namingStrategy;

public function __construct(PropertyNamingStrategyInterface $namingStrategy)
{
$this->namingStrategy = $namingStrategy;
}

public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadata
{
$classMetadata = new ClassMetadata($name = $class->name);
Expand All @@ -18,6 +30,20 @@ public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadat
$classMetadata->fileResources[] = $fileResource;
}

foreach ($class->getProperties() as $property) {
if ($property->class !== $name || (isset($property->info) && $property->info['class'] !== $name)) {
continue;
}

$propertyMetadata = new PropertyMetadata($name, $property->getName());

if (!$propertyMetadata->serializedName) {
$propertyMetadata->serializedName = $this->namingStrategy->translateName($propertyMetadata);
}

$classMetadata->addPropertyMetadata($propertyMetadata);
}

return $classMetadata;
}
}
5 changes: 3 additions & 2 deletions tests/Fixtures/AuthorDeprecatedReadOnly.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace JMS\Serializer\Tests\Fixtures;

use JMS\Serializer\Annotation\Accessor;
use JMS\Serializer\Annotation\ReadOnly;
use JMS\Serializer\Annotation\DeprecatedReadOnly;
use JMS\Serializer\Annotation\SerializedName;
use JMS\Serializer\Annotation\Type;
use JMS\Serializer\Annotation\XmlRoot;
Expand All @@ -19,9 +19,10 @@
class AuthorDeprecatedReadOnly
{
/**
* @JMS\Serializer\Annotation\ReadOnly
* @DeprecatedReadOnly
* @SerializedName("id")
*/
#[DeprecatedReadOnly]
#[SerializedName(name: 'id')]
private $id;

Expand Down
11 changes: 7 additions & 4 deletions tests/Fixtures/AuthorDeprecatedReadOnlyPerClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace JMS\Serializer\Tests\Fixtures;

use JMS\Serializer\Annotation\Accessor;
use JMS\Serializer\Annotation\ReadOnly;
use JMS\Serializer\Annotation\DeprecatedReadOnly;
use JMS\Serializer\Annotation\SerializedName;
use JMS\Serializer\Annotation\Type;
use JMS\Serializer\Annotation\XmlRoot;
Expand All @@ -14,15 +14,17 @@
* @deprecated ReadOnly annotation is deprecated
*
* @XmlRoot("author")
* @ReadOnly
* @DeprecatedReadOnly
*/
#[XmlRoot(name: 'author')]
#[DeprecatedReadOnly]
class AuthorDeprecatedReadOnlyPerClass
{
/**
* @ReadOnly
* @DeprecatedReadOnly
* @SerializedName("id")
*/
#[DeprecatedReadOnly]
#[SerializedName(name: 'id')]
private $id;

Expand All @@ -36,11 +38,12 @@ public function __construct($id, $name)
* @Type("string")
* @SerializedName("full_name")
* @Accessor("getName")
* @ReadOnly(false)
* @DeprecatedReadOnly(false)
*/
#[Type(name: 'string')]
#[SerializedName(name: 'full_name')]
#[Accessor(getter: 'getName')]
#[DeprecatedReadOnly(readOnly: false)]
private $name;

public function getId()
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixtures/Doctrine/PersistendCollection/SmartPhone.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class SmartPhone
*
* @var string
*/
#[Serializer\SerializedName(name: 'id')]
#[Serializer\Type(name: 'string')]
protected $id;

Expand All @@ -40,6 +41,7 @@ class SmartPhone
*
* @var ArrayCollection<int, App>
*/
#[Serializer\SerializedName(name: 'applications')]
#[Serializer\Type(name: 'ArrayCollection<JMS\Serializer\Tests\Fixtures\Doctrine\PersistendCollection\App>')]
private $apps;

Expand Down
3 changes: 3 additions & 0 deletions tests/Fixtures/ObjectWithLifecycleCallbacks.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function __construct($firstname = 'Foo', $lastname = 'Bar')
/**
* @PreSerialize
*/
#[PreSerialize]
private function prepareForSerialization()
{
$this->name = $this->firstname . ' ' . $this->lastname;
Expand All @@ -47,6 +48,7 @@ private function prepareForSerialization()
/**
* @PostSerialize
*/
#[PostSerialize]
private function cleanUpAfterSerialization()
{
$this->name = null;
Expand All @@ -55,6 +57,7 @@ private function cleanUpAfterSerialization()
/**
* @PostDeserialize
*/
#[PostDeserialize]
private function afterDeserialization()
{
[$this->firstname, $this->lastname] = explode(' ', $this->name);
Expand Down
9 changes: 8 additions & 1 deletion tests/Metadata/Driver/AnnotationDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@

use Doctrine\Common\Annotations\AnnotationReader;
use JMS\Serializer\Metadata\Driver\AnnotationDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use Metadata\Driver\DriverChain;
use Metadata\Driver\DriverInterface;

class AnnotationDriverTest extends BaseAnnotationOrAttributeDriverTestCase
{
protected function getDriver(?string $subDir = null, bool $addUnderscoreDir = true): DriverInterface
{
return new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy(), null, $this->getExpressionEvaluator());
$namingStrategy = new IdenticalPropertyNamingStrategy();

return new DriverChain([
new AnnotationDriver(new AnnotationReader(), $namingStrategy, null, $this->getExpressionEvaluator()),
new NullDriver($namingStrategy),
]);
}
}
9 changes: 8 additions & 1 deletion tests/Metadata/Driver/AttributeDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace JMS\Serializer\Tests\Metadata\Driver;

use JMS\Serializer\Metadata\Driver\AttributeDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use Metadata\Driver\DriverChain;
use Metadata\Driver\DriverInterface;

class AttributeDriverTest extends BaseAnnotationOrAttributeDriverTestCase
Expand All @@ -21,6 +23,11 @@ protected function setUp(): void

protected function getDriver(?string $subDir = null, bool $addUnderscoreDir = true): DriverInterface
{
return new AttributeDriver(new IdenticalPropertyNamingStrategy(), null, $this->getExpressionEvaluator());
$namingStrategy = new IdenticalPropertyNamingStrategy();

return new DriverChain([
new AttributeDriver($namingStrategy, null, $this->getExpressionEvaluator()),
new NullDriver($namingStrategy),
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
namespace JMS\Serializer\Tests\Metadata\Driver;

use JMS\Serializer\Tests\Fixtures\AllExcludedObject;
use Metadata\Driver\DriverInterface;

abstract class BaseAnnotationOrAttributeDriverTestCase extends BaseDriverTestCase
{
abstract protected function getDriver(?string $subDir = null, bool $addUnderscoreDir = true): DriverInterface;

public function testAllExcluded(): void
{
$a = new AllExcludedObject();
Expand Down
15 changes: 11 additions & 4 deletions tests/Metadata/Driver/DocBlockDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\Driver\AnnotationDriver;
use JMS\Serializer\Metadata\Driver\DocBlockDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Metadata\Driver\TypedPropertiesDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use JMS\Serializer\Tests\Fixtures\DocBlockType\Collection\CollectionAsList;
Expand Down Expand Up @@ -46,19 +47,25 @@
use JMS\Serializer\Tests\Fixtures\DocBlockType\SingleClassFromGlobalNamespaceTypeHint;
use JMS\Serializer\Tests\Fixtures\DocBlockType\UnionTypedDocBLockProperty;
use JMS\Serializer\Tests\Fixtures\DocBlockType\VirtualPropertyGetter;
use Metadata\Driver\DriverChain;
use PHPUnit\Framework\TestCase;

class DocBlockDriverTest extends TestCase
{
private function resolve(string $classToResolve): ClassMetadata
{
$namingStrategy = new IdenticalPropertyNamingStrategy();

$driver = new DriverChain([
new AnnotationDriver(new AnnotationReader(), $namingStrategy),
new NullDriver($namingStrategy),
]);

if (PHP_VERSION_ID > 70400) {
$baseDriver = new TypedPropertiesDriver(new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy()));
} else {
$baseDriver = new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy());
$driver = new TypedPropertiesDriver($driver);
}

$driver = new DocBlockDriver($baseDriver);
$driver = new DocBlockDriver($driver);

$m = $driver->loadMetadataForClass(new \ReflectionClass($classToResolve));
self::assertNotNull($m);
Expand Down
3 changes: 2 additions & 1 deletion tests/Metadata/Driver/NullDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use PHPUnit\Framework\TestCase;

class NullDriverTest extends TestCase
{
public function testReturnsValidMetadata()
{
$driver = new NullDriver();
$driver = new NullDriver(new IdenticalPropertyNamingStrategy());

$metadata = $driver->loadMetadataForClass(new \ReflectionClass('stdClass'));

Expand Down
12 changes: 10 additions & 2 deletions tests/Metadata/Driver/UnionTypedPropertiesDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use Doctrine\Common\Annotations\AnnotationReader;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\Driver\AnnotationDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Metadata\Driver\TypedPropertiesDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use JMS\Serializer\Tests\Fixtures\TypedProperties\UnionTypedProperties;
use Metadata\Driver\DriverChain;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

Expand All @@ -34,8 +36,14 @@ public function testInferUnionTypesShouldResultInNoType()

private function resolve(string $classToResolve): ClassMetadata
{
$baseDriver = new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy());
$driver = new TypedPropertiesDriver($baseDriver);
$namingStrategy = new IdenticalPropertyNamingStrategy();

$driver = new DriverChain([
new AnnotationDriver(new AnnotationReader(), $namingStrategy),
new NullDriver($namingStrategy),
]);

$driver = new TypedPropertiesDriver($driver);

$m = $driver->loadMetadataForClass(new ReflectionClass($classToResolve));
self::assertNotNull($m);
Expand Down
11 changes: 10 additions & 1 deletion tests/Serializer/GraphNavigatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
use JMS\Serializer\Handler\HandlerRegistry;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\Metadata\Driver\AnnotationDriver;
use JMS\Serializer\Metadata\Driver\NullDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
use JMS\Serializer\Visitor\SerializationVisitorInterface;
use JMS\Serializer\VisitorInterface;
use Metadata\Driver\DriverChain;
use Metadata\MetadataFactory;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -243,7 +245,14 @@ protected function setUp(): void
$this->handlerRegistry = new HandlerRegistry();
$this->objectConstructor = new UnserializeObjectConstructor();

$this->metadataFactory = new MetadataFactory(new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy()));
$namingStrategy = new IdenticalPropertyNamingStrategy();

$driver = new DriverChain([
new AnnotationDriver(new AnnotationReader(), $namingStrategy),
new NullDriver($namingStrategy),
]);

$this->metadataFactory = new MetadataFactory($driver);

$this->serializationNavigator = new SerializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->accessor, $this->dispatcher);
$this->serializationNavigator->initialize($this->serializationVisitor, $this->context);
Expand Down

0 comments on commit a41e7cd

Please sign in to comment.