Skip to content
Open
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
59 changes: 59 additions & 0 deletions Exception/NoSuchAccessorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/*
* This file is part of the 4devs Serialiser package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FDevs\Serializer\Exception;

class NoSuchAccessorException extends NoSuchMetadataException
{
/**
* @var string
*/
private $className;
/**
* @var string
*/
private $propertyName;
/**
* @var array
*/
private $context;

/**
* {@inheritdoc}
*/
public function __construct(string $className, string $propertyName, array $context, \Throwable $previous = null)
{
$message = sprintf('Accessor for the class name "%s" and property name "%s" and context "%s" not found', $className, $propertyName);
parent::__construct($message, 0, $previous);
}

/**
* @return string
*/
public function getClassName(): string
{
return $this->className;
}

/**
* @return string
*/
public function getPropertyName(): string
{
return $this->propertyName;
}

/**
* @return array
*/
public function getContext(): array
{
return $this->context;
}
}
68 changes: 68 additions & 0 deletions Mapping/Factory/ClassMetadataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/*
* This file is part of the 4devs Serialiser package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FDevs\Serializer\Mapping\Factory;

use FDevs\Serializer\Mapping\ClassMetadata;
use FDevs\Serializer\Mapping\ClassMetadataInterface;
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;

class ClassMetadataFactory implements MetadataFactoryInterface
{
use ClassResolverTrait;
use KeyResolverTrait;
/**
* @var PropertyListExtractorInterface
*/
private $propertyListExtractor;

/**
* @var PropertyMetadataFactoryInterface
*/
private $propertyMetadataFactory;

/**
* @var ClassMetadataInterface[]
*/
private $metas = [];

/**
* {@inheritdoc}
*/
public function getMetadataFor($value, array $context = []): ClassMetadataInterface
{
$key = $this->getKeyPrefix($value, $context);
if (empty($this->metas[$key])) {
$class = $this->getClass($value);
$meta = new ClassMetadata($class);
$properties = $this->getProperties($value, $context);
foreach ($properties as $property) {
if ($this->propertyMetadataFactory->hasMetadataFor($value, $property, $context)) {
$meta->addPropertyMetadata($this->propertyMetadataFactory->getMetadataFor($value, $property, $context));
}
}
$this->metas[$key] = $meta;
}

return $this->metas[$key];
}

/**
* {@inheritdoc}
*/
public function hasMetadataFor($value, array $context = []): bool
{
return (is_object($value) || is_string($value)) && null !== $this->getProperties($value, $context);
}

private function getProperties($value, array $context = []): ?array
{
return $this->propertyListExtractor->getProperties($this->getClass($value), $context);
}
}
24 changes: 24 additions & 0 deletions Mapping/Factory/KeyResolverTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the 4devs Serialiser package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FDevs\Serializer\Mapping\Factory;

trait KeyResolverTrait
{
/**
* @param object|string $value
* @param array $context
*
* @return string
*/
private function getKeyPrefix($value, array $context): string
{
return 'FDevs_'.md5(serialize([$value, $context]));
}
}
7 changes: 7 additions & 0 deletions Mapping/Factory/MetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
use FDevs\Serializer\Mapping\ClassMetadataInterface;
use FDevs\Serializer\Mapping\Loader\LoaderInterface;

@trigger_error('The "FDevs\Serializer\Mapping\Factory\MetadataFactory" service is deprecated. You should use FDevs\Serializer\Mapping\Factory\ClassMetadataFactory.', E_USER_DEPRECATED);

