Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@17
|
3 * \DrupalPractice\Project
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace DrupalPractice;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use \Drupal\Sniffs\InfoFiles\ClassFilesSniff;
|
Chris@0
|
14 use Symfony\Component\Yaml\Yaml;
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * Helper class to retrieve project information like module/theme name for a file.
|
Chris@0
|
18 *
|
Chris@0
|
19 * @category PHP
|
Chris@0
|
20 * @package PHP_CodeSniffer
|
Chris@0
|
21 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
22 */
|
Chris@17
|
23 class Project
|
Chris@0
|
24 {
|
Chris@0
|
25
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * Determines the project short name a file might be associated with.
|
Chris@0
|
29 *
|
Chris@17
|
30 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @return string|false Returns the project machine name or false if it could not
|
Chris@0
|
33 * be derived.
|
Chris@0
|
34 */
|
Chris@17
|
35 public static function getName(File $phpcsFile)
|
Chris@0
|
36 {
|
Chris@0
|
37 // Cache the project name per file as this might get called often.
|
Chris@0
|
38 static $cache;
|
Chris@0
|
39
|
Chris@0
|
40 if (isset($cache[$phpcsFile->getFilename()]) === true) {
|
Chris@0
|
41 return $cache[$phpcsFile->getFilename()];
|
Chris@0
|
42 }
|
Chris@0
|
43
|
Chris@0
|
44 $pathParts = pathinfo($phpcsFile->getFilename());
|
Chris@0
|
45 // Module and install files are easy: they contain the project name in the
|
Chris@0
|
46 // file name.
|
Chris@0
|
47 if (isset($pathParts['extension']) === true && ($pathParts['extension'] === 'module' || $pathParts['extension'] === 'install')) {
|
Chris@0
|
48 $cache[$phpcsFile->getFilename()] = $pathParts['filename'];
|
Chris@0
|
49 return $pathParts['filename'];
|
Chris@0
|
50 }
|
Chris@0
|
51
|
Chris@0
|
52 $infoFile = static::getInfoFile($phpcsFile);
|
Chris@0
|
53 if ($infoFile === false) {
|
Chris@0
|
54 return false;
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 $pathParts = pathinfo($infoFile);
|
Chris@0
|
58 $cache[$phpcsFile->getFilename()] = $pathParts['filename'];
|
Chris@0
|
59 return $pathParts['filename'];
|
Chris@0
|
60
|
Chris@0
|
61 }//end getName()
|
Chris@0
|
62
|
Chris@0
|
63
|
Chris@0
|
64 /**
|
Chris@0
|
65 * Determines the info file a file might be associated with.
|
Chris@0
|
66 *
|
Chris@17
|
67 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@0
|
68 *
|
Chris@0
|
69 * @return string|false The project info file name or false if it could not
|
Chris@0
|
70 * be derived.
|
Chris@0
|
71 */
|
Chris@17
|
72 public static function getInfoFile(File $phpcsFile)
|
Chris@0
|
73 {
|
Chris@0
|
74 // Cache the project name per file as this might get called often.
|
Chris@0
|
75 static $cache;
|
Chris@0
|
76
|
Chris@0
|
77 if (isset($cache[$phpcsFile->getFilename()]) === true) {
|
Chris@0
|
78 return $cache[$phpcsFile->getFilename()];
|
Chris@0
|
79 }
|
Chris@0
|
80
|
Chris@0
|
81 $pathParts = pathinfo($phpcsFile->getFilename());
|
Chris@0
|
82
|
Chris@0
|
83 // Search for an info file.
|
Chris@0
|
84 $dir = $pathParts['dirname'];
|
Chris@0
|
85 do {
|
Chris@0
|
86 $infoFiles = glob("$dir/*.info.yml");
|
Chris@0
|
87 if (empty($infoFiles) === true) {
|
Chris@0
|
88 $infoFiles = glob("$dir/*.info");
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 // Go one directory up if we do not find an info file here.
|
Chris@0
|
92 $dir = dirname($dir);
|
Chris@0
|
93 } while (empty($infoFiles) === true && $dir !== dirname($dir));
|
Chris@0
|
94
|
Chris@0
|
95 // No info file found, so we give up.
|
Chris@0
|
96 if (empty($infoFiles) === true) {
|
Chris@0
|
97 $cache[$phpcsFile->getFilename()] = false;
|
Chris@0
|
98 return false;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 // Sort the info file names and take the shortest info file.
|
Chris@17
|
102 usort($infoFiles, array(__NAMESPACE__.'\Project', 'compareLength'));
|
Chris@0
|
103 $infoFile = $infoFiles[0];
|
Chris@0
|
104 $cache[$phpcsFile->getFilename()] = $infoFile;
|
Chris@0
|
105 return $infoFile;
|
Chris@0
|
106
|
Chris@0
|
107 }//end getInfoFile()
|
Chris@0
|
108
|
Chris@0
|
109
|
Chris@0
|
110 /**
|
Chris@0
|
111 * Determines the *.services.yml file in a module.
|
Chris@0
|
112 *
|
Chris@17
|
113 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@0
|
114 *
|
Chris@0
|
115 * @return string|false The Services YML file name or false if it could not
|
Chris@0
|
116 * be derived.
|
Chris@0
|
117 */
|
Chris@17
|
118 public static function getServicesYmlFile(File $phpcsFile)
|
Chris@0
|
119 {
|
Chris@0
|
120 // Cache the services file per file as this might get called often.
|
Chris@0
|
121 static $cache;
|
Chris@0
|
122
|
Chris@0
|
123 if (isset($cache[$phpcsFile->getFilename()]) === true) {
|
Chris@0
|
124 return $cache[$phpcsFile->getFilename()];
|
Chris@0
|
125 }
|
Chris@0
|
126
|
Chris@0
|
127 $pathParts = pathinfo($phpcsFile->getFilename());
|
Chris@0
|
128
|
Chris@0
|
129 // Search for an info file.
|
Chris@0
|
130 $dir = $pathParts['dirname'];
|
Chris@0
|
131 do {
|
Chris@0
|
132 $ymlFiles = glob("$dir/*.services.yml");
|
Chris@0
|
133
|
Chris@0
|
134 // Go one directory up if we do not find an info file here.
|
Chris@0
|
135 $dir = dirname($dir);
|
Chris@0
|
136 } while (empty($ymlFiles) === true && $dir !== dirname($dir));
|
Chris@0
|
137
|
Chris@0
|
138 // No YML file found, so we give up.
|
Chris@0
|
139 if (empty($ymlFiles) === true) {
|
Chris@0
|
140 $cache[$phpcsFile->getFilename()] = false;
|
Chris@0
|
141 return false;
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 // Sort the YML file names and take the shortest info file.
|
Chris@17
|
145 usort($ymlFiles, array(__NAMESPACE__.'\Project', 'compareLength'));
|
Chris@0
|
146 $ymlFile = $ymlFiles[0];
|
Chris@0
|
147 $cache[$phpcsFile->getFilename()] = $ymlFile;
|
Chris@0
|
148 return $ymlFile;
|
Chris@0
|
149
|
Chris@0
|
150 }//end getServicesYmlFile()
|
Chris@0
|
151
|
Chris@0
|
152
|
Chris@0
|
153 /**
|
Chris@0
|
154 * Return true if the given class is a Drupal service registered in *.services.yml.
|
Chris@0
|
155 *
|
Chris@17
|
156 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
157 * @param int $classPtr The position of the class declaration
|
Chris@17
|
158 * in the token stack.
|
Chris@0
|
159 *
|
Chris@0
|
160 * @return bool
|
Chris@0
|
161 */
|
Chris@17
|
162 public static function isServiceClass(File $phpcsFile, $classPtr)
|
Chris@0
|
163 {
|
Chris@0
|
164 // Cache the information per file as this might get called often.
|
Chris@0
|
165 static $cache;
|
Chris@0
|
166
|
Chris@0
|
167 if (isset($cache[$phpcsFile->getFilename()]) === true) {
|
Chris@0
|
168 return $cache[$phpcsFile->getFilename()];
|
Chris@0
|
169 }
|
Chris@0
|
170
|
Chris@0
|
171 // Get the namespace of the class if there is one.
|
Chris@0
|
172 $namespacePtr = $phpcsFile->findPrevious(T_NAMESPACE, ($classPtr - 1));
|
Chris@0
|
173 if ($namespacePtr === false) {
|
Chris@0
|
174 $cache[$phpcsFile->getFilename()] = false;
|
Chris@0
|
175 return false;
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 $ymlFile = static::getServicesYmlFile($phpcsFile);
|
Chris@0
|
179 if ($ymlFile === false) {
|
Chris@0
|
180 $cache[$phpcsFile->getFilename()] = false;
|
Chris@0
|
181 return false;
|
Chris@0
|
182 }
|
Chris@0
|
183
|
Chris@0
|
184 $services = Yaml::parse(file_get_contents($ymlFile));
|
Chris@0
|
185 if (isset($services['services']) === false) {
|
Chris@0
|
186 $cache[$phpcsFile->getFilename()] = false;
|
Chris@0
|
187 return false;
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 $nsEnd = $phpcsFile->findNext(
|
Chris@0
|
191 [
|
Chris@0
|
192 T_NS_SEPARATOR,
|
Chris@0
|
193 T_STRING,
|
Chris@0
|
194 T_WHITESPACE,
|
Chris@0
|
195 ],
|
Chris@0
|
196 ($namespacePtr + 1),
|
Chris@0
|
197 null,
|
Chris@0
|
198 true
|
Chris@0
|
199 );
|
Chris@0
|
200 $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1)));
|
Chris@0
|
201 $classNameSpaced = ltrim($namespace.'\\'.$phpcsFile->getDeclarationName($classPtr), '\\');
|
Chris@0
|
202
|
Chris@0
|
203 foreach ($services['services'] as $service) {
|
Chris@0
|
204 if (isset($service['class']) === true
|
Chris@0
|
205 && $classNameSpaced === ltrim($service['class'], '\\')
|
Chris@0
|
206 ) {
|
Chris@0
|
207 $cache[$phpcsFile->getFilename()] = true;
|
Chris@0
|
208 return true;
|
Chris@0
|
209 }
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212 return false;
|
Chris@0
|
213
|
Chris@0
|
214 }//end isServiceClass()
|
Chris@0
|
215
|
Chris@0
|
216
|
Chris@0
|
217 /**
|
Chris@0
|
218 * Helper method to sort array values by string length with usort().
|
Chris@0
|
219 *
|
Chris@0
|
220 * @param string $a First string.
|
Chris@0
|
221 * @param string $b Second string.
|
Chris@0
|
222 *
|
Chris@0
|
223 * @return int
|
Chris@0
|
224 */
|
Chris@0
|
225 public static function compareLength($a, $b)
|
Chris@0
|
226 {
|
Chris@0
|
227 return (strlen($a) - strlen($b));
|
Chris@0
|
228
|
Chris@0
|
229 }//end compareLength()
|
Chris@0
|
230
|
Chris@0
|
231
|
Chris@0
|
232 /**
|
Chris@0
|
233 * Determines the Drupal core version a file might be associated with.
|
Chris@0
|
234 *
|
Chris@17
|
235 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@0
|
236 *
|
Chris@0
|
237 * @return string|false The core version string or false if it could not
|
Chris@0
|
238 * be derived.
|
Chris@0
|
239 */
|
Chris@17
|
240 public static function getCoreVersion(File $phpcsFile)
|
Chris@0
|
241 {
|
Chris@0
|
242 $infoFile = static::getInfoFile($phpcsFile);
|
Chris@0
|
243 if ($infoFile === false) {
|
Chris@0
|
244 return false;
|
Chris@0
|
245 }
|
Chris@0
|
246
|
Chris@0
|
247 $pathParts = pathinfo($infoFile);
|
Chris@0
|
248
|
Chris@0
|
249 // Drupal 6 and 7 use the .info file extension.
|
Chris@0
|
250 if ($pathParts['extension'] === 'info') {
|
Chris@17
|
251 $info_settings = ClassFilesSniff::drupalParseInfoFormat(file_get_contents($infoFile));
|
Chris@0
|
252 if (isset($info_settings['core']) === true) {
|
Chris@0
|
253 return $info_settings['core'];
|
Chris@0
|
254 }
|
Chris@0
|
255 } else {
|
Chris@0
|
256 // Drupal 8 uses the .yml file extension.
|
Chris@0
|
257 // @todo Revisit for Drupal 9, but I don't want to do YAML parsing
|
Chris@0
|
258 // for now.
|
Chris@0
|
259 return '8.x';
|
Chris@0
|
260 }
|
Chris@0
|
261
|
Chris@0
|
262 }//end getCoreVersion()
|
Chris@0
|
263
|
Chris@0
|
264
|
Chris@0
|
265 }//end class
|