/** * @inheritdoc */ public function check(\SplFileInfo $file, Tokens $tokens) { if (in_array(preg_replace('#/+#', '/', $file->getPathname()), $this->excludedFiles, true)) { return; } $namespaces = []; $classes = []; foreach ($tokens as $index => $token) { if ($token->isGivenKind(T_NAMESPACE)) { $namespaceIndex = $tokens->getNextNonWhitespace($index); $namespaceEndIndex = $tokens->getNextTokenOfKind($index, array(';')); $namespaces[$index] = trim($tokens->generatePartialCode($namespaceIndex, $namespaceEndIndex - 1)); } elseif ($token->isClassy()) { $classyIndex = $tokens->getNextNonWhitespace($index); $classes[$classyIndex] = $tokens[$classyIndex]->getContent(); } } list($namespace, $namespaceIndex) = $this->resolveNamespace($tokens, $namespaces); list($className, $classNameIndex) = $this->resolveClassName($tokens, $classes); // Check namespace directory $this->checkNamespaceDirectory($file, $tokens, $namespace, $namespaceIndex); // File basename must match class name $fileBasename = $file->getBasename('.' . $file->getExtension()); if ($className !== null && $fileBasename !== $className) { $tokens->reportAt($classNameIndex, new Message(E_ERROR, 'check_psr4_filename_must_match_classname', ['expected' => $fileBasename, 'class' => $className])); } }
private function checkMethodSignature(Tokens $tokens, $methodName, $methodFunctionIndex) { $startParenthesisIndex = $tokens->getNextTokenOfKind($methodFunctionIndex, array('(')); $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex); for ($index = $startParenthesisIndex; $index < $endParenthesisIndex; ++$index) { if (!$tokens[$index]->isGivenKind(T_VARIABLE)) { continue; } $tokens->reportAt($index, new Message(E_ERROR, 'check_method_arguments_not_allowed', ['method' => $methodName])); break; } }
/** * @inheritdoc */ public function check(\SplFileInfo $file, Tokens $tokens) { $analyzer = new TokensAnalyzer($tokens); $constructorIndex = null; $constructorArgs = []; $properties = []; // Find the class $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]); if ($classIndex === null) { return; } $classAttributes = TokensHelper::getClassAttributes($tokens, $classIndex); // Event must be final or abstract if (!$classAttributes['abstract'] && !$classAttributes['final']) { $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_be_final_or_abstract', ['class' => $classAttributes['name']])); } $elements = $analyzer->getClassyElements(); foreach ($elements as $index => $element) { /** @var Token $token */ if ($element['type'] === 'method') { $methodAttributes = TokensHelper::getMethodAttributes($tokens, $index, $analyzer); if ($methodInfo = $this->checkMethod($tokens, $index, $methodAttributes)) { $constructorIndex = $index; $constructorArgs = $methodInfo['arguments']; } } elseif ($element['type'] === 'property' && ($propertyInfo = $this->checkProperty($tokens, $index))) { $properties[$index] = $propertyInfo; } } // A event without data is not a error if ($constructorIndex === null && count($properties) === 0) { return; } // Event with properties must have a constructor if ($constructorIndex === null) { $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_have_constructor', ['class' => $classAttributes['name']])); } else { $expectedProperties = array_map(function ($info) { return $info['token']->getContent(); }, $constructorArgs); $unknownConstructorArgs = array_diff($expectedProperties, $properties); if (count($unknownConstructorArgs) > 0) { $tokens->reportAt($constructorIndex, new Message(E_ERROR, 'check_class_constructor_has_arguments_without_related_property', ['arguments' => implode(', ', $unknownConstructorArgs)])); } $unassignedProperties = array_diff($properties, $expectedProperties); if (count($unassignedProperties) > 0) { foreach ($unassignedProperties as $index => $property) { $tokens->reportAt($index, new Message(E_ERROR, 'check_class_property_must_be_know_as_constructor_argument', ['property' => $property])); } } } }
/** * @inheritdoc */ public function check(\SplFileInfo $file, Tokens $tokens) { $tokenCount = $tokens->count(); for ($index = 0; $index < $tokenCount; ++$index) { $token = $tokens[$index]; if (!$token->isGivenKind(T_CLASS)) { continue; } $prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)]; $classStart = $tokens->getNextTokenOfKind($index, array('{')); $classEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classStart); // ignore class if it is abstract or already final if ($prevToken->isGivenKind(array(T_ABSTRACT, T_FINAL))) { $index = $classEnd; continue; } $classNameIndex = $tokens->getNextTokenOfKind($index, [[T_STRING]]); $className = $tokens[$classNameIndex]->getContent(); $tokens->reportAt($index, new Message(E_ERROR, 'check_class_should_be_final', ['class' => $className])); $index = $classEnd; } }