Skip to content

Commit

Permalink
SDK-2708: Add PYZ constant check (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeyspryker authored Oct 24, 2023
1 parent 4f81326 commit 48f4eb9
Show file tree
Hide file tree
Showing 10 changed files with 426 additions and 72 deletions.
50 changes: 15 additions & 35 deletions src/Builder/ClassModifier/ClassConstant/ClassConstantModifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
namespace SprykerSdk\Integrator\Builder\ClassModifier\ClassConstant;

use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\ParserFactory;
use SprykerSdk\Integrator\Builder\ClassModifier\AddVisitorsTrait;
use SprykerSdk\Integrator\Builder\Exception\LiteralValueParsingException;
use SprykerSdk\Integrator\Builder\Finder\ClassNodeFinderInterface;
use SprykerSdk\Integrator\Builder\Finder\ClassConstantFinderInterface;
use SprykerSdk\Integrator\Builder\Visitor\AddConstantVisitor;
use SprykerSdk\Integrator\Transfer\ClassInformationTransfer;

Expand All @@ -28,23 +27,23 @@ class ClassConstantModifier implements ClassConstantModifierInterface
protected const SINGLE_EXPRESSION_COUNT = 1;

/**
* @var \SprykerSdk\Integrator\Builder\Finder\ClassNodeFinderInterface
* @var \PhpParser\ParserFactory
*/
protected $classNodeFinder;
private ParserFactory $parserFactory;

/**
* @var \PhpParser\ParserFactory
* @var \SprykerSdk\Integrator\Builder\Finder\ClassConstantFinderInterface
*/
private ParserFactory $parserFactory;
protected ClassConstantFinderInterface $classConstantFinder;

/**
* @param \SprykerSdk\Integrator\Builder\Finder\ClassNodeFinderInterface $classNodeFinder
* @param \PhpParser\ParserFactory $parserFactory
* @param \SprykerSdk\Integrator\Builder\Finder\ClassConstantFinderInterface $classConstantFinder
*/
public function __construct(ClassNodeFinderInterface $classNodeFinder, ParserFactory $parserFactory)
public function __construct(ParserFactory $parserFactory, ClassConstantFinderInterface $classConstantFinder)
{
$this->classNodeFinder = $classNodeFinder;
$this->parserFactory = $parserFactory;
$this->classConstantFinder = $classConstantFinder;
}

/**
Expand All @@ -55,9 +54,13 @@ public function __construct(ClassNodeFinderInterface $classNodeFinder, ParserFac
*
* @return \SprykerSdk\Integrator\Transfer\ClassInformationTransfer
*/
public function setConstant(ClassInformationTransfer $classInformationTransfer, string $constantName, $value, bool $isLiteral): ClassInformationTransfer
{
$parentConstant = $this->getFirstParentConstant($classInformationTransfer, $constantName);
public function setConstant(
ClassInformationTransfer $classInformationTransfer,
string $constantName,
$value,
bool $isLiteral
): ClassInformationTransfer {
$parentConstant = $this->classConstantFinder->findParentConstantByName($classInformationTransfer, $constantName);

$modifier = 'public';
if ($parentConstant) {
Expand All @@ -79,29 +82,6 @@ public function setConstant(ClassInformationTransfer $classInformationTransfer,
return $this->addVisitorsClassInformationTransfer($classInformationTransfer, $visitors);
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param string $constantName
*
* @return \PhpParser\Node\Stmt\ClassConst|null
*/
protected function getFirstParentConstant(ClassInformationTransfer $classInformationTransfer, string $constantName): ?ClassConst
{
$parentConstant = null;

do {
$classInformationTransfer = $classInformationTransfer->getParent();

if ($classInformationTransfer === null) {
break;
}

$parentConstant = $this->classNodeFinder->findConstantNode($classInformationTransfer, $constantName);
} while ($parentConstant === null);

return $parentConstant;
}

/**
* @param string $value
*
Expand Down
38 changes: 23 additions & 15 deletions src/Builder/Creator/AbstractMethodCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use PhpParser\BuilderFactory;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use SprykerSdk\Integrator\Builder\Resolver\PrefixedConstNameResolverInterface;
use SprykerSdk\Integrator\Transfer\ClassInformationTransfer;

class AbstractMethodCreator
{
Expand All @@ -21,32 +23,38 @@ class AbstractMethodCreator
protected const SIMPLE_VARIABLE_SEMICOLON_COUNT = 1;

/**
* @var string
* @var \SprykerSdk\Integrator\Builder\Resolver\PrefixedConstNameResolverInterface
*/
protected const STATIC_CONST_PREPOSITION = 'static';
protected PrefixedConstNameResolverInterface $prefixedConstNameResolver;

/**
* @var string
* @param \SprykerSdk\Integrator\Builder\Resolver\PrefixedConstNameResolverInterface $prefixedConstNameResolver
*/
protected const PARENT_CONST_PREPOSITION = 'parent';

/**
* @var array
*/
protected const CONST_PREPOSITIONS = [
self::STATIC_CONST_PREPOSITION,
self::PARENT_CONST_PREPOSITION,
];
public function __construct(PrefixedConstNameResolverInterface $prefixedConstNameResolver)
{
$this->prefixedConstNameResolver = $prefixedConstNameResolver;
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param string $className
* @param string $constantName
*
* @return \PhpParser\Node\Expr\ClassConstFetch
*/
protected function createClassConstantExpression(string $className, string $constantName): ClassConstFetch
{
return (new BuilderFactory())->classConstFetch($className, $constantName);
protected function createClassConstantExpression(
ClassInformationTransfer $classInformationTransfer,
string $className,
string $constantName
): ClassConstFetch {
return (new BuilderFactory())->classConstFetch(
$className,
$this->prefixedConstNameResolver->resolveClassConstantName(
$classInformationTransfer,
$className,
$constantName,
),
);
}

/**
Expand Down
18 changes: 13 additions & 5 deletions src/Builder/Creator/MethodCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use SprykerSdk\Integrator\Builder\Exception\LiteralValueParsingException;
use SprykerSdk\Integrator\Builder\Exception\NotFoundReturnExpressionException;
use SprykerSdk\Integrator\Builder\Finder\ClassNodeFinderInterface;
use SprykerSdk\Integrator\Builder\Resolver\PrefixedConstNameResolverInterface;
use SprykerSdk\Integrator\Builder\Visitor\AddMethodVisitor;
use SprykerSdk\Integrator\Transfer\ClassInformationTransfer;

Expand Down Expand Up @@ -66,14 +67,18 @@ class MethodCreator extends AbstractMethodCreator implements MethodCreatorInterf
* @param \SprykerSdk\Integrator\Builder\Creator\MethodDocBlockCreatorInterface $methodDocBlockCreator
* @param \SprykerSdk\Integrator\Builder\Creator\MethodReturnTypeCreatorInterface $methodReturnTypeCreator
* @param \PhpParser\ParserFactory $parserFactory
* @param \SprykerSdk\Integrator\Builder\Resolver\PrefixedConstNameResolverInterface $prefixedConstNameResolver
*/
public function __construct(
ClassNodeFinderInterface $classNodeFinder,
MethodStatementsCreatorInterface $methodStatementsCreator,
MethodDocBlockCreatorInterface $methodDocBlockCreator,
MethodReturnTypeCreatorInterface $methodReturnTypeCreator,
ParserFactory $parserFactory
ParserFactory $parserFactory,
PrefixedConstNameResolverInterface $prefixedConstNameResolver
) {
parent::__construct($prefixedConstNameResolver);

$this->classNodeFinder = $classNodeFinder;
$this->methodStatementsCreator = $methodStatementsCreator;
$this->methodDocBlockCreator = $methodDocBlockCreator;
Expand Down Expand Up @@ -103,7 +108,7 @@ public function createMethodBody(ClassInformationTransfer $classInformationTrans
}

if ($this->isSingleReturnStatement($tree)) {
return $this->createSingleStatementMethodBody($tree, $value, $isLiteral);
return $this->createSingleStatementMethodBody($classInformationTransfer, $tree, $value, $isLiteral);
}

return $tree;
Expand Down Expand Up @@ -230,6 +235,7 @@ protected function isSingleReturnStatement(array $tree): bool
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param array<\PhpParser\Node\Stmt> $tree
* @param mixed $value
* @param bool $isLiteral
Expand All @@ -238,15 +244,15 @@ protected function isSingleReturnStatement(array $tree): bool
*
* @return array<\PhpParser\Node\Stmt\Return_>
*/
protected function createSingleStatementMethodBody(array $tree, $value, bool $isLiteral): array
protected function createSingleStatementMethodBody(ClassInformationTransfer $classInformationTransfer, array $tree, $value, bool $isLiteral): array
{
/** @var \PhpParser\Node\Stmt\Expression|null $returnExpression */
$returnExpression = $tree[0] ?? null;
if (!$returnExpression) {
throw new NotFoundReturnExpressionException(sprintf('Not found any statements in value: `%s`', $value));
}
if (property_exists($returnExpression->expr, 'class')) {
return $this->createClassConstantReturnStatement($returnExpression);
return $this->createClassConstantReturnStatement($classInformationTransfer, $returnExpression);
}

$returnExpression = !$isLiteral && $returnExpression->expr instanceof ConstFetch && !in_array((string)$returnExpression->expr->name, ['true', 'false'], true)
Expand All @@ -257,11 +263,12 @@ protected function createSingleStatementMethodBody(array $tree, $value, bool $is
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param \PhpParser\Node\Stmt\Expression $expression
*
* @return array<\PhpParser\Node\Stmt\Return_>
*/
protected function createClassConstantReturnStatement(Expression $expression): array
protected function createClassConstantReturnStatement(ClassInformationTransfer $classInformationTransfer, Expression $expression): array
{
/** @var \PhpParser\Node\Expr\ClassConstFetch $expressionStatement */
$expressionStatement = $expression->expr;
Expand All @@ -271,6 +278,7 @@ protected function createClassConstantReturnStatement(Expression $expression): a
$expressionClass = $expressionStatement->class;
$expressionClass->parts = [$expressionClass->toString()];
$returnClassConstExpression = $this->createClassConstantExpression(
$classInformationTransfer,
$expressionClass->toString(),
$expressionName->name,
);
Expand Down
37 changes: 22 additions & 15 deletions src/Builder/Creator/MethodStatementsCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,21 @@ public function createMethodStatementsFromValue(ClassInformationTransfer $classI
return $this->createNodeTreeFromArrayValue($classInformationTransfer, $value);
}

return $this->createNodeTreeFromStringValue($value);
return $this->createNodeTreeFromStringValue($classInformationTransfer, $value);
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param mixed $value
*
* @return array
* @return array<mixed>
*/
protected function createNodeTreeFromStringValue($value): array
protected function createNodeTreeFromStringValue(ClassInformationTransfer $classInformationTransfer, $value): array
{
$arrayItems = [];
$valueItems = explode('::', $value);
$arrayItems[] = new ArrayItem(
$this->createClassConstantExpression($valueItems[0], $valueItems[1]),
$this->createClassConstantExpression($classInformationTransfer, $valueItems[0], $valueItems[1]),
);

return $arrayItems;
Expand All @@ -96,7 +97,7 @@ protected function createNodeTreeFromArrayValue(ClassInformationTransfer $classI

if (is_array($item)) {
$insideArrayItems = $this->createMethodStatementsFromValue($classInformationTransfer, $item);
$arrayItems[] = $this->createArrayItem([], $keyParts, $insideArrayItems);
$arrayItems[] = $this->createArrayItem($classInformationTransfer, [], $keyParts, $insideArrayItems);

continue;
}
Expand All @@ -109,27 +110,32 @@ protected function createNodeTreeFromArrayValue(ClassInformationTransfer $classI
$itemParts = [$item];
}

$arrayItems[] = $this->createArrayItem($itemParts, $keyParts);
$arrayItems[] = $this->createArrayItem($classInformationTransfer, $itemParts, $keyParts);
}

return $arrayItems;
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param array $itemParts
* @param array $keyParts
* @param array $insideArrayItems
*
* @return \PhpParser\Node\Expr\ArrayItem
*/
protected function createArrayItem(array $itemParts, array $keyParts = [], array $insideArrayItems = []): ArrayItem
{
protected function createArrayItem(
ClassInformationTransfer $classInformationTransfer,
array $itemParts,
array $keyParts = [],
array $insideArrayItems = []
): ArrayItem {
if (count($itemParts) === static::SIMPLE_VARIABLE_SEMICOLON_COUNT) {
return $this->createSingleSemicolonVariableArrayItem($itemParts, $keyParts);
return $this->createSingleSemicolonVariableArrayItem($classInformationTransfer, $itemParts, $keyParts);
}

if (!$keyParts && !$insideArrayItems) {
return new ArrayItem($this->createClassConstantExpression($itemParts[static::CONSTANT_TYPE_INDEX], $itemParts[static::CONSTANT_NAME_INDEX]));
return new ArrayItem($this->createClassConstantExpression($classInformationTransfer, $itemParts[static::CONSTANT_TYPE_INDEX], $itemParts[static::CONSTANT_NAME_INDEX]));
}
$countKeyParts = count($keyParts);
$key = null;
Expand All @@ -139,7 +145,7 @@ protected function createArrayItem(array $itemParts, array $keyParts = [], array
}

if ($countKeyParts === 2) {
$key = $this->createClassConstantExpression($keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]);
$key = $this->createClassConstantExpression($classInformationTransfer, $keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]);
}

if ($insideArrayItems) {
Expand All @@ -150,25 +156,26 @@ protected function createArrayItem(array $itemParts, array $keyParts = [], array
}

return new ArrayItem(
$this->createClassConstantExpression($itemParts[static::CONSTANT_TYPE_INDEX], $itemParts[static::CONSTANT_NAME_INDEX]),
$this->createClassConstantExpression($classInformationTransfer, $itemParts[static::CONSTANT_TYPE_INDEX], $itemParts[static::CONSTANT_NAME_INDEX]),
$key,
);
}

/**
* @param \SprykerSdk\Integrator\Transfer\ClassInformationTransfer $classInformationTransfer
* @param array $itemParts
* @param array $keyParts
*
* @return \PhpParser\Node\Expr\ArrayItem
*/
protected function createSingleSemicolonVariableArrayItem(array $itemParts, array $keyParts): ArrayItem
protected function createSingleSemicolonVariableArrayItem(ClassInformationTransfer $classInformationTransfer, array $itemParts, array $keyParts): ArrayItem
{
if (in_array($itemParts[0], [true, false], true)) {
return new ArrayItem(
new ConstFetch(new Name($itemParts[0] ? 'true' : 'false')),
(count($keyParts) == 1) ?
$this->createValueExpression($keyParts[static::CONSTANT_TYPE_INDEX]) :
$this->createClassConstantExpression($keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]),
$this->createClassConstantExpression($classInformationTransfer, $keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]),
);
}
$singleItemParts = [];
Expand All @@ -190,7 +197,7 @@ protected function createSingleSemicolonVariableArrayItem(array $itemParts, arra
if ($keyParts) {
$key = (count($keyParts) == 1) ?
$this->createValueExpression($keyParts[static::CONSTANT_TYPE_INDEX]) :
$this->createClassConstantExpression($keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]);
$this->createClassConstantExpression($classInformationTransfer, $keyParts[static::CONSTANT_TYPE_INDEX], $keyParts[static::CONSTANT_NAME_INDEX]);
}

return new ArrayItem(
Expand Down
Loading

0 comments on commit 48f4eb9

Please sign in to comment.