1
0
mirror of synced 2024-12-03 18:56:07 +03:00

WIP: php 7.4 support

This commit is contained in:
Pavel 2024-03-11 20:31:56 +03:00
parent 8ced4dc8a1
commit f53f545c22
5 changed files with 169 additions and 26 deletions

View File

@ -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",

View File

@ -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());

View File

@ -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),
));
}

View 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);
}
}

View File

@ -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);
}
}