PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/core/Mage/Compiler/Model/Process.php

https://bitbucket.org/kdms/sh-magento
PHP | 451 lines | 316 code | 32 blank | 103 comment | 65 complexity | eb11255963d9157cc4681c0cdb878283 MD5 | raw file
  1. <?php
  2. /**
  3. * Magento Enterprise Edition
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Magento Enterprise Edition License
  8. * that is bundled with this package in the file LICENSE_EE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://www.magentocommerce.com/license/enterprise-edition
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Compiler
  23. * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://www.magentocommerce.com/license/enterprise-edition
  25. */
  26. /**
  27. * Compilation process model
  28. *
  29. * @category Mage
  30. * @package Mage_Compiler
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Compiler_Model_Process
  34. {
  35. protected $_compileDir = null;
  36. protected $_includeDir = null;
  37. protected $_statDir = null;
  38. protected $_compileConfig = null;
  39. protected $_includePaths = array();
  40. protected $_processedClasses= array();
  41. protected $_controllerFolders = array();
  42. public function __construct($options=array())
  43. {
  44. if (isset($options['compile_dir'])) {
  45. $this->_compileDir = $options['compile_dir'];
  46. } else {
  47. $this->_compileDir = Mage::getBaseDir() . DS . 'includes';
  48. }
  49. $this->_includeDir = $this->_compileDir . DS . 'src';
  50. $this->_statDir = $this->_compileDir . DS . 'stat';
  51. }
  52. /**
  53. * Get compilation config
  54. *
  55. * @return Mage_Core_Model_Config_Base
  56. */
  57. public function getCompileConfig()
  58. {
  59. if ($this->_compileConfig === null) {
  60. $this->_compileConfig = Mage::getConfig()->loadModulesConfiguration('compilation.xml');
  61. }
  62. return $this->_compileConfig;
  63. }
  64. /**
  65. * Get allowed include paths
  66. *
  67. * @return array
  68. */
  69. protected function _getIncludePaths()
  70. {
  71. if (empty($this->_includePaths)) {
  72. $originalPath = Mage::registry('original_include_path');
  73. /**
  74. * Exclude current dirrectory include path
  75. */
  76. if ($originalPath == '.') {
  77. $path = get_include_path();
  78. } else {
  79. $path = str_replace($originalPath, '', get_include_path());
  80. }
  81. $this->_includePaths = explode(PS, $path);
  82. foreach ($this->_includePaths as $index => $path) {
  83. if (empty($path) || $path == '.') {
  84. unset($this->_includePaths[$index]);
  85. }
  86. }
  87. }
  88. return $this->_includePaths;
  89. }
  90. /**
  91. * Copy directory
  92. *
  93. * @param string $source
  94. * @param string $target
  95. * @return Mage_Compiler_Model_Process
  96. */
  97. protected function _copy($source, $target, $firstIteration = true)
  98. {
  99. if (is_dir($source)) {
  100. $dir = dir($source);
  101. while (false !== ($file = $dir->read())) {
  102. if (($file[0] == '.')) {
  103. continue;
  104. }
  105. $sourceFile = $source . DS . $file;
  106. if ($file == 'controllers') {
  107. $this->_controllerFolders[] = $sourceFile;
  108. continue;
  109. }
  110. if ($firstIteration) {
  111. $targetFile = $target . DS . $file;
  112. } else {
  113. $targetFile = $target . '_' . $file;
  114. }
  115. $this->_copy($sourceFile, $targetFile, false);
  116. }
  117. } else {
  118. if (strpos(str_replace($this->_includeDir, '', $target), '-')
  119. || !in_array(substr($source, strlen($source)-4, 4), array('.php'))) {
  120. return $this;
  121. }
  122. copy($source, $target);
  123. }
  124. return $this;
  125. }
  126. /**
  127. * Copy Zend Locale data to compilation folter
  128. *
  129. * @param string $destDir
  130. * @return Mage_Compiler_Model_Process
  131. */
  132. protected function _copyZendLocaleData($destDir)
  133. {
  134. $source = Mage::getBaseDir('lib').DS.'Zend'.DS.'Locale'.DS.'Data';
  135. $dir = dir($source);
  136. while (false !== ($file = $dir->read())) {
  137. if (($file[0] == '.')) {
  138. continue;
  139. }
  140. $sourceFile = $source . DS . $file;
  141. $targetFile = $destDir . DS . $file;
  142. copy($sourceFile, $targetFile);
  143. }
  144. return $this;
  145. }
  146. /**
  147. * Copy controllers with folders structure
  148. *
  149. * @param string $basePath base include path where files are located
  150. * @return Mage_Compiler_Model_Process
  151. */
  152. protected function _copyControllers($basePath)
  153. {
  154. foreach ($this->_controllerFolders as $path) {
  155. $relPath = str_replace($basePath, '', $path);
  156. $relPath = trim($relPath, DS);
  157. $arrDirs = explode(DS, $relPath);
  158. $destPath = $this->_includeDir;
  159. foreach ($arrDirs as $dir) {
  160. $destPath.= DS.$dir;
  161. $this->_mkdir($destPath);
  162. }
  163. $this->_copyAll($path, $destPath);
  164. }
  165. return $this;
  166. }
  167. /**
  168. * Copy all files and subfolders
  169. *
  170. * @param string $source
  171. * @param string $target
  172. * @return Mage_Compiler_Model_Process
  173. */
  174. protected function _copyAll($source, $target)
  175. {
  176. if (is_dir($source)) {
  177. $this->_mkdir($target);
  178. $dir = dir($source);
  179. while (false !== ($file = $dir->read())) {
  180. if (($file[0] == '.')) {
  181. continue;
  182. }
  183. $sourceFile = $source . DS . $file;
  184. $targetFile = $target . DS . $file;
  185. $this->_copyAll($sourceFile, $targetFile);
  186. }
  187. } else {
  188. if (!in_array(substr($source, strlen($source)-4, 4), array('.php'))) {
  189. return $this;
  190. }
  191. copy($source, $target);
  192. }
  193. return $this;
  194. }
  195. /**
  196. * Create directory if not exist
  197. *
  198. * @param string $dir
  199. * @return string
  200. */
  201. protected function _mkdir($dir)
  202. {
  203. if (!is_dir($dir)) {
  204. mkdir($dir);
  205. @chmod($dir, 0777);
  206. }
  207. return $dir;
  208. }
  209. /**
  210. * Copy files from all include directories to one.
  211. * Lib files and controllers files will be copied as is
  212. *
  213. * @return Mage_Compiler_Model_Process
  214. */
  215. protected function _collectFiles()
  216. {
  217. $paths = $this->_getIncludePaths();
  218. $paths = array_reverse($paths);
  219. $destDir= $this->_includeDir;
  220. $libDir = Mage::getBaseDir('lib');
  221. $this->_mkdir($destDir);
  222. foreach ($paths as $path) {
  223. $this->_controllerFolders = array();
  224. $this->_copy($path, $destDir);
  225. $this->_copyControllers($path);
  226. if ($path == $libDir) {
  227. $this->_copyAll($libDir, $destDir);
  228. }
  229. }
  230. $destDir.= DS.'Data';
  231. $this->_mkdir($destDir);
  232. $this->_copyZendLocaleData($destDir);
  233. return $this;
  234. }
  235. public function getCollectedFilesCount()
  236. {
  237. return count(glob($this->_includeDir.DS.'*'));
  238. }
  239. public function getCompiledFilesCount()
  240. {
  241. return count(glob($this->_includeDir.DS.Varien_Autoload::SCOPE_FILE_PREFIX.'*'));
  242. }
  243. public function getCompileClassList()
  244. {
  245. $arrFiles = array();
  246. $statDir = $this->_statDir;
  247. $statFiles= array();
  248. if (is_dir($statDir)) {
  249. $dir = dir($statDir);
  250. while (false !== ($file = $dir->read())) {
  251. if (($file[0] == '.')) {
  252. continue;
  253. }
  254. $statFiles[str_replace('.csv', '', $file)] = $this->_statDir.DS.$file;
  255. }
  256. }
  257. foreach ($this->getCompileConfig()->getNode('includes')->children() as $code => $config) {
  258. $classes = $config->asArray();
  259. if (is_array($classes)) {
  260. $arrFiles[$code] = array_keys($classes);
  261. } else {
  262. $arrFiles[$code] = array();
  263. }
  264. $statClasses = array();
  265. if (isset($statFiles[$code])) {
  266. $statClasses = explode("\n", file_get_contents($statFiles[$code]));
  267. $popularStatClasses = array();
  268. foreach ($statClasses as $index => $classInfo) {
  269. $classInfo = explode(':', $classInfo);
  270. $popularStatClasses[$classInfo[1]][] = $classInfo[0];
  271. }
  272. ksort($popularStatClasses);
  273. $statClasses = array_pop($popularStatClasses);
  274. unset($statFiles[$code]);
  275. }
  276. $arrFiles[$code] = array_merge($arrFiles[$code], $statClasses);
  277. $arrFiles[$code] = array_unique($arrFiles[$code]);
  278. sort($arrFiles[$code]);
  279. }
  280. foreach($statFiles as $code => $file) {
  281. $classes = explode("\n", file_get_contents($file));
  282. $popularStatClasses = array();
  283. foreach ($classes as $index => $classInfo) {
  284. $classInfo = explode(':', $classInfo);
  285. $popularStatClasses[$classInfo[1]][] = $classInfo[0];
  286. }
  287. ksort($popularStatClasses);
  288. $arrFiles[$code] = array_pop($popularStatClasses);
  289. }
  290. foreach ($arrFiles as $scope=>$classes) {
  291. if ($scope != 'default') {
  292. foreach ($classes as $index => $class) {
  293. if (in_array($class, $arrFiles['default'])) {
  294. unset($arrFiles[$scope][$index]);
  295. }
  296. }
  297. }
  298. }
  299. return $arrFiles;
  300. }
  301. /**
  302. * Compile classes code to files
  303. *
  304. * @return Mage_Compiler_Model_Process
  305. */
  306. protected function _compileFiles()
  307. {
  308. $classesInfo = $this->getCompileClassList();
  309. foreach ($classesInfo as $code => $classes) {
  310. $classesSorce = $this->_getClassesSourceCode($classes, $code);
  311. file_put_contents($this->_includeDir.DS.Varien_Autoload::SCOPE_FILE_PREFIX.$code.'.php', $classesSorce);
  312. }
  313. return $this;
  314. }
  315. protected function _getClassesSourceCode($classes, $scope)
  316. {
  317. $sortedClasses = array();
  318. foreach ($classes as $className) {
  319. $implements = array_reverse(class_implements($className));
  320. foreach ($implements as $class) {
  321. if (!in_array($class, $sortedClasses) && !in_array($class, $this->_processedClasses) && strstr($class, '_')) {
  322. $sortedClasses[] = $class;
  323. if ($scope == 'default') {
  324. $this->_processedClasses[] = $class;
  325. }
  326. }
  327. }
  328. $extends = array_reverse(class_parents($className));
  329. foreach ($extends as $class) {
  330. if (!in_array($class, $sortedClasses) && !in_array($class, $this->_processedClasses) && strstr($class, '_')) {
  331. $sortedClasses[] = $class;
  332. if ($scope == 'default') {
  333. $this->_processedClasses[] = $class;
  334. }
  335. }
  336. }
  337. if (!in_array($className, $sortedClasses) && !in_array($className, $this->_processedClasses)) {
  338. $sortedClasses[] = $className;
  339. if ($scope == 'default') {
  340. $this->_processedClasses[] = $className;
  341. }
  342. }
  343. }
  344. $classesSource = "<?php\n";
  345. foreach ($sortedClasses as $className) {
  346. $file = $this->_includeDir.DS.$className.'.php';
  347. if (!file_exists($file)) {
  348. continue;
  349. }
  350. $content = file_get_contents($file);
  351. $content = ltrim($content, '<?php');
  352. $content = rtrim($content, "\n\r\t?>");
  353. $classesSource.= $content;
  354. }
  355. return $classesSource;
  356. }
  357. public function clear()
  358. {
  359. $this->registerIncludePath(false);
  360. if (is_dir($this->_includeDir)) {
  361. mageDelTree($this->_includeDir);
  362. }
  363. return $this;
  364. }
  365. /**
  366. * Run compilation process
  367. *
  368. * @return Mage_Compiler_Model_Process
  369. */
  370. public function run()
  371. {
  372. $this->_collectFiles();
  373. $this->_compileFiles();
  374. $this->registerIncludePath();
  375. return $this;
  376. }
  377. /**
  378. * Enable or disable include path constant definition in compiler config.php
  379. *
  380. * @param bool $flag
  381. * @return Mage_Compiler_Model_Process
  382. */
  383. public function registerIncludePath($flag = true)
  384. {
  385. $file = $this->_compileDir.DS.'config.php';
  386. if (is_writeable($file)) {
  387. $content = file_get_contents($file);
  388. $content = explode("\n", $content);
  389. foreach ($content as $index => $line) {
  390. if (strpos($line, 'COMPILER_INCLUDE_PATH')) {
  391. if ($flag) {
  392. $content[$index] = ltrim($line, '#');
  393. } else {
  394. $content[$index] = '#'.$line;
  395. }
  396. }
  397. }
  398. file_put_contents($file, implode("\n", $content));
  399. }
  400. return $this;
  401. }
  402. /**
  403. * Validate if compilation process is allowed
  404. *
  405. * @return array
  406. */
  407. public function validate()
  408. {
  409. $result = array();
  410. if (!is_writeable($this->_compileDir)) {
  411. $result[] = Mage::helper('compiler')->__('Directory "%s" must be writeable', $this->_compileDir);
  412. }
  413. $file = $this->_compileDir.DS.'config.php';
  414. if (!is_writeable($file)) {
  415. $result[] = Mage::helper('compiler')->__('File "%s" must be writeable', $file);
  416. }
  417. return $result;
  418. }
  419. }