/**
* Class MetadataFactory.
*
* @deprecated
*/
class MetadataFactory implements MetadataFactoryInterface
{
/**
Expand Down
142 changes: 142 additions & 0 deletions Mapping/Factory/PropertyMetadataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

/*
* This file is part of the 4devs Serialiser package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FDevs\Serializer\Mapping\Factory;

use FDevs\Serializer\Exception\NoSuchAccessorException;
use FDevs\Serializer\Mapping\Guess\AccessorGuesserInterface;
use FDevs\Serializer\Mapping\Guess\GuessInterface;
use FDevs\Serializer\Mapping\Guess\TypeGuesserInterface;
use FDevs\Serializer\Mapping\Guess\TypeGuessInterface;
use FDevs\Serializer\Mapping\Metadata;
use FDevs\Serializer\Mapping\NameConverterInterface;
use FDevs\Serializer\Mapping\PropertyMetadata;
use FDevs\Serializer\Mapping\PropertyMetadataInterface;
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;

class PropertyMetadataFactory implements PropertyMetadataFactoryInterface
{
use ClassResolverTrait;
use KeyResolverTrait;

/**
* @var TypeGuesserInterface
*/
private $typeGuesser;

/**
* @var PropertyAccessExtractorInterface
*/
private $propertyAccessExtractor;

/**
* @var NameConverterInterface
*/
private $nameConverter;
/**
* @var array|TypeGuessInterface[]
*/
private $types = [];

/**
* @var array|GuessInterface[]
*/
private $accessors = [];

/**
* @var AccessorGuesserInterface
*/
private $accessorGuesser;

/**
* PropertyMetadataFactory constructor.
*
* @param TypeGuesserInterface $typeGuesser
* @param AccessorGuesserInterface $accessorGuesser
* @param PropertyAccessExtractorInterface $propertyAccessExtractor
* @param NameConverterInterface $nameConverter
*/
public function __construct(
TypeGuesserInterface $typeGuesser,
AccessorGuesserInterface $accessorGuesser,
PropertyAccessExtractorInterface $propertyAccessExtractor,
NameConverterInterface $nameConverter)
{
$this->typeGuesser = $typeGuesser;
$this->propertyAccessExtractor = $propertyAccessExtractor;
$this->nameConverter = $nameConverter;
$this->accessorGuesser = $accessorGuesser;
}

/**
* {@inheritdoc}
*/
public function getMetadataFor($value, string $propertyName, array $context = []): PropertyMetadataInterface
{
$meta = new PropertyMetadata($this->nameConverter->convert($value, $propertyName, $context));
$type = $this->guessType($value, $propertyName, $context);
$accessor = $this->guessAccessor($value, $propertyName, $context);
$meta
->setType(new Metadata($type->getName(), $type->getOptions()))
->setNullable($type->isNullable())
->setAccessor(new Metadata($accessor->getName(), $accessor->getOptions()));

return $meta;
}

/**
* {@inheritdoc}
*/
public function hasMetadataFor($value, string $propertyName, array $context = []): bool
{
return (is_object($value) || is_string($value))
&& $this->propertyAccessExtractor->isReadable($this->getClass($value), $propertyName, $context)
&& null !== $this->guessType($value, $propertyName, $context);
}

/**
* @param object|string $value
* @param string $propertyName
* @param array $context
*
* @return TypeGuessInterface|null
*/
private function guessType($value, string $propertyName, array $context)
{
$key = $this->getKeyPrefix($value, $context).'_'.$propertyName;
if (empty($this->types[$key])) {
$this->types[$key] = $this->typeGuesser->guessType($this->getClass($value), $propertyName, $context);
}

return $this->types[$key];
}

/**
* @param object|string $value
* @param string $propertyName
* @param array $context
*
* @throws NoSuchAccessorException
*
* @return GuessInterface
*/
private function guessAccessor($value, string $propertyName, array $context)
{
$key = $this->getKeyPrefix($value, $context).'_'.$propertyName;
if (empty($this->accessors[$key])) {
$className = $this->getClass($value);
$this->accessors[$key] = $this->accessorGuesser->guessAccessor($className, $propertyName, $context);
if (null === $this->accessors[$key]) {
throw new NoSuchAccessorException($className, $propertyName, $context);
}
}

return $this->accessors[$key];
}
}
40 changes: 40 additions & 0 deletions Mapping/Factory/PropertyMetadataFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/*
* This file is part of the 4devs Serialiser package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FDevs\Serializer\Mapping\Factory;

use FDevs\Serializer\Exception\NoSuchMetadataException;
use FDevs\Serializer\Mapping\PropertyMetadataInterface;

interface PropertyMetadataFactoryInterface
{
/**
* Returns the metadata for the given value.
*
* @param mixed $value Some object or class name
* @param string $propertyName
* @param array $context
*
* @throws NoSuchMetadataException If no metadata exists for the given value
*
* @return PropertyMetadataInterface The metadata for the value
*/
public function getMetadataFor($value, string $propertyName, array $context = []): PropertyMetadataInterface;

/**
* Returns whether the property to return metadata for the given value.
*
* @param mixed $value
* @param string $propertyName
* @param array $context
*
* @return bool Whether metadata can be returned for that value
*/
public function hasMetadataFor($value, string $propertyName, array $context = []): bool;
}
Loading