Fix and start cleaning up type resolution issues
parent
f5a93a2e09
commit
bfbad095ee
|
@ -136,7 +136,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
$namespaceImportTable[$alias] = (string)$name;
|
$namespaceImportTable[$alias] = (string)$name;
|
||||||
}
|
}
|
||||||
$namespaceDefinition = $node->getNamespaceDefinition();
|
$namespaceDefinition = $node->getNamespaceDefinition();
|
||||||
if ($namespaceDefinition !== null) {
|
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
|
||||||
$namespaceName = (string)$namespaceDefinition->name->getNamespacedName();
|
$namespaceName = (string)$namespaceDefinition->name->getNamespacedName();
|
||||||
} else {
|
} else {
|
||||||
$namespaceName = 'global';
|
$namespaceName = 'global';
|
||||||
|
@ -518,7 +518,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
*/
|
*/
|
||||||
public function resolveExpressionNodeToType($expr): Type
|
public function resolveExpressionNodeToType($expr): Type
|
||||||
{
|
{
|
||||||
if (!($expr instanceof Tolerant\Node)) {
|
if ($expr == null || $expr instanceof Tolerant\MissingToken || $expr instanceof Tolerant\SkippedToken) {
|
||||||
// TODO some members are null or Missing/SkippedToken
|
// TODO some members are null or Missing/SkippedToken
|
||||||
// How do we handle this more generally?
|
// How do we handle this more generally?
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
|
@ -529,18 +529,19 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\Variable || $expr instanceof Tolerant\Node\UseVariableName) {
|
if ($expr instanceof Tolerant\Node\Expression\Variable || $expr instanceof Tolerant\Node\UseVariableName) {
|
||||||
if ($expr instanceof Tolerant\Node\Expression\Variable && $expr->getName() === 'this') {
|
if ($expr->getName() === 'this') {
|
||||||
return new Types\This;
|
return new Types\This;
|
||||||
}
|
}
|
||||||
// Find variable definition
|
// Find variable definition (parameter or assignment expression)
|
||||||
$defNode = $this->resolveVariableToNode($expr);
|
$defNode = $this->resolveVariableToNode($expr);
|
||||||
if ($defNode instanceof Tolerant\Node\Expression || $defNode instanceof Tolerant\Node\UseVariableName) {
|
if ($defNode instanceof Tolerant\Node\Expression\AssignmentExpression || $defNode instanceof Tolerant\Node\UseVariableName) {
|
||||||
return $this->resolveExpressionNodeToType($defNode);
|
return $this->resolveExpressionNodeToType($defNode);
|
||||||
}
|
}
|
||||||
if ($defNode instanceof Tolerant\Node\Parameter) {
|
if ($defNode instanceof Tolerant\Node\Parameter) {
|
||||||
return $this->getTypeFromNode($defNode);
|
return $this->getTypeFromNode($defNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\CallExpression &&
|
if ($expr instanceof Tolerant\Node\Expression\CallExpression &&
|
||||||
!(
|
!(
|
||||||
$expr->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
$expr->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
@ -562,7 +563,9 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (strtolower((string)$expr->getText()) === 'true' || strtolower((string)$expr->getText()) === 'false') {
|
|
||||||
|
$lowerText = strtolower($expr->getText());
|
||||||
|
if ($lowerText === 'true' || $lowerText === 'false') {
|
||||||
return new Types\Boolean;
|
return new Types\Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +577,8 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
return $def->type;
|
return $def->type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (($access = $expr) instanceof Tolerant\Node\Expression\MemberAccessExpression) {
|
|
||||||
|
if (($access = $expr) instanceof Tolerant\Node\Expression\MemberAccessExpression) {
|
||||||
if ($access->memberName instanceof Tolerant\Node\Expression) {
|
if ($access->memberName instanceof Tolerant\Node\Expression) {
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
@ -606,6 +610,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
($scopedAccess = $expr) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
($scopedAccess = $expr) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
||||||
) {
|
) {
|
||||||
|
@ -626,15 +631,19 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
return $def->type;
|
return $def->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) {
|
||||||
return $this->resolveClassNameToType($expr->classTypeDesignator);
|
return $this->resolveClassNameToType($expr->classTypeDesignator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\CloneExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\CloneExpression) {
|
||||||
return $this->resolveExpressionNodeToType($expr->expression);
|
return $this->resolveExpressionNodeToType($expr->expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
||||||
return $this->resolveExpressionNodeToType($expr->rightOperand);
|
return $this->resolveExpressionNodeToType($expr->rightOperand);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) {
|
||||||
// ?:
|
// ?:
|
||||||
if ($expr->ifExpression === null) {
|
if ($expr->ifExpression === null) {
|
||||||
|
@ -649,6 +658,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
$this->resolveExpressionNodeToType($expr->elseExpression)
|
$this->resolveExpressionNodeToType($expr->elseExpression)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) {
|
if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) {
|
||||||
// ?? operator
|
// ?? operator
|
||||||
return new Types\Compound([
|
return new Types\Compound([
|
||||||
|
@ -656,6 +666,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
$this->resolveExpressionNodeToType($expr->rightOperand)
|
$this->resolveExpressionNodeToType($expr->rightOperand)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
TolerantParserHelpers::isBooleanExpression($expr)
|
TolerantParserHelpers::isBooleanExpression($expr)
|
||||||
|
|
||||||
|
@ -666,6 +677,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
) {
|
) {
|
||||||
return new Types\Boolean;
|
return new Types\Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
($expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
($expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
||||||
($expr->operator->kind === Tolerant\TokenKind::DotToken || $expr->operator->kind === Tolerant\TokenKind::DotEqualsToken)) ||
|
($expr->operator->kind === Tolerant\TokenKind::DotToken || $expr->operator->kind === Tolerant\TokenKind::DotEqualsToken)) ||
|
||||||
|
@ -683,8 +695,10 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
// || $expr instanceof Node\Expr\Scalar\MagicConst\Namespace_
|
// || $expr instanceof Node\Expr\Scalar\MagicConst\Namespace_
|
||||||
// || $expr instanceof Node\Expr\Scalar\MagicConst\Trait_
|
// || $expr instanceof Node\Expr\Scalar\MagicConst\Trait_
|
||||||
) {
|
) {
|
||||||
|
var_dump("string literal");
|
||||||
return new Types\String_;
|
return new Types\String_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
||||||
($operator = $expr->operator->kind)
|
($operator = $expr->operator->kind)
|
||||||
|
@ -706,6 +720,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
return new Types\Float_;
|
return new Types\Float_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// TODO better naming
|
// TODO better naming
|
||||||
($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) ||
|
($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) ||
|
||||||
|
@ -719,6 +734,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
) {
|
) {
|
||||||
return new Types\Integer;
|
return new Types\Integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken
|
$expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken
|
||||||
||
|
||
|
||||||
|
@ -726,6 +742,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
) {
|
) {
|
||||||
return new Types\Float_;
|
return new Types\Float_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) {
|
||||||
$valueTypes = [];
|
$valueTypes = [];
|
||||||
$keyTypes = [];
|
$keyTypes = [];
|
||||||
|
@ -753,6 +770,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
return new Types\Array_($valueType, $keyType);
|
return new Types\Array_($valueType, $keyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) {
|
||||||
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
|
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
|
||||||
if (!($varType instanceof Types\Array_)) {
|
if (!($varType instanceof Types\Array_)) {
|
||||||
|
@ -760,10 +778,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
return $varType->getValueType();
|
return $varType->getValueType();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) {
|
if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) {
|
||||||
// TODO: resolve path to PhpDocument and find return statement
|
// TODO: resolve path to PhpDocument and find return statement
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,14 +805,15 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
return new Types\Object_;
|
return new Types\Object_;
|
||||||
}
|
}
|
||||||
$className = (string)$class->getResolvedName();
|
$className = (string)$class->getResolvedName();
|
||||||
|
$lowerClassName = strtolower($className);
|
||||||
|
|
||||||
if ($className === 'static') {
|
if ($lowerClassName === 'static') {
|
||||||
return new Types\Static_;
|
return new Types\Static_;
|
||||||
}
|
}
|
||||||
if ($className === 'self' || $className === 'parent') {
|
if ($lowerClassName === 'self' || $lowerClassName === 'parent') {
|
||||||
$classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
|
$classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
|
||||||
if ($className === 'parent') {
|
if ($lowerClassName === 'parent') {
|
||||||
if ($classNode === null || $classNode->classBaseClause === null || $classNode->classBaseClause->baseClass === null) {
|
if ($classNode === null || $classNode->classBaseClause === null) {
|
||||||
return new Types\Object_;
|
return new Types\Object_;
|
||||||
}
|
}
|
||||||
// parent is resolved to the parent class
|
// parent is resolved to the parent class
|
||||||
|
@ -825,19 +846,25 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
*/
|
*/
|
||||||
public function getTypeFromNode($node)
|
public function getTypeFromNode($node)
|
||||||
{
|
{
|
||||||
// For parameters, get the type of the parameter [first from doc block, then from param type]
|
// For parameters, get the type of the parameter [first from doc block, then from combo of param type and default
|
||||||
if ($node instanceof Tolerant\Node\Parameter) {
|
if ($node instanceof Tolerant\Node\Parameter) {
|
||||||
// Parameters
|
// Parameters
|
||||||
// Get the doc block for the the function call
|
// Get the doc block for the the function call
|
||||||
|
// /**
|
||||||
|
// * @param MyClass $myParam
|
||||||
|
// */
|
||||||
|
// function foo($a)
|
||||||
$functionLikeDeclaration = TolerantParserHelpers::getFunctionLikeDeclarationFromParameter($node);
|
$functionLikeDeclaration = TolerantParserHelpers::getFunctionLikeDeclarationFromParameter($node);
|
||||||
$variableName = $node->variableName->getText($node->getFileContents());
|
$variableName = $node->getName();
|
||||||
$docBlock = $this->getDocBlock($functionLikeDeclaration);
|
$docBlock = $this->getDocBlock($functionLikeDeclaration);
|
||||||
|
|
||||||
$parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName);
|
$parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName);
|
||||||
if ($parameterDocBlockTag !== null && $parameterDocBlockTag->getType() !== null) {
|
if ($parameterDocBlockTag !== null && ($type = $parameterDocBlockTag->getType())) {
|
||||||
return $parameterDocBlockTag->getType();
|
// Doc block comments supercede all other forms of type inference
|
||||||
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function foo(MyClass $a)
|
||||||
if ($node->typeDeclaration !== null) {
|
if ($node->typeDeclaration !== null) {
|
||||||
// Use PHP7 return type hint
|
// Use PHP7 return type hint
|
||||||
if ($node->typeDeclaration instanceof Tolerant\Token) {
|
if ($node->typeDeclaration instanceof Tolerant\Token) {
|
||||||
|
@ -847,13 +874,14 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
$type = new Types\Object_(new Fqsen('\\' . (string)$node->typeDeclaration->getResolvedName()));
|
$type = new Types\Object_(new Fqsen('\\' . (string)$node->typeDeclaration->getResolvedName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// function foo($a = 3)
|
||||||
if ($node->default !== null) {
|
if ($node->default !== null) {
|
||||||
$defaultType = $this->resolveExpressionNodeToType($node->default);
|
$defaultType = $this->resolveExpressionNodeToType($node->default);
|
||||||
if (isset($type) && !is_a($type, get_class($defaultType))) {
|
if (isset($type) && !is_a($type, get_class($defaultType))) {
|
||||||
$type = new Types\Compound([$type, $defaultType]);
|
// TODO - verify it is worth creating a compound type
|
||||||
} else {
|
return new Types\Compound([$type, $defaultType]);
|
||||||
$type = $defaultType;
|
|
||||||
}
|
}
|
||||||
|
$type = $defaultType;
|
||||||
}
|
}
|
||||||
return $type ?? new Types\Mixed;
|
return $type ?? new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
@ -869,7 +897,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
// Use @return tag
|
// Use @return tag
|
||||||
return $returnTags[0]->getType();
|
return $returnTags[0]->getType();
|
||||||
}
|
}
|
||||||
if ($node->returnType !== null) {
|
if ($node->returnType !== null && !($node->returnType instanceof Tolerant\MissingToken)) {
|
||||||
// Use PHP7 return type hint
|
// Use PHP7 return type hint
|
||||||
if ($node->returnType instanceof Tolerant\Token) {
|
if ($node->returnType instanceof Tolerant\Token) {
|
||||||
// Resolve a string like "bool" to a type object
|
// Resolve a string like "bool" to a type object
|
||||||
|
@ -882,15 +910,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// for variables / assignments, get the documented type the assignment resolves to.
|
// for variables / assignments, get the documented type the assignment resolves to.
|
||||||
if ($node->parent instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
|
||||||
$node = $node->parent;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
($declarationNode =
|
($declarationNode =
|
||||||
TolerantParserHelpers::tryGetPropertyDeclaration($node) ??
|
TolerantParserHelpers::tryGetPropertyDeclaration($node) ??
|
||||||
TolerantParserHelpers::tryGetConstOrClassConstDeclaration($node)
|
TolerantParserHelpers::tryGetConstOrClassConstDeclaration($node)
|
||||||
) !== null ||
|
) !== null ||
|
||||||
$node instanceof Tolerant\Node\Expression\AssignmentExpression)
|
($node = $node->parent) instanceof Tolerant\Node\Expression\AssignmentExpression)
|
||||||
{
|
{
|
||||||
$declarationNode = $declarationNode ?? $node;
|
$declarationNode = $declarationNode ?? $node;
|
||||||
|
|
||||||
|
@ -903,22 +928,25 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
||||||
) {
|
) {
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the expression
|
// Resolve the expression
|
||||||
if ($declarationNode instanceof Tolerant\Node\PropertyDeclaration) {
|
if ($declarationNode instanceof Tolerant\Node\PropertyDeclaration) {
|
||||||
// TODO should have default
|
// TODO should have default
|
||||||
if (isset($node->rightOperand)) {
|
if (isset($node->parent->rightOperand)) {
|
||||||
return $this->resolveExpressionNodeToType($node->rightOperand);
|
return $this->resolveExpressionNodeToType($node->parent->rightOperand);
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Tolerant\Node\ConstElement) {
|
} else if ($node instanceof Tolerant\Node\ConstElement) {
|
||||||
return $this->resolveExpressionNodeToType($node->assignment);
|
return $this->resolveExpressionNodeToType($node->assignment);
|
||||||
} else if ($node instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
} else if ($node instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
||||||
return $this->resolveExpressionNodeToType($node);
|
return $this->resolveExpressionNodeToType($node->rightOperand);
|
||||||
}
|
}
|
||||||
// TODO: read @property tags of class
|
// TODO: read @property tags of class
|
||||||
// TODO: Try to infer the type from default value / constant value
|
// TODO: Try to infer the type from default value / constant value
|
||||||
// Unknown
|
// Unknown
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The node does not have a type
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class ValidationTest extends TestCase
|
||||||
|
|
||||||
foreach (new RecursiveIteratorIterator($iterator) as $file) {
|
foreach (new RecursiveIteratorIterator($iterator) as $file) {
|
||||||
if (strpos(\strrev((string)$file), \strrev(".php")) === 0
|
if (strpos(\strrev((string)$file), \strrev(".php")) === 0
|
||||||
// && strpos((string)$file, "taxonomy.php")!== false
|
// && strpos((string)$file, "memberAccess3.php")!== false
|
||||||
) {
|
) {
|
||||||
if ($file->getSize() < 100000) {
|
if ($file->getSize() < 100000) {
|
||||||
$testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName];
|
$testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName];
|
||||||
|
@ -151,13 +151,18 @@ class ValidationTest extends TestCase
|
||||||
$static = [];
|
$static = [];
|
||||||
foreach ($document->getDefinitions() as $defn) {
|
foreach ($document->getDefinitions() as $defn) {
|
||||||
$fqns[] = $defn->fqn;
|
$fqns[] = $defn->fqn;
|
||||||
|
|
||||||
|
if ($defn->type instanceof \phpDocumentor\Reflection\Types\Null_) {
|
||||||
|
$defn->type = new \phpDocumentor\Reflection\Types\Mixed;
|
||||||
|
}
|
||||||
$currentTypes[$defn->fqn] = $defn->type;
|
$currentTypes[$defn->fqn] = $defn->type;
|
||||||
|
|
||||||
$canBeInstantiated[$defn->fqn] = $defn->canBeInstantiated;
|
$canBeInstantiated[$defn->fqn] = $defn->canBeInstantiated;
|
||||||
|
|
||||||
$defn->symbolInformation->location = null;
|
$defn->symbolInformation->location = null;
|
||||||
$symbols[$defn->fqn] = $defn->symbolInformation;
|
$symbols[$defn->fqn] = $defn->symbolInformation;
|
||||||
|
|
||||||
$extends[$defn->fqn] = $defn->extends;
|
$extends[$defn->fqn] = $defn->extends ?? [];
|
||||||
$global[$defn->fqn] = $defn->isGlobal;
|
$global[$defn->fqn] = $defn->isGlobal;
|
||||||
$docs[$defn->fqn] = $defn->documentation;
|
$docs[$defn->fqn] = $defn->documentation;
|
||||||
$static[$defn->fqn] = $defn->isStatic;
|
$static[$defn->fqn] = $defn->isStatic;
|
||||||
|
@ -165,7 +170,7 @@ class ValidationTest extends TestCase
|
||||||
if ($definitions !== null) {
|
if ($definitions !== null) {
|
||||||
|
|
||||||
$this->assertEquals($definitions, $fqns, 'defn->fqn does not match');
|
$this->assertEquals($definitions, $fqns, 'defn->fqn does not match');
|
||||||
// $this->assertEquals($types, $currentTypes, "defn->type does not match");
|
$this->assertEquals($types, $currentTypes, "defn->type does not match");
|
||||||
$this->assertEquals($instantiated, $canBeInstantiated, "defn->canBeInstantiated does not match");
|
$this->assertEquals($instantiated, $canBeInstantiated, "defn->canBeInstantiated does not match");
|
||||||
$this->assertEquals($extend, $extends, 'defn->extends does not match');
|
$this->assertEquals($extend, $extends, 'defn->extends does not match');
|
||||||
$this->assertEquals($isGlobal, $global, 'defn->isGlobal does not match');
|
$this->assertEquals($isGlobal, $global, 'defn->isGlobal does not match');
|
||||||
|
@ -180,7 +185,9 @@ class ValidationTest extends TestCase
|
||||||
'false', 'true', 'null', 'FALSE', 'TRUE', 'NULL',
|
'false', 'true', 'null', 'FALSE', 'TRUE', 'NULL',
|
||||||
'__', // magic constants are treated as normal constants
|
'__', // magic constants are treated as normal constants
|
||||||
'Exception', // catch exception types missing from old definition resolver
|
'Exception', // catch exception types missing from old definition resolver
|
||||||
'Trait' // use Trait references are missing from old definition resolve
|
'Trait', // use Trait references are missing from old definition resolve
|
||||||
|
'->tableAlias', '->realField', '->field', '->first_name', '->last_name', '->quoteMatch', '->idCol', '->timeCol', '->dataCol',
|
||||||
|
'pathToUri', 'uriToPath' // group function use declarations are broken in old definition resolver
|
||||||
];
|
];
|
||||||
foreach ($this->getIndex($parserKinds[0], $frameworkName)->references as $key=>$value) {
|
foreach ($this->getIndex($parserKinds[0], $frameworkName)->references as $key=>$value) {
|
||||||
foreach ($skipped as $s) {
|
foreach ($skipped as $s) {
|
||||||
|
|
|
@ -0,0 +1,549 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Definition\Builder;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Definition\ArrayNode;
|
||||||
|
use Symfony\Component\Config\Definition\PrototypedArrayNode;
|
||||||
|
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a fluent interface for defining an array node.
|
||||||
|
*
|
||||||
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*/
|
||||||
|
class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface
|
||||||
|
{
|
||||||
|
protected $performDeepMerging = true;
|
||||||
|
protected $ignoreExtraKeys = false;
|
||||||
|
protected $removeExtraKeys = true;
|
||||||
|
protected $children = array();
|
||||||
|
protected $prototype;
|
||||||
|
protected $atLeastOne = false;
|
||||||
|
protected $allowNewKeys = true;
|
||||||
|
protected $key;
|
||||||
|
protected $removeKeyItem;
|
||||||
|
protected $addDefaults = false;
|
||||||
|
protected $addDefaultChildren = false;
|
||||||
|
protected $nodeBuilder;
|
||||||
|
protected $normalizeKeys = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __construct($name, NodeParentInterface $parent = null)
|
||||||
|
{
|
||||||
|
parent::__construct($name, $parent);
|
||||||
|
|
||||||
|
$this->nullEquivalent = array();
|
||||||
|
$this->trueEquivalent = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom children builder.
|
||||||
|
*
|
||||||
|
* @param NodeBuilder $builder A custom NodeBuilder
|
||||||
|
*/
|
||||||
|
public function setBuilder(NodeBuilder $builder)
|
||||||
|
{
|
||||||
|
$this->nodeBuilder = $builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a builder to add children nodes.
|
||||||
|
*
|
||||||
|
* @return NodeBuilder
|
||||||
|
*/
|
||||||
|
public function children()
|
||||||
|
{
|
||||||
|
return $this->getNodeBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a prototype for child nodes.
|
||||||
|
*
|
||||||
|
* @param string $type the type of node
|
||||||
|
*
|
||||||
|
* @return NodeDefinition
|
||||||
|
*/
|
||||||
|
public function prototype($type)
|
||||||
|
{
|
||||||
|
return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return VariableNodeDefinition
|
||||||
|
*/
|
||||||
|
public function variablePrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('variable');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ScalarNodeDefinition
|
||||||
|
*/
|
||||||
|
public function scalarPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('scalar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BooleanNodeDefinition
|
||||||
|
*/
|
||||||
|
public function booleanPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return IntegerNodeDefinition
|
||||||
|
*/
|
||||||
|
public function integerPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('integer');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FloatNodeDefinition
|
||||||
|
*/
|
||||||
|
public function floatPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('float');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function arrayPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('array');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return EnumNodeDefinition
|
||||||
|
*/
|
||||||
|
public function enumPrototype()
|
||||||
|
{
|
||||||
|
return $this->prototype('enum');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the default value if the node is not set in the configuration.
|
||||||
|
*
|
||||||
|
* This method is applicable to concrete nodes only (not to prototype nodes).
|
||||||
|
* If this function has been called and the node is not set during the finalization
|
||||||
|
* phase, it's default value will be derived from its children default values.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function addDefaultsIfNotSet()
|
||||||
|
{
|
||||||
|
$this->addDefaults = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds children with a default value when none are defined.
|
||||||
|
*
|
||||||
|
* @param int|string|array|null $children The number of children|The child name|The children names to be added
|
||||||
|
*
|
||||||
|
* This method is applicable to prototype nodes only.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function addDefaultChildrenIfNoneSet($children = null)
|
||||||
|
{
|
||||||
|
$this->addDefaultChildren = $children;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires the node to have at least one element.
|
||||||
|
*
|
||||||
|
* This method is applicable to prototype nodes only.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function requiresAtLeastOneElement()
|
||||||
|
{
|
||||||
|
$this->atLeastOne = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallows adding news keys in a subsequent configuration.
|
||||||
|
*
|
||||||
|
* If used all keys have to be defined in the same configuration file.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function disallowNewKeysInSubsequentConfigs()
|
||||||
|
{
|
||||||
|
$this->allowNewKeys = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a normalization rule for XML configurations.
|
||||||
|
*
|
||||||
|
* @param string $singular The key to remap
|
||||||
|
* @param string $plural The plural of the key for irregular plurals
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function fixXmlConfig($singular, $plural = null)
|
||||||
|
{
|
||||||
|
$this->normalization()->remap($singular, $plural);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the attribute which value is to be used as key.
|
||||||
|
*
|
||||||
|
* This is useful when you have an indexed array that should be an
|
||||||
|
* associative array. You can select an item from within the array
|
||||||
|
* to be the key of the particular item. For example, if "id" is the
|
||||||
|
* "key", then:
|
||||||
|
*
|
||||||
|
* array(
|
||||||
|
* array('id' => 'my_name', 'foo' => 'bar'),
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* becomes
|
||||||
|
*
|
||||||
|
* array(
|
||||||
|
* 'my_name' => array('foo' => 'bar'),
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* If you'd like "'id' => 'my_name'" to still be present in the resulting
|
||||||
|
* array, then you can set the second argument of this method to false.
|
||||||
|
*
|
||||||
|
* This method is applicable to prototype nodes only.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the key
|
||||||
|
* @param bool $removeKeyItem Whether or not the key item should be removed
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function useAttributeAsKey($name, $removeKeyItem = true)
|
||||||
|
{
|
||||||
|
$this->key = $name;
|
||||||
|
$this->removeKeyItem = $removeKeyItem;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the node can be unset.
|
||||||
|
*
|
||||||
|
* @param bool $allow
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function canBeUnset($allow = true)
|
||||||
|
{
|
||||||
|
$this->merge()->allowUnset($allow);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an "enabled" boolean to enable the current section.
|
||||||
|
*
|
||||||
|
* By default, the section is disabled. If any configuration is specified then
|
||||||
|
* the node will be automatically enabled:
|
||||||
|
*
|
||||||
|
* enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden
|
||||||
|
* enableableArrayNode: ~ # The config is enabled & use the default values
|
||||||
|
* enableableArrayNode: true # The config is enabled & use the default values
|
||||||
|
* enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden
|
||||||
|
* enableableArrayNode: {enabled: false, ...} # The config is disabled
|
||||||
|
* enableableArrayNode: false # The config is disabled
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function canBeEnabled()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addDefaultsIfNotSet()
|
||||||
|
->treatFalseLike(array('enabled' => false))
|
||||||
|
->treatTrueLike(array('enabled' => true))
|
||||||
|
->treatNullLike(array('enabled' => true))
|
||||||
|
->beforeNormalization()
|
||||||
|
->ifArray()
|
||||||
|
->then(function ($v) {
|
||||||
|
$v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
|
||||||
|
|
||||||
|
return $v;
|
||||||
|
})
|
||||||
|
->end()
|
||||||
|
->children()
|
||||||
|
->booleanNode('enabled')
|
||||||
|
->defaultFalse()
|
||||||
|
;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an "enabled" boolean to enable the current section.
|
||||||
|
*
|
||||||
|
* By default, the section is enabled.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function canBeDisabled()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addDefaultsIfNotSet()
|
||||||
|
->treatFalseLike(array('enabled' => false))
|
||||||
|
->treatTrueLike(array('enabled' => true))
|
||||||
|
->treatNullLike(array('enabled' => true))
|
||||||
|
->children()
|
||||||
|
->booleanNode('enabled')
|
||||||
|
->defaultTrue()
|
||||||
|
;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the deep merging of the node.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function performNoDeepMerging()
|
||||||
|
{
|
||||||
|
$this->performDeepMerging = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows extra config keys to be specified under an array without
|
||||||
|
* throwing an exception.
|
||||||
|
*
|
||||||
|
* Those config values are simply ignored and removed from the
|
||||||
|
* resulting array. This should be used only in special cases where
|
||||||
|
* you want to send an entire configuration array through a special
|
||||||
|
* tree that processes only part of the array.
|
||||||
|
*
|
||||||
|
* @param bool $remove Whether to remove the extra keys
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function ignoreExtraKeys($remove = true)
|
||||||
|
{
|
||||||
|
$this->ignoreExtraKeys = true;
|
||||||
|
$this->removeExtraKeys = $remove;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets key normalization.
|
||||||
|
*
|
||||||
|
* @param bool $bool Whether to enable key normalization
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function normalizeKeys($bool)
|
||||||
|
{
|
||||||
|
$this->normalizeKeys = (bool) $bool;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a node definition.
|
||||||
|
*
|
||||||
|
* $node = new ArrayNodeDefinition()
|
||||||
|
* ->children()
|
||||||
|
* ->scalarNode('foo')->end()
|
||||||
|
* ->scalarNode('baz')->end()
|
||||||
|
* ->end()
|
||||||
|
* ->append($this->getBarNodeDefinition())
|
||||||
|
* ;
|
||||||
|
*
|
||||||
|
* @param NodeDefinition $node A NodeDefinition instance
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition This node
|
||||||
|
*/
|
||||||
|
public function append(NodeDefinition $node)
|
||||||
|
{
|
||||||
|
$this->children[$node->name] = $node->setParent($this);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a node builder to be used to add children and prototype.
|
||||||
|
*
|
||||||
|
* @return NodeBuilder The node builder
|
||||||
|
*/
|
||||||
|
protected function getNodeBuilder()
|
||||||
|
{
|
||||||
|
if (null === $this->nodeBuilder) {
|
||||||
|
$this->nodeBuilder = new NodeBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->nodeBuilder->setParent($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function createNode()
|
||||||
|
{
|
||||||
|
if (null === $this->prototype) {
|
||||||
|
$node = new ArrayNode($this->name, $this->parent);
|
||||||
|
|
||||||
|
$this->validateConcreteNode($node);
|
||||||
|
|
||||||
|
$node->setAddIfNotSet($this->addDefaults);
|
||||||
|
|
||||||
|
foreach ($this->children as $child) {
|
||||||
|
$child->parent = $node;
|
||||||
|
$node->addChild($child->getNode());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$node = new PrototypedArrayNode($this->name, $this->parent);
|
||||||
|
|
||||||
|
$this->validatePrototypeNode($node);
|
||||||
|
|
||||||
|
if (null !== $this->key) {
|
||||||
|
$node->setKeyAttribute($this->key, $this->removeKeyItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $this->atLeastOne) {
|
||||||
|
$node->setMinNumberOfElements(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->default) {
|
||||||
|
$node->setDefaultValue($this->defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
$node->setAddChildrenIfNoneSet($this->addDefaultChildren);
|
||||||
|
if ($this->prototype instanceof static && null === $this->prototype->prototype) {
|
||||||
|
$this->prototype->addDefaultsIfNotSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->prototype->parent = $node;
|
||||||
|
$node->setPrototype($this->prototype->getNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->setAllowNewKeys($this->allowNewKeys);
|
||||||
|
$node->addEquivalentValue(null, $this->nullEquivalent);
|
||||||
|
$node->addEquivalentValue(true, $this->trueEquivalent);
|
||||||
|
$node->addEquivalentValue(false, $this->falseEquivalent);
|
||||||
|
$node->setPerformDeepMerging($this->performDeepMerging);
|
||||||
|
$node->setRequired($this->required);
|
||||||
|
$node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys);
|
||||||
|
$node->setNormalizeKeys($this->normalizeKeys);
|
||||||
|
|
||||||
|
if (null !== $this->normalization) {
|
||||||
|
$node->setNormalizationClosures($this->normalization->before);
|
||||||
|
$node->setXmlRemappings($this->normalization->remappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->merge) {
|
||||||
|
$node->setAllowOverwrite($this->merge->allowOverwrite);
|
||||||
|
$node->setAllowFalse($this->merge->allowFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->validation) {
|
||||||
|
$node->setFinalValidationClosures($this->validation->rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the configuration of a concrete node.
|
||||||
|
*
|
||||||
|
* @param ArrayNode $node The related node
|
||||||
|
*
|
||||||
|
* @throws InvalidDefinitionException
|
||||||
|
*/
|
||||||
|
protected function validateConcreteNode(ArrayNode $node)
|
||||||
|
{
|
||||||
|
$path = $node->getPath();
|
||||||
|
|
||||||
|
if (null !== $this->key) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $this->atLeastOne) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->default) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the configuration of a prototype node.
|
||||||
|
*
|
||||||
|
* @param PrototypedArrayNode $node The related node
|
||||||
|
*
|
||||||
|
* @throws InvalidDefinitionException
|
||||||
|
*/
|
||||||
|
protected function validatePrototypeNode(PrototypedArrayNode $node)
|
||||||
|
{
|
||||||
|
$path = $node->getPath();
|
||||||
|
|
||||||
|
if ($this->addDefaults) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
if ($this->default) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('A default value and default children might not be used together at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Fixtures\Prophecy;
|
||||||
|
|
||||||
|
class WithReturnTypehints extends EmptyClass
|
||||||
|
{
|
||||||
|
public function getSelf(): self {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string {
|
||||||
|
return __CLASS__;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): parent {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
function b() {
|
||||||
|
for ($collection = $this; null !== $collection; $collection = $collection->getParent()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$a = new A;
|
||||||
|
|
||||||
|
$b = function () use ($a) {
|
||||||
|
echo $a->b();
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
use function LanguageServer\{pathToUri, timeout};
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
private static $deprecationsTriggered = array(
|
||||||
|
__CLASS__ => true
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
function a () {
|
||||||
|
$client->textDocument = new class($this->args)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public function setAccount(AccountInterface $account)
|
||||||
|
{
|
||||||
|
// If the passed account is already proxied, use the actual account instead
|
||||||
|
// to prevent loops.
|
||||||
|
if ($account instanceof static) {
|
||||||
|
$account = $account->getAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyClass
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the main property, or NULL if there is none.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $mainPropertyName = NULL;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyClass
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the main property, or NULL if there is none.
|
||||||
|
*/
|
||||||
|
protected $mainPropertyName = 'hello';
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$expectedOutput = array(
|
||||||
|
array($process::OUT, '123'),
|
||||||
|
);
|
Loading…
Reference in New Issue