PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/dev/tests/static/framework/Magento/TestFramework/Inspection/WordsFinder.php

https://bitbucket.org/leminhtamboy/wisi
PHP | 345 lines | 220 code | 27 blank | 98 comment | 12 complexity | e71dfe35d0aa14aa45007719758ed7cb MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. <?php
  2. /**
  3. * Copyright © 2013-2017 Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * Finder for a list of preconfigured words
  8. */
  9. namespace Magento\TestFramework\Inspection;
  10. class WordsFinder
  11. {
  12. /**
  13. * List of file extensions, that indicate a binary file
  14. *
  15. * @var array
  16. */
  17. protected $_binaryExtensions = [
  18. 'jpg', 'jpeg', 'png', 'gif', 'swf', 'mp3', 'avi', 'mov', 'flv', 'jar', 'zip',
  19. 'eot', 'ttf', 'woff', 'woff2', 'ico', 'svg',
  20. ];
  21. /**
  22. * Copyright string which must be present in every non-binary file
  23. *
  24. * @var string
  25. */
  26. protected $copyrightString = 'Copyright © 2013-2017 Magento, Inc. All rights reserved.';
  27. /**
  28. * Copying string which must be present in every non-binary file right after copyright string
  29. *
  30. * @var string
  31. */
  32. protected $copyingString = 'See COPYING.txt for license details.';
  33. /**
  34. * List of extensions for which copyright check must be skipped
  35. *
  36. * @var array
  37. */
  38. protected $copyrightSkipExtensions = ['csv', 'json', 'lock', 'md', 'txt'];
  39. /**
  40. * List of paths where copyright check must be skipped
  41. *
  42. * @var array
  43. */
  44. protected $copyrightSkipList = [
  45. 'lib/web/legacy-build.min.js'
  46. ];
  47. /**
  48. * Whether copyright presence should be checked or not
  49. *
  50. * @var bool
  51. */
  52. protected $isCopyrightChecked;
  53. /**
  54. * Words to search for
  55. *
  56. * @var array
  57. */
  58. protected $_words = [];
  59. /**
  60. * Map of whitelisted paths to whitelisted words
  61. *
  62. * @var array
  63. */
  64. protected $_whitelist = [];
  65. /**
  66. * Path to base dir, used to calculate relative paths
  67. *
  68. * @var string
  69. */
  70. protected $_baseDir;
  71. /**
  72. * Component Registrar
  73. *
  74. * @var \Magento\Framework\Component\ComponentRegistrar
  75. */
  76. protected $componentRegistrar;
  77. /**
  78. * @param string|array $configFiles
  79. * @param string $baseDir
  80. * @param \Magento\Framework\Component\ComponentRegistrar $componentRegistrar
  81. * @param bool $isCopyrightChecked
  82. * @throws \Magento\TestFramework\Inspection\Exception
  83. */
  84. public function __construct($configFiles, $baseDir, $componentRegistrar, $isCopyrightChecked = false)
  85. {
  86. if (!is_dir($baseDir)) {
  87. throw new \Magento\TestFramework\Inspection\Exception("Base directory {$baseDir} does not exist");
  88. }
  89. $this->_baseDir = str_replace('\\', '/', realpath($baseDir));
  90. $this->componentRegistrar = $componentRegistrar;
  91. // Load config files
  92. if (!is_array($configFiles)) {
  93. $configFiles = [$configFiles];
  94. }
  95. foreach ($configFiles as $configFile) {
  96. $this->_loadConfig($configFile);
  97. }
  98. // Add config files to whitelist, as they surely contain banned words
  99. foreach ($configFiles as $configFile) {
  100. $configFile = str_replace('\\', '/', realpath($configFile));
  101. $this->_whitelist[$configFile] = [];
  102. }
  103. $this->_normalizeWhitelistPaths();
  104. // Final verifications
  105. if (!$this->_words) {
  106. throw new \Magento\TestFramework\Inspection\Exception('No words to check');
  107. }
  108. $this->isCopyrightChecked = $isCopyrightChecked;
  109. }
  110. /**
  111. * Load configuration from file, adding words and whitelisted entries to main config
  112. *
  113. * @param string $configFile
  114. * @throws \Magento\TestFramework\Inspection\Exception
  115. */
  116. protected function _loadConfig($configFile)
  117. {
  118. if (!file_exists($configFile)) {
  119. throw new \Magento\TestFramework\Inspection\Exception("Configuration file {$configFile} does not exist");
  120. }
  121. try {
  122. $xml = new \SimpleXMLElement(file_get_contents($configFile));
  123. } catch (\Exception $e) {
  124. throw new \Magento\TestFramework\Inspection\Exception($e->getMessage(), $e->getCode(), $e);
  125. }
  126. $this->_extractWords($xml)->_extractWhitelist($xml);
  127. }
  128. /**
  129. * Extract words from configuration xml
  130. *
  131. * @param \SimpleXMLElement $configXml
  132. * @return \Magento\TestFramework\Inspection\WordsFinder
  133. * @throws \Magento\TestFramework\Inspection\Exception
  134. */
  135. protected function _extractWords(\SimpleXMLElement $configXml)
  136. {
  137. $words = [];
  138. $nodes = $configXml->xpath('//config/words/word');
  139. foreach ($nodes as $node) {
  140. $words[] = (string)$node;
  141. }
  142. $words = array_filter($words);
  143. $words = array_merge($this->_words, $words);
  144. $this->_words = array_unique($words);
  145. return $this;
  146. }
  147. /**
  148. * Extract whitelisted entries and words from configuration xml
  149. *
  150. * @param \SimpleXMLElement $configXml
  151. * @return \Magento\TestFramework\Inspection\WordsFinder
  152. * @throws \Magento\TestFramework\Inspection\Exception
  153. */
  154. protected function _extractWhitelist(\SimpleXMLElement $configXml)
  155. {
  156. // Load whitelist entries
  157. $whitelist = [];
  158. $nodes = $configXml->xpath('//config/whitelist/item');
  159. foreach ($nodes as $node) {
  160. $path = $node->xpath('path');
  161. if (!$path) {
  162. throw new \Magento\TestFramework\Inspection\Exception(
  163. 'A "path" must be defined for the whitelisted item'
  164. );
  165. }
  166. $component = $node->xpath('component');
  167. if ($component) {
  168. $componentType = $component[0]->xpath('@type')[0];
  169. $componentName = $component[0]->xpath('@name')[0];
  170. $path = $this->componentRegistrar->getPath((string)$componentType, (string)$componentName)
  171. . '/' . (string)$path[0];
  172. } else {
  173. $path = $this->_baseDir . '/' . (string)$path[0];
  174. }
  175. // Words
  176. $words = [];
  177. $wordNodes = $node->xpath('word');
  178. if ($wordNodes) {
  179. foreach ($wordNodes as $wordNode) {
  180. $words[] = (string)$wordNode;
  181. }
  182. }
  183. $whitelist[$path] = $words;
  184. }
  185. // Merge with already present whitelist
  186. foreach ($whitelist as $newPath => $newWords) {
  187. if (isset($this->_whitelist[$newPath])) {
  188. $newWords = array_merge($this->_whitelist[$newPath], $newWords);
  189. }
  190. $this->_whitelist[$newPath] = array_unique($newWords);
  191. }
  192. return $this;
  193. }
  194. /**
  195. * Normalize whitelist paths, so that they containt only native directory separators
  196. */
  197. protected function _normalizeWhitelistPaths()
  198. {
  199. $whitelist = $this->_whitelist;
  200. $this->_whitelist = [];
  201. foreach ($whitelist as $whitelistFile => $whitelistWords) {
  202. $whitelistFile = str_replace('\\', '/', $whitelistFile);
  203. $this->_whitelist[$whitelistFile] = $whitelistWords;
  204. }
  205. }
  206. /**
  207. * Checks the file content and name against the list of words. Do not check content of binary files.
  208. * Exclude whitelisted entries.
  209. *
  210. * @param string $file
  211. * @return array Words, found
  212. */
  213. public function findWords($file)
  214. {
  215. $foundWords = $this->_findWords($file);
  216. if (!$foundWords) {
  217. return [];
  218. }
  219. return self::_removeWhitelistedWords($file, $foundWords);
  220. }
  221. /**
  222. * Checks the file content and name against the list of words. Do not check content of binary files.
  223. *
  224. * @param string $file
  225. * @return array
  226. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  227. */
  228. protected function _findWords($file)
  229. {
  230. $checkContents = !$this->_isBinaryFile($file);
  231. $path = $this->getSearchablePath($file);
  232. $contents = $checkContents ? file_get_contents($file) : '';
  233. $foundWords = [];
  234. foreach ($this->_words as $word) {
  235. if (stripos($path, $word) !== false || stripos($contents, $word) !== false) {
  236. $foundWords[] = $word;
  237. }
  238. }
  239. if ($contents && $this->isCopyrightChecked && !$this->isCopyrightCheckSkipped($file)
  240. && (($copyrightStringPosition = mb_strpos($contents, $this->copyrightString)) === false
  241. || ($copyingStringPosition = strpos($contents, $this->copyingString)) === false
  242. || $copyingStringPosition - $copyrightStringPosition - mb_strlen($this->copyrightString) > 10)
  243. ) {
  244. $foundWords[] = 'Copyright string is missing';
  245. }
  246. return $foundWords;
  247. }
  248. /**
  249. * @param string $path
  250. * @return bool
  251. */
  252. protected function isCopyrightCheckSkipped($path)
  253. {
  254. if (in_array(pathinfo($path, PATHINFO_EXTENSION), $this->copyrightSkipExtensions)) {
  255. return true;
  256. }
  257. foreach ($this->copyrightSkipList as $dir) {
  258. if (strpos($path, $dir) !== false) {
  259. return true;
  260. }
  261. }
  262. return false;
  263. }
  264. /**
  265. * Check, whether file is a binary one
  266. *
  267. * @param string $file
  268. * @return bool
  269. */
  270. protected function _isBinaryFile($file)
  271. {
  272. return in_array(pathinfo($file, PATHINFO_EXTENSION), $this->_binaryExtensions);
  273. }
  274. /**
  275. * Removes whitelisted words from array of found words
  276. *
  277. * @param string $path
  278. * @param array $foundWords
  279. * @return array
  280. */
  281. protected function _removeWhitelistedWords($path, $foundWords)
  282. {
  283. $path = str_replace('\\', '/', $path);
  284. foreach ($this->_whitelist as $whitelistPath => $whitelistWords) {
  285. if (strncmp($whitelistPath, $path, strlen($whitelistPath)) != 0) {
  286. continue;
  287. }
  288. if (!$whitelistWords) {
  289. // All words are permitted there
  290. return [];
  291. }
  292. $foundWords = array_diff($foundWords, $whitelistWords);
  293. }
  294. return $foundWords;
  295. }
  296. /**
  297. * Return the path for words search
  298. *
  299. * @param string $file
  300. * @return string
  301. */
  302. protected function getSearchablePath($file)
  303. {
  304. if (strpos($file, $this->_baseDir) === false) {
  305. return $file;
  306. }
  307. return substr($file, strlen($this->_baseDir) + 1);
  308. }
  309. }