diff --git a/src/TolerantDefinitionResolver.php b/src/TolerantDefinitionResolver.php index 357afa8..58aac55 100644 --- a/src/TolerantDefinitionResolver.php +++ b/src/TolerantDefinitionResolver.php @@ -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,7 +577,8 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface 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) { 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; } diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index 3e4a32c..f3b08af 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -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) { diff --git a/validation/frameworks/broken/ArrayNodeDefinition.php b/validation/frameworks/broken/ArrayNodeDefinition.php new file mode 100644 index 0000000..4ad218b --- /dev/null +++ b/validation/frameworks/broken/ArrayNodeDefinition.php @@ -0,0 +1,549 @@ + + * + * 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 + */ +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) + ); + } + } + } +} diff --git a/validation/frameworks/broken/WithReturnTypehints.php b/validation/frameworks/broken/WithReturnTypehints.php new file mode 100644 index 0000000..045051f --- /dev/null +++ b/validation/frameworks/broken/WithReturnTypehints.php @@ -0,0 +1,18 @@ +getParent()) { + } + } +} diff --git a/validation/frameworks/broken/functionUse.php b/validation/frameworks/broken/functionUse.php new file mode 100644 index 0000000..ff3fe9e --- /dev/null +++ b/validation/frameworks/broken/functionUse.php @@ -0,0 +1,7 @@ +b(); +}; \ No newline at end of file diff --git a/validation/frameworks/broken/functionUse2.php b/validation/frameworks/broken/functionUse2.php new file mode 100644 index 0000000..593e6ac --- /dev/null +++ b/validation/frameworks/broken/functionUse2.php @@ -0,0 +1,4 @@ + true + ); +} \ No newline at end of file diff --git a/validation/frameworks/broken/namespaces9.php b/validation/frameworks/broken/namespaces9.php new file mode 100644 index 0000000..5ca557a --- /dev/null +++ b/validation/frameworks/broken/namespaces9.php @@ -0,0 +1,6 @@ +textDocument = new class($this->args) + { + }; + } +} diff --git a/validation/frameworks/broken/parameterTypeResolution1.php b/validation/frameworks/broken/parameterTypeResolution1.php new file mode 100644 index 0000000..09ead6f --- /dev/null +++ b/validation/frameworks/broken/parameterTypeResolution1.php @@ -0,0 +1,12 @@ +getAccount(); + } + } +} diff --git a/validation/frameworks/broken/propertyName1.php b/validation/frameworks/broken/propertyName1.php new file mode 100644 index 0000000..c53cf54 --- /dev/null +++ b/validation/frameworks/broken/propertyName1.php @@ -0,0 +1,12 @@ +