1
0
Fork 0

Fix and start cleaning up type resolution issues

pull/357/head
Sara Itani 2017-04-19 17:52:37 -07:00
parent f5a93a2e09
commit bfbad095ee
14 changed files with 712 additions and 30 deletions

View File

@ -136,7 +136,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$namespaceImportTable[$alias] = (string)$name;
}
$namespaceDefinition = $node->getNamespaceDefinition();
if ($namespaceDefinition !== null) {
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
$namespaceName = (string)$namespaceDefinition->name->getNamespacedName();
} else {
$namespaceName = 'global';
@ -518,7 +518,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
*/
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
// How do we handle this more generally?
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->getName() === 'this') {
if ($expr->getName() === 'this') {
return new Types\This;
}
// Find variable definition
// Find variable definition (parameter or assignment expression)
$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);
}
if ($defNode instanceof Tolerant\Node\Parameter) {
return $this->getTypeFromNode($defNode);
}
}
if ($expr instanceof Tolerant\Node\Expression\CallExpression &&
!(
$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;
}
@ -574,6 +577,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return $def->type;
}
}
if (($access = $expr) instanceof Tolerant\Node\Expression\MemberAccessExpression) {
if ($access->memberName instanceof Tolerant\Node\Expression) {
return new Types\Mixed;
@ -606,6 +610,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
}
}
}
if (
($scopedAccess = $expr) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
) {
@ -626,15 +631,19 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
}
return $def->type;
}
if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) {
return $this->resolveClassNameToType($expr->classTypeDesignator);
}
if ($expr instanceof Tolerant\Node\Expression\CloneExpression) {
return $this->resolveExpressionNodeToType($expr->expression);
}
if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) {
return $this->resolveExpressionNodeToType($expr->rightOperand);
}
if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) {
// ?:
if ($expr->ifExpression === null) {
@ -649,6 +658,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$this->resolveExpressionNodeToType($expr->elseExpression)
]);
}
if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) {
// ?? operator
return new Types\Compound([
@ -656,6 +666,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$this->resolveExpressionNodeToType($expr->rightOperand)
]);
}
if (
TolerantParserHelpers::isBooleanExpression($expr)
@ -666,6 +677,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
) {
return new Types\Boolean;
}
if (
($expr instanceof Tolerant\Node\Expression\BinaryExpression &&
($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\Trait_
) {
var_dump("string literal");
return new Types\String_;
}
if (
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
($operator = $expr->operator->kind)
@ -706,6 +720,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
}
return new Types\Float_;
}
if (
// TODO better naming
($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) ||
@ -719,6 +734,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
) {
return new Types\Integer;
}
if (
$expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken
||
@ -726,6 +742,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
) {
return new Types\Float_;
}
if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) {
$valueTypes = [];
$keyTypes = [];
@ -753,6 +770,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
}
return new Types\Array_($valueType, $keyType);
}
if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) {
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
if (!($varType instanceof Types\Array_)) {
@ -760,10 +778,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
}
return $varType->getValueType();
}
if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) {
// TODO: resolve path to PhpDocument and find return statement
return new Types\Mixed;
}
return new Types\Mixed;
}
@ -785,14 +805,15 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Object_;
}
$className = (string)$class->getResolvedName();
$lowerClassName = strtolower($className);
if ($className === 'static') {
if ($lowerClassName === 'static') {
return new Types\Static_;
}
if ($className === 'self' || $className === 'parent') {
if ($lowerClassName === 'self' || $lowerClassName === 'parent') {
$classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
if ($className === 'parent') {
if ($classNode === null || $classNode->classBaseClause === null || $classNode->classBaseClause->baseClass === null) {
if ($lowerClassName === 'parent') {
if ($classNode === null || $classNode->classBaseClause === null) {
return new Types\Object_;
}
// parent is resolved to the parent class
@ -825,19 +846,25 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
*/
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) {
// Parameters
// Get the doc block for the the function call
// /**
// * @param MyClass $myParam
// */
// function foo($a)
$functionLikeDeclaration = TolerantParserHelpers::getFunctionLikeDeclarationFromParameter($node);
$variableName = $node->variableName->getText($node->getFileContents());
$variableName = $node->getName();
$docBlock = $this->getDocBlock($functionLikeDeclaration);
$parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName);
if ($parameterDocBlockTag !== null && $parameterDocBlockTag->getType() !== null) {
return $parameterDocBlockTag->getType();
if ($parameterDocBlockTag !== null && ($type = $parameterDocBlockTag->getType())) {
// Doc block comments supercede all other forms of type inference
return $type;
}
// function foo(MyClass $a)
if ($node->typeDeclaration !== null) {
// Use PHP7 return type hint
if ($node->typeDeclaration instanceof Tolerant\Token) {
@ -847,13 +874,14 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$type = new Types\Object_(new Fqsen('\\' . (string)$node->typeDeclaration->getResolvedName()));
}
}
// function foo($a = 3)
if ($node->default !== null) {
$defaultType = $this->resolveExpressionNodeToType($node->default);
if (isset($type) && !is_a($type, get_class($defaultType))) {
$type = new Types\Compound([$type, $defaultType]);
} else {
$type = $defaultType;
// TODO - verify it is worth creating a compound type
return new Types\Compound([$type, $defaultType]);
}
$type = $defaultType;
}
return $type ?? new Types\Mixed;
}
@ -869,7 +897,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
// Use @return tag
return $returnTags[0]->getType();
}
if ($node->returnType !== null) {
if ($node->returnType !== null && !($node->returnType instanceof Tolerant\MissingToken)) {
// Use PHP7 return type hint
if ($node->returnType instanceof Tolerant\Token) {
// 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.
if ($node->parent instanceof Tolerant\Node\Expression\AssignmentExpression) {
$node = $node->parent;
}
if (
($declarationNode =
TolerantParserHelpers::tryGetPropertyDeclaration($node) ??
TolerantParserHelpers::tryGetConstOrClassConstDeclaration($node)
) !== null ||
$node instanceof Tolerant\Node\Expression\AssignmentExpression)
($node = $node->parent) instanceof Tolerant\Node\Expression\AssignmentExpression)
{
$declarationNode = $declarationNode ?? $node;
@ -903,22 +928,25 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
) {
return $type;
}
// Resolve the expression
if ($declarationNode instanceof Tolerant\Node\PropertyDeclaration) {
// TODO should have default
if (isset($node->rightOperand)) {
return $this->resolveExpressionNodeToType($node->rightOperand);
if (isset($node->parent->rightOperand)) {
return $this->resolveExpressionNodeToType($node->parent->rightOperand);
}
} else if ($node instanceof Tolerant\Node\ConstElement) {
return $this->resolveExpressionNodeToType($node->assignment);
} else if ($node instanceof Tolerant\Node\Expression\AssignmentExpression) {
return $this->resolveExpressionNodeToType($node);
return $this->resolveExpressionNodeToType($node->rightOperand);
}
// TODO: read @property tags of class
// TODO: Try to infer the type from default value / constant value
// Unknown
return new Types\Mixed;
}
// The node does not have a type
return null;
}

View File

@ -35,7 +35,7 @@ class ValidationTest extends TestCase
foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos(\strrev((string)$file), \strrev(".php")) === 0
// && strpos((string)$file, "taxonomy.php")!== false
// && strpos((string)$file, "memberAccess3.php")!== false
) {
if ($file->getSize() < 100000) {
$testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName];
@ -151,13 +151,18 @@ class ValidationTest extends TestCase
$static = [];
foreach ($document->getDefinitions() as $defn) {
$fqns[] = $defn->fqn;
if ($defn->type instanceof \phpDocumentor\Reflection\Types\Null_) {
$defn->type = new \phpDocumentor\Reflection\Types\Mixed;
}
$currentTypes[$defn->fqn] = $defn->type;
$canBeInstantiated[$defn->fqn] = $defn->canBeInstantiated;
$defn->symbolInformation->location = null;
$symbols[$defn->fqn] = $defn->symbolInformation;
$extends[$defn->fqn] = $defn->extends;
$extends[$defn->fqn] = $defn->extends ?? [];
$global[$defn->fqn] = $defn->isGlobal;
$docs[$defn->fqn] = $defn->documentation;
$static[$defn->fqn] = $defn->isStatic;
@ -165,7 +170,7 @@ class ValidationTest extends TestCase
if ($definitions !== null) {
$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($extend, $extends, 'defn->extends 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',
'__', // magic constants are treated as normal constants
'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 ($skipped as $s) {

View File

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

View File

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

View File

@ -0,0 +1,8 @@
<?php
class A {
function b() {
for ($collection = $this; null !== $collection; $collection = $collection->getParent()) {
}
}
}

View File

@ -0,0 +1,7 @@
<?php
$a = new A;
$b = function () use ($a) {
echo $a->b();
};

View File

@ -0,0 +1,4 @@
<?php
use function LanguageServer\{pathToUri, timeout};

View File

@ -0,0 +1,7 @@
<?php
class A {
private static $deprecationsTriggered = array(
__CLASS__ => true
);
}

View File

@ -0,0 +1,6 @@
<?php
declare(strict_types = 1);
namespace {
}

View File

@ -0,0 +1,9 @@
<?php
class A {
function a () {
$client->textDocument = new class($this->args)
{
};
}
}

View File

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

View File

@ -0,0 +1,12 @@
<?php
class MyClass
{
/**
* The name of the main property, or NULL if there is none.
*
* @var string
*/
protected $mainPropertyName = NULL;
}

View File

@ -0,0 +1,10 @@
<?php
class MyClass
{
/**
* The name of the main property, or NULL if there is none.
*/
protected $mainPropertyName = 'hello';
}

View File

@ -0,0 +1,5 @@
<?php
$expectedOutput = array(
array($process::OUT, '123'),
);