/src/Com/AramisAuto/PHPreprocessor.php

https://github.com/constructions-incongrues/phpreprocessor · PHP · 214 lines · 140 code · 28 blank · 46 comment · 13 complexity · d510c9cf52469789ac0be02f2980e5ac MD5 · raw file

  1. <?php
  2. namespace Com\AramisAuto;
  3. class PHPreprocessor
  4. {
  5. /**
  6. * @var SplFileObject
  7. */
  8. private $_log;
  9. public function __construct(\SplFileObject $logfile)
  10. {
  11. $this->_log = $logfile;
  12. }
  13. /**
  14. * Extracts tokens from files in source directory and outputs corresponding stub properties file to stdout.
  15. */
  16. public function extract(array $options)
  17. {
  18. // TODO : options sanity checks
  19. // Find dist files
  20. $files = $this->_findDistFiles($options['src']);
  21. // Extract tokens
  22. $tokens = $this->_extractTokensFromFiles($files);
  23. // Compute merge properties
  24. if (isset($options['merge-with'])) {
  25. if (!is_readable($options['merge-with'])) {
  26. throw new \InvalidArgumentException(sprintf('File %s is not readable', $options['merge-with']));
  27. }
  28. $errorReporting = error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
  29. $mergeWith = parse_ini_file($options['merge-with']);
  30. error_reporting($errorReporting);
  31. $tokens = array_merge($tokens, $mergeWith);
  32. }
  33. // Exclude tokens
  34. if (isset($options['exclude-from'])) {
  35. $files = explode(',', $options['exclude-from']);
  36. foreach ($files as $file) {
  37. // Sanity checks
  38. if (!is_readable($file)) {
  39. throw new \InvalidArgumentException(sprintf('File "%s" can not be read', $file));
  40. }
  41. // Remove excluded tokens from list
  42. // TODO : this should use array_diff_assoc()
  43. $errorReporting = error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
  44. $tokensExcluded = parse_ini_file($file);
  45. error_reporting($errorReporting);
  46. foreach ($tokensExcluded as $tokenName => $tokenValue) {
  47. if (!empty($tokensExcluded[$tokenName])) {
  48. unset($tokens[$tokenName]);
  49. }
  50. }
  51. }
  52. }
  53. // Output results
  54. $file = new \SplFileObject('php://stdout', 'w');
  55. $file->fwrite($this->_generateProperties($tokens));
  56. }
  57. public function apply(array $options)
  58. {
  59. // TODO : options sanity checks
  60. // Find dist files
  61. $files = $this->_findDistFiles($options['src']);
  62. // Unserialize tokens
  63. $tokensFiles = explode(',', $options['properties']);
  64. $tokens = array();
  65. foreach ($tokensFiles as $tokenFile)
  66. {
  67. if (!is_readable($tokenFile))
  68. {
  69. throw new \RuntimeException(sprintf('File "%s" is not readable', $tokenFile));
  70. }
  71. $errorReporting = error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
  72. $tokens = array_merge($tokens, parse_ini_file($tokenFile, false, INI_SCANNER_RAW));
  73. error_reporting($errorReporting);
  74. }
  75. // Create non -dist files
  76. $copied_files = array();
  77. foreach ($files as $file)
  78. {
  79. $new_file = substr($file, 0, strlen($file) - strlen('-dist'));
  80. copy($file, $new_file); // TODO : sanity checks
  81. $copied_files[] = $new_file;
  82. }
  83. // Perform replacements
  84. $this->_replaceTokens($copied_files, '@', '@', $tokens);
  85. }
  86. /**
  87. * Finds *-dist files in given directory.
  88. *
  89. * @param string $srcDir Path to sources root directory
  90. *
  91. * @return array An array of paths to found -dist files
  92. */
  93. private function _findDistFiles($srcDir)
  94. {
  95. $distFiles = array();
  96. $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($srcDir, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS));
  97. foreach($iterator as $path => $fileInfo) {
  98. if ($fileInfo->isFile()) {
  99. if (preg_match('/.*-dist$/', $fileInfo->getFilename())) {
  100. $distFiles[] = $path;
  101. }
  102. }
  103. }
  104. $this->_logMessage('Found %d -dist files in directory "%s"', array(count($distFiles), $srcDir));
  105. return $distFiles;
  106. }
  107. /**
  108. * Extracts, deduplicates and sorts tokens.
  109. *
  110. * @param array $files
  111. *
  112. * @return array $tokens
  113. */
  114. private function _extractTokensFromFiles(array $files)
  115. {
  116. // Extract tokens from files
  117. $tokens = array();
  118. foreach ($files as $path) {
  119. $matches = array();
  120. preg_match_all('/@[a-z0-9\._]*?@/i', file_get_contents($path), $matches);
  121. if (count($matches[0])) {
  122. foreach ($matches[0] as $token) {
  123. if (!isset($tokens[$token])) {
  124. $tokens[$token] = null;
  125. }
  126. }
  127. }
  128. }
  129. // Cleanup
  130. foreach ($tokens as $token => $files) {
  131. $tokens[trim($token, '@')] = null;
  132. unset($tokens[$token]);
  133. }
  134. ksort($tokens);
  135. return $tokens;
  136. }
  137. private function _generateProperties(array $tokens)
  138. {
  139. // Generate properties file text
  140. $lines = array();
  141. // File header
  142. $lines[] = sprintf('# This file has been automatically generated by %s', __CLASS__);
  143. $namespaceCurrent = '';
  144. foreach ($tokens as $token => $value) {
  145. $parts = explode('.', $token);
  146. $namespace = $parts[0];
  147. if ($namespace != $namespaceCurrent) {
  148. $namespaceCurrent = $namespace;
  149. $lines[] = '';
  150. $lines[] = '# '.$namespaceCurrent;
  151. }
  152. $lines[] = sprintf('%s=%s', $token, $value);
  153. }
  154. $lines[] = '';
  155. return implode("\n", $lines);
  156. }
  157. /**
  158. * Replaces tokens in an array of files.
  159. *
  160. * @param array $files An array of filenames
  161. * @param string $beginToken The begin token delimiter
  162. * @param string $endToken The end token delimiter
  163. * @param array $tokens An array of token/value pairs
  164. */
  165. private function _replaceTokens($files, $beginToken, $endToken, $tokens)
  166. {
  167. if (!is_array($files)) {
  168. $files = array($files);
  169. }
  170. foreach ($files as $file) {
  171. $content = file_get_contents($file);
  172. foreach ($tokens as $key => $value) {
  173. $content = str_replace($beginToken.$key.$endToken, $value, $content, $count);
  174. }
  175. file_put_contents($file, $content);
  176. }
  177. $this->_logMessage('Replaced %d tokens in %d files', array(count($tokens), count($files)));
  178. }
  179. private function _logMessage($messagePattern, array $values = array())
  180. {
  181. $messagePattern = sprintf('[%s] %s', date('r'), $messagePattern);
  182. array_unshift($values, $messagePattern);
  183. $message = call_user_func_array('sprintf', $values);
  184. $this->_log->fwrite($message."\n");
  185. }
  186. }