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