/** * Parses the method definition (@method), and extract * * This should ideally all be extracted to a dedicated parser component. There is * no point in keeping this hidden away in here. * * @param string $def * * @return null|Method */ private function parseCommentMethodDef($def) { // We use some half-assed regular expression, and hope that it will work out. // To bad that there is no well defined structure, and also no well written // parser :-( if (!preg_match('/ # Syntax: [return type] [name]([type] [parameter], [...]) [<description>] # Possible Type Name (?:([^\\s]+)\\s+)? # Method Name (must be followed by "()") ([^\\s(]+) # Arguments \\(([^)]*)\\) /xs', $def, $match)) { $this->logger->warning(sprintf('Could not parse @method tag "%s".', $def)); return null; } list(, $typeName, $methodName, $arguments) = $match; $method = new Method($methodName); if (!empty($typeName) && null !== ($returnType = $this->commentParser->tryGettingType($typeName))) { $method->setReturnType($returnType); } $arguments = trim($arguments); if (empty($arguments)) { return $method; } foreach (explode(',', $arguments) as $i => $argument) { if (!preg_match('/(?:([^\\s]+)\\s+)?\\$?([^\\s,]+)/', $argument, $match)) { $this->logger->warning(sprintf('Could not parse argument "%s" of @method "%s"; skipping all further arguments.', $argument, $method->getName())); break; } $parameter = new \Scrutinizer\PhpAnalyzer\Model\MethodParameter($match[2], $i); if (!empty($match[1]) && null !== ($paramType = $this->commentParser->tryGettingType($match[1]))) { $parameter->setPhpType($paramType); } $method->addParameter($parameter); } return $method; }
/** * @group foreach */ public function testForeach3() { $this->registry->registerClass($foo = new Clazz('Foo')); $foo->setTypeRegistry($this->registry); $foo->addImplementedInterface('Iterator'); $foo->addMethod($current = new Method('current')); $current->setReturnType($this->registry->getClassOrCreate('Bar')); $this->assuming('y', 'object<Foo>'); $this->inMethod('$x = $i = null; foreach ($y as $i => $x);'); $this->verify('x', $this->createNullableType('object<Bar>')); $this->verify('i', $this->createNullableType('integer|string')); }