WIP: php 7.4 support
This commit is contained in:
parent
8ced4dc8a1
commit
f53f545c22
@ -26,7 +26,7 @@
|
||||
"php-http/message-factory": "^1.0",
|
||||
"php-http/discovery": "^1.13",
|
||||
"doctrine/annotations": "^1.13|^2.0",
|
||||
"liip/serializer": "2.6.*",
|
||||
"liip/serializer": "2.2.* || 2.6.*",
|
||||
"php-http/httplug": "^2.2",
|
||||
"civicrm/composer-compile-plugin": "^0.18.0",
|
||||
"symfony/console": "^4.0|^5.0|^6.0",
|
||||
|
@ -253,6 +253,15 @@ final class DeserializerGenerator
|
||||
);
|
||||
}
|
||||
|
||||
private function isArrayTraversable(PropertyTypeArray $array): bool
|
||||
{
|
||||
if (method_exists($array, 'isCollection')) {
|
||||
return $array->isCollection();
|
||||
}
|
||||
|
||||
return $array->isTraversable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, positive-int> $stack
|
||||
*/
|
||||
@ -266,13 +275,23 @@ final class DeserializerGenerator
|
||||
|
||||
switch ($type) {
|
||||
case $type instanceof PropertyTypeArray:
|
||||
if ($type->isTraversable()) {
|
||||
if ($this->isArrayTraversable($type)) {
|
||||
return $this->generateCodeForArrayCollection($propertyMetadata, $type, $arrayPath, $modelPropertyPath, $stack);
|
||||
}
|
||||
|
||||
return $this->generateCodeForArray($type, $arrayPath, $modelPropertyPath, $stack);
|
||||
|
||||
case $type instanceof PropertyTypeDateTime:
|
||||
if (method_exists($type, 'getDeserializeFormat')) {
|
||||
$format = $type->getDeserializeFormat();
|
||||
|
||||
if (null !== $format) {
|
||||
return $this->templating->renderAssignDateTimeFromFormat($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath, $format, $type->getZone());
|
||||
}
|
||||
|
||||
return $this->templating->renderAssignDateTimeToField($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath);
|
||||
}
|
||||
|
||||
$formats = $type->getDeserializeFormats() ?: (\is_string($type->getFormat()) ? [$type->getFormat()] : $type->getFormat());
|
||||
if (null !== $formats) {
|
||||
return $this->templating->renderAssignDateTimeFromFormat($type->isImmutable(), (string) $modelPropertyPath, (string) $arrayPath, $formats, $type->getZone());
|
||||
|
@ -16,15 +16,21 @@ final class Parser implements ParserInterface
|
||||
*/
|
||||
private Lexer $lexer;
|
||||
|
||||
private ?Token $token = null;
|
||||
|
||||
private string $input;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $root = true;
|
||||
|
||||
public function parse(string $string): array
|
||||
public function parse(string $type): array
|
||||
{
|
||||
$this->input = $type;
|
||||
|
||||
$this->lexer = new Lexer();
|
||||
$this->lexer->setInput($string);
|
||||
$this->lexer->setInput($type);
|
||||
$this->lexer->moveNext();
|
||||
|
||||
return $this->visit();
|
||||
@ -33,7 +39,7 @@ final class Parser implements ParserInterface
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function visit()
|
||||
private function visit(bool $fetchingParam = false)
|
||||
{
|
||||
$this->lexer->moveNext();
|
||||
|
||||
@ -43,15 +49,44 @@ final class Parser implements ParserInterface
|
||||
);
|
||||
}
|
||||
|
||||
if (Lexer::T_FLOAT === $this->lexer->token->type) {
|
||||
return floatval($this->lexer->token->value);
|
||||
} elseif (Lexer::T_INTEGER === $this->lexer->token->type) {
|
||||
return intval($this->lexer->token->value);
|
||||
} elseif (Lexer::T_NULL === $this->lexer->token->type) {
|
||||
if (is_array($this->lexer->token)) {
|
||||
$this->token = Token::fromArray($this->lexer->token);
|
||||
} else {
|
||||
$this->token = Token::fromObject($this->lexer->token);
|
||||
}
|
||||
|
||||
if ("" === $this->token->value && $fetchingParam) {
|
||||
$len = 0;
|
||||
$this->lexer->moveNext();
|
||||
|
||||
while (true) {
|
||||
if (is_array($this->lexer->token)) {
|
||||
$this->token = Token::fromArray($this->lexer->token);
|
||||
} else {
|
||||
$this->token = Token::fromObject($this->lexer->token);
|
||||
}
|
||||
|
||||
if ("" === $this->token->value) {
|
||||
$len++;
|
||||
break;
|
||||
}
|
||||
|
||||
$len += strlen($this->token->value);
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
return substr($this->input, 9, $len + substr_count($this->input, ' '));
|
||||
}
|
||||
|
||||
if (Lexer::T_FLOAT === $this->token->type) {
|
||||
return floatval($this->token->value);
|
||||
} elseif (Lexer::T_INTEGER === $this->token->type) {
|
||||
return intval($this->token->value);
|
||||
} elseif (Lexer::T_NULL === $this->token->type) {
|
||||
return null;
|
||||
} elseif (Lexer::T_STRING === $this->lexer->token->type) {
|
||||
return $this->lexer->token->value;
|
||||
} elseif (Lexer::T_IDENTIFIER === $this->lexer->token->type) {
|
||||
} elseif (Lexer::T_STRING === $this->token->type) {
|
||||
return $this->token->value;
|
||||
} elseif (Lexer::T_IDENTIFIER === $this->token->type) {
|
||||
if ($this->lexer->isNextToken(Lexer::T_TYPE_START)) {
|
||||
return $this->visitCompoundType();
|
||||
} elseif ($this->lexer->isNextToken(Lexer::T_ARRAY_START)) {
|
||||
@ -59,14 +94,14 @@ final class Parser implements ParserInterface
|
||||
}
|
||||
|
||||
return $this->visitSimpleType();
|
||||
} elseif (!$this->root && Lexer::T_ARRAY_START === $this->lexer->token->type) {
|
||||
} elseif (!$this->root && Lexer::T_ARRAY_START === $this->token->type) {
|
||||
return $this->visitArrayType();
|
||||
}
|
||||
|
||||
throw new SyntaxError(sprintf(
|
||||
'Syntax error, unexpected "%s" (%s)',
|
||||
$this->lexer->token->value,
|
||||
$this->getConstant($this->lexer->token->type),
|
||||
$this->token->value,
|
||||
$this->getConstant($this->token->type),
|
||||
));
|
||||
}
|
||||
|
||||
@ -75,7 +110,7 @@ final class Parser implements ParserInterface
|
||||
*/
|
||||
private function visitSimpleType()
|
||||
{
|
||||
$value = $this->lexer->token->value;
|
||||
$value = $this->token->value;
|
||||
|
||||
return ['name' => $value, 'params' => []];
|
||||
}
|
||||
@ -83,13 +118,13 @@ final class Parser implements ParserInterface
|
||||
private function visitCompoundType(): array
|
||||
{
|
||||
$this->root = false;
|
||||
$name = $this->lexer->token->value;
|
||||
$name = $this->token->value;
|
||||
$this->match(Lexer::T_TYPE_START);
|
||||
|
||||
$params = [];
|
||||
if (!$this->lexer->isNextToken(Lexer::T_TYPE_END)) {
|
||||
while (true) {
|
||||
$params[] = $this->visit();
|
||||
$params[] = $this->visit(true);
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_TYPE_END)) {
|
||||
break;
|
||||
@ -139,7 +174,13 @@ final class Parser implements ParserInterface
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->lexer->lookahead->type === $token) {
|
||||
if (is_array($this->lexer->lookahead)) {
|
||||
$lookahead = Token::fromArray($this->lexer->lookahead);
|
||||
} else {
|
||||
$lookahead = Token::fromObject($this->lexer->lookahead);
|
||||
}
|
||||
|
||||
if ($lookahead->type === $token) {
|
||||
$this->lexer->moveNext();
|
||||
|
||||
return;
|
||||
@ -147,8 +188,8 @@ final class Parser implements ParserInterface
|
||||
|
||||
throw new SyntaxError(sprintf(
|
||||
'Syntax error, unexpected "%s" (%s), expected was %s',
|
||||
$this->lexer->lookahead->value,
|
||||
$this->getConstant($this->lexer->lookahead->type),
|
||||
$lookahead->value,
|
||||
$this->getConstant($lookahead->type),
|
||||
$this->getConstant($token),
|
||||
));
|
||||
}
|
||||
|
64
src/Component/Serializer/Parser/JMSCore/Type/Token.php
Normal file
64
src/Component/Serializer/Parser/JMSCore/Type/Token.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @template T of string|int
|
||||
* @template V of string|int
|
||||
*/
|
||||
final class Token
|
||||
{
|
||||
public static function fromArray(array $source): Token
|
||||
{
|
||||
return new self($source['value'] ?? '', $source['type'] ?? '', $source['position'] ?? '');
|
||||
}
|
||||
|
||||
public static function fromObject(object $source): Token
|
||||
{
|
||||
return new self($source->value, $source->type, $source->position);
|
||||
}
|
||||
|
||||
/**
|
||||
* The string value of the token in the input string
|
||||
*
|
||||
* @readonly
|
||||
* @var string|int
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* The type of the token (identifier, numeric, string, input parameter, none)
|
||||
*
|
||||
* @readonly
|
||||
* @var T|null
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The position of the token in the input string
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
public int $position;
|
||||
|
||||
/**
|
||||
* @param string|int $value
|
||||
* @param string|int $type
|
||||
*/
|
||||
public function __construct($value, $type, int $position)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
/** @param T ...$types */
|
||||
public function isA(...$types): bool
|
||||
{
|
||||
return in_array($this->type, $types, true);
|
||||
}
|
||||
}
|
@ -7,11 +7,13 @@ namespace RetailCrm\Api\Component\Serializer\Parser;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Liip\MetadataParser\Exception\InvalidTypeException;
|
||||
use Liip\MetadataParser\Metadata\AbstractPropertyType;
|
||||
use Liip\MetadataParser\Metadata\DateTimeOptions;
|
||||
use Liip\MetadataParser\Metadata\PropertyType;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypeClass;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypeDateTime;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypeIterable;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypeArray;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypePrimitive;
|
||||
use Liip\MetadataParser\Metadata\PropertyTypeUnknown;
|
||||
use RetailCrm\Api\Component\Serializer\Parser\JMSCore\Type\Parser;
|
||||
@ -28,9 +30,13 @@ final class JMSTypeParser
|
||||
*/
|
||||
private Parser $jmsTypeParser;
|
||||
|
||||
private $useArrayDateFormat = true;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->jmsTypeParser = new Parser();
|
||||
$this->useArrayDateFormat = null === (new \ReflectionClass(DateTimeOptions::class))
|
||||
->getConstructor()->getParameters()[2]->getType();
|
||||
}
|
||||
|
||||
public function parse(string $rawType): PropertyType
|
||||
@ -57,7 +63,7 @@ final class JMSTypeParser
|
||||
|
||||
if (0 === \count($typeInfo['params'])) {
|
||||
if (self::TYPE_ARRAY === $typeInfo['name']) {
|
||||
return new PropertyTypeIterable(new PropertyTypeUnknown(false), false, $nullable);
|
||||
return self::iterableType(new PropertyTypeUnknown(false), false, $nullable);
|
||||
}
|
||||
|
||||
if (PropertyTypePrimitive::isTypePrimitive($typeInfo['name'])) {
|
||||
@ -77,10 +83,10 @@ final class JMSTypeParser
|
||||
$collectionClass = $this->getCollectionClass($typeInfo['name']);
|
||||
if (self::TYPE_ARRAY === $typeInfo['name'] || $collectionClass) {
|
||||
if (1 === \count($typeInfo['params'])) {
|
||||
return new PropertyTypeIterable($this->parseType($typeInfo['params'][0], true), false, $nullable, $collectionClass);
|
||||
return self::iterableType($this->parseType($typeInfo['params'][0], true), false, $nullable, $collectionClass);
|
||||
}
|
||||
if (2 === \count($typeInfo['params'])) {
|
||||
return new PropertyTypeIterable($this->parseType($typeInfo['params'][1], true), true, $nullable, $collectionClass);
|
||||
return self::iterableType($this->parseType($typeInfo['params'][1], true), true, $nullable, $collectionClass);
|
||||
}
|
||||
|
||||
throw new InvalidTypeException(sprintf('JMS property type array can\'t have more than 2 parameters (%s)', var_export($typeInfo, true)));
|
||||
@ -102,7 +108,7 @@ final class JMSTypeParser
|
||||
new DateTimeOptions(
|
||||
$serializeFormat,
|
||||
($typeInfo['params'][1] ?? null) ?: null,
|
||||
$deserializeFormats,
|
||||
$this->useArrayDateFormat ? $deserializeFormats : $deserializeFormats[0],
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -119,4 +125,17 @@ final class JMSTypeParser
|
||||
return is_a($name, Collection::class, true) ? $name : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function iterableType(
|
||||
PropertyType $subType,
|
||||
bool $hashmap,
|
||||
bool $nullable,
|
||||
?string $collectionClass = null
|
||||
): AbstractPropertyType {
|
||||
if (class_exists('Liip\MetadataParser\Metadata\PropertyTypeIterable')) {
|
||||
return new PropertyTypeIterable($subType, $hashmap, $nullable, $collectionClass);
|
||||
}
|
||||
|
||||
return new PropertyTypeArray($subType, $hashmap, $nullable, $collectionClass !== null);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user