Chris@0: getFilename()]) === true) { Chris@0: return $cache[$phpcsFile->getFilename()]; Chris@0: } Chris@0: Chris@0: $pathParts = pathinfo($phpcsFile->getFilename()); Chris@0: // Module and install files are easy: they contain the project name in the Chris@0: // file name. Chris@0: if (isset($pathParts['extension']) === true && ($pathParts['extension'] === 'module' || $pathParts['extension'] === 'install')) { Chris@0: $cache[$phpcsFile->getFilename()] = $pathParts['filename']; Chris@0: return $pathParts['filename']; Chris@0: } Chris@0: Chris@0: $infoFile = static::getInfoFile($phpcsFile); Chris@0: if ($infoFile === false) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: $pathParts = pathinfo($infoFile); Chris@0: $cache[$phpcsFile->getFilename()] = $pathParts['filename']; Chris@0: return $pathParts['filename']; Chris@0: Chris@0: }//end getName() Chris@0: Chris@0: Chris@0: /** Chris@0: * Determines the info file a file might be associated with. Chris@0: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. Chris@0: * Chris@0: * @return string|false The project info file name or false if it could not Chris@0: * be derived. Chris@0: */ Chris@17: public static function getInfoFile(File $phpcsFile) Chris@0: { Chris@0: // Cache the project name per file as this might get called often. Chris@0: static $cache; Chris@0: Chris@0: if (isset($cache[$phpcsFile->getFilename()]) === true) { Chris@0: return $cache[$phpcsFile->getFilename()]; Chris@0: } Chris@0: Chris@0: $pathParts = pathinfo($phpcsFile->getFilename()); Chris@0: Chris@0: // Search for an info file. Chris@0: $dir = $pathParts['dirname']; Chris@0: do { Chris@0: $infoFiles = glob("$dir/*.info.yml"); Chris@0: if (empty($infoFiles) === true) { Chris@0: $infoFiles = glob("$dir/*.info"); Chris@0: } Chris@0: Chris@0: // Go one directory up if we do not find an info file here. Chris@0: $dir = dirname($dir); Chris@0: } while (empty($infoFiles) === true && $dir !== dirname($dir)); Chris@0: Chris@0: // No info file found, so we give up. Chris@0: if (empty($infoFiles) === true) { Chris@0: $cache[$phpcsFile->getFilename()] = false; Chris@0: return false; Chris@0: } Chris@0: Chris@0: // Sort the info file names and take the shortest info file. Chris@17: usort($infoFiles, array(__NAMESPACE__.'\Project', 'compareLength')); Chris@0: $infoFile = $infoFiles[0]; Chris@0: $cache[$phpcsFile->getFilename()] = $infoFile; Chris@0: return $infoFile; Chris@0: Chris@0: }//end getInfoFile() Chris@0: Chris@0: Chris@0: /** Chris@0: * Determines the *.services.yml file in a module. Chris@0: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. Chris@0: * Chris@0: * @return string|false The Services YML file name or false if it could not Chris@0: * be derived. Chris@0: */ Chris@17: public static function getServicesYmlFile(File $phpcsFile) Chris@0: { Chris@0: // Cache the services file per file as this might get called often. Chris@0: static $cache; Chris@0: Chris@0: if (isset($cache[$phpcsFile->getFilename()]) === true) { Chris@0: return $cache[$phpcsFile->getFilename()]; Chris@0: } Chris@0: Chris@0: $pathParts = pathinfo($phpcsFile->getFilename()); Chris@0: Chris@0: // Search for an info file. Chris@0: $dir = $pathParts['dirname']; Chris@0: do { Chris@0: $ymlFiles = glob("$dir/*.services.yml"); Chris@0: Chris@0: // Go one directory up if we do not find an info file here. Chris@0: $dir = dirname($dir); Chris@0: } while (empty($ymlFiles) === true && $dir !== dirname($dir)); Chris@0: Chris@0: // No YML file found, so we give up. Chris@0: if (empty($ymlFiles) === true) { Chris@0: $cache[$phpcsFile->getFilename()] = false; Chris@0: return false; Chris@0: } Chris@0: Chris@0: // Sort the YML file names and take the shortest info file. Chris@17: usort($ymlFiles, array(__NAMESPACE__.'\Project', 'compareLength')); Chris@0: $ymlFile = $ymlFiles[0]; Chris@0: $cache[$phpcsFile->getFilename()] = $ymlFile; Chris@0: return $ymlFile; Chris@0: Chris@0: }//end getServicesYmlFile() Chris@0: Chris@0: Chris@0: /** Chris@0: * Return true if the given class is a Drupal service registered in *.services.yml. Chris@0: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. Chris@17: * @param int $classPtr The position of the class declaration Chris@17: * in the token stack. Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@17: public static function isServiceClass(File $phpcsFile, $classPtr) Chris@0: { Chris@0: // Cache the information per file as this might get called often. Chris@0: static $cache; Chris@0: Chris@0: if (isset($cache[$phpcsFile->getFilename()]) === true) { Chris@0: return $cache[$phpcsFile->getFilename()]; Chris@0: } Chris@0: Chris@0: // Get the namespace of the class if there is one. Chris@0: $namespacePtr = $phpcsFile->findPrevious(T_NAMESPACE, ($classPtr - 1)); Chris@0: if ($namespacePtr === false) { Chris@0: $cache[$phpcsFile->getFilename()] = false; Chris@0: return false; Chris@0: } Chris@0: Chris@0: $ymlFile = static::getServicesYmlFile($phpcsFile); Chris@0: if ($ymlFile === false) { Chris@0: $cache[$phpcsFile->getFilename()] = false; Chris@0: return false; Chris@0: } Chris@0: Chris@0: $services = Yaml::parse(file_get_contents($ymlFile)); Chris@0: if (isset($services['services']) === false) { Chris@0: $cache[$phpcsFile->getFilename()] = false; Chris@0: return false; Chris@0: } Chris@0: Chris@0: $nsEnd = $phpcsFile->findNext( Chris@0: [ Chris@0: T_NS_SEPARATOR, Chris@0: T_STRING, Chris@0: T_WHITESPACE, Chris@0: ], Chris@0: ($namespacePtr + 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); Chris@0: $classNameSpaced = ltrim($namespace.'\\'.$phpcsFile->getDeclarationName($classPtr), '\\'); Chris@0: Chris@0: foreach ($services['services'] as $service) { Chris@0: if (isset($service['class']) === true Chris@0: && $classNameSpaced === ltrim($service['class'], '\\') Chris@0: ) { Chris@0: $cache[$phpcsFile->getFilename()] = true; Chris@0: return true; Chris@0: } Chris@0: } Chris@0: Chris@0: return false; Chris@0: Chris@0: }//end isServiceClass() Chris@0: Chris@0: Chris@0: /** Chris@0: * Helper method to sort array values by string length with usort(). Chris@0: * Chris@0: * @param string $a First string. Chris@0: * @param string $b Second string. Chris@0: * Chris@0: * @return int Chris@0: */ Chris@0: public static function compareLength($a, $b) Chris@0: { Chris@0: return (strlen($a) - strlen($b)); Chris@0: Chris@0: }//end compareLength() Chris@0: Chris@0: Chris@0: /** Chris@0: * Determines the Drupal core version a file might be associated with. Chris@0: * Chris@17: * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. Chris@0: * Chris@0: * @return string|false The core version string or false if it could not Chris@0: * be derived. Chris@0: */ Chris@17: public static function getCoreVersion(File $phpcsFile) Chris@0: { Chris@0: $infoFile = static::getInfoFile($phpcsFile); Chris@0: if ($infoFile === false) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: $pathParts = pathinfo($infoFile); Chris@0: Chris@0: // Drupal 6 and 7 use the .info file extension. Chris@0: if ($pathParts['extension'] === 'info') { Chris@17: $info_settings = ClassFilesSniff::drupalParseInfoFormat(file_get_contents($infoFile)); Chris@0: if (isset($info_settings['core']) === true) { Chris@0: return $info_settings['core']; Chris@0: } Chris@0: } else { Chris@0: // Drupal 8 uses the .yml file extension. Chris@0: // @todo Revisit for Drupal 9, but I don't want to do YAML parsing Chris@0: // for now. Chris@0: return '8.x'; Chris@0: } Chris@0: Chris@0: }//end getCoreVersion() Chris@0: Chris@0: Chris@0: }//end class