PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ubuntu-xenial64/www/application/vendor/phug/renderer/src/Phug/Renderer/Adapter/FileAdapter.php

https://bitbucket.org/stouty/vagrants
PHP | 352 lines | 221 code | 47 blank | 84 comment | 17 complexity | 07a3a036b982c124c04c714b9df522f8 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. namespace Phug\Renderer\Adapter;
  3. use Phug\Renderer;
  4. use Phug\Renderer\AbstractAdapter;
  5. use Phug\Renderer\CacheInterface;
  6. use RuntimeException;
  7. class FileAdapter extends AbstractAdapter implements CacheInterface
  8. {
  9. private $renderingFile;
  10. public function __construct(Renderer $renderer, $options)
  11. {
  12. parent::__construct($renderer, [
  13. 'cache_dir' => null,
  14. 'tmp_dir' => sys_get_temp_dir(),
  15. 'tmp_name_function' => 'tempnam',
  16. 'up_to_date_check' => true,
  17. 'keep_base_name' => false,
  18. ]);
  19. $this->setOptions($options);
  20. }
  21. protected function cacheFileContents($destination, $output, $importsMap = [])
  22. {
  23. $imports = file_put_contents(
  24. $destination.'.imports.serialize.txt',
  25. serialize($importsMap)
  26. ) ?: 0;
  27. $template = file_put_contents($destination, $output);
  28. return $template === false ? false : $template + $imports;
  29. }
  30. /**
  31. * Return the cached file path after cache optional process.
  32. *
  33. * @param $path
  34. * @param string $input pug input
  35. * @param callable $rendered method to compile the source into PHP
  36. * @param bool $success
  37. *
  38. * @return string
  39. */
  40. public function cache($path, $input, callable $rendered, &$success = null)
  41. {
  42. $cacheFolder = $this->getCacheDirectory();
  43. $destination = $path;
  44. if (!$this->isCacheUpToDate($destination, $input)) {
  45. if (!is_writable($cacheFolder)) {
  46. throw new RuntimeException(sprintf('Cache directory must be writable. "%s" is not.', $cacheFolder), 6);
  47. }
  48. $compiler = $this->getRenderer()->getCompiler();
  49. $fullPath = $compiler->locate($path) ?: $path;
  50. $output = $rendered($fullPath, $input);
  51. $importsPaths = $compiler->getImportPaths($fullPath);
  52. $success = $this->cacheFileContents(
  53. $destination,
  54. $output,
  55. $importsPaths
  56. );
  57. }
  58. return $destination;
  59. }
  60. /**
  61. * Display rendered template after optional cache process.
  62. *
  63. * @param $path
  64. * @param string $input pug input
  65. * @param callable $rendered method to compile the source into PHP
  66. * @param array $variables local variables
  67. * @param bool $success
  68. */
  69. public function displayCached($path, $input, callable $rendered, array $variables, &$success = null)
  70. {
  71. $__pug_parameters = $variables;
  72. $__pug_path = $this->cache($path, $input, $rendered, $success);
  73. call_user_func(function () use ($__pug_path, $__pug_parameters) {
  74. extract($__pug_parameters);
  75. include $__pug_path;
  76. });
  77. }
  78. /**
  79. * Cache a template file in the cache directory (even if the cache is up to date).
  80. * Returns the number of bytes written in the cache file or false if a
  81. * failure occurred.
  82. *
  83. * @param string $path
  84. *
  85. * @return bool|int
  86. */
  87. public function cacheFile($path)
  88. {
  89. $outputFile = $path;
  90. $this->isCacheUpToDate($outputFile);
  91. $compiler = $this->getRenderer()->getCompiler();
  92. return $this->cacheFileContents(
  93. $outputFile,
  94. $compiler->compileFile($path),
  95. $compiler->getCurrentImportPaths()
  96. );
  97. }
  98. /**
  99. * Cache a template file in the cache directory if the cache is obsolete.
  100. * Returns true if the cache is up to date and cache not change,
  101. * else returns the number of bytes written in the cache file or false if
  102. * a failure occurred.
  103. *
  104. * @param string $path
  105. *
  106. * @return bool|int
  107. */
  108. public function cacheFileIfChanged($path)
  109. {
  110. $outputFile = $path;
  111. if (!$this->isCacheUpToDate($outputFile)) {
  112. $compiler = $this->getRenderer()->getCompiler();
  113. return $this->cacheFileContents(
  114. $outputFile,
  115. $compiler->compileFile($path),
  116. $compiler->getCurrentImportPaths()
  117. );
  118. }
  119. return true;
  120. }
  121. /**
  122. * Scan a directory recursively, compile them and save them into the cache directory.
  123. *
  124. * @param string $directory the directory to search in pug
  125. *
  126. * @throws \Phug\RendererException
  127. *
  128. * @return array count of cached files and error count
  129. */
  130. public function cacheDirectory($directory)
  131. {
  132. $success = 0;
  133. $errors = 0;
  134. $errorDetails = [];
  135. $renderer = $this->getRenderer();
  136. foreach ($renderer->scanDirectory($directory) as $inputFile) {
  137. $renderer->initCompiler();
  138. $compiler = $renderer->getCompiler();
  139. $path = $inputFile;
  140. $this->isCacheUpToDate($path);
  141. $sandBox = $this->getRenderer()->getNewSandBox(function () use (&$success, $compiler, $path, $inputFile) {
  142. $this->cacheFileContents($path, $compiler->compileFile($inputFile), $compiler->getCurrentImportPaths());
  143. $success++;
  144. });
  145. if ($sandBox->getThrowable()) {
  146. $errors++;
  147. $errorDetails[] = [
  148. 'directory' => $directory,
  149. 'inputFile' => $inputFile,
  150. 'path' => $path,
  151. 'error' => $sandBox->getThrowable(),
  152. ];
  153. }
  154. }
  155. return [$success, $errors, $errorDetails];
  156. }
  157. protected function createTemporaryFile()
  158. {
  159. return call_user_func(
  160. $this->getOption('tmp_name_function'),
  161. $this->getOption('tmp_dir'),
  162. 'pug'
  163. );
  164. }
  165. protected function getCompiledFile($php)
  166. {
  167. $this->renderingFile = $this->createTemporaryFile();
  168. file_put_contents($this->renderingFile, $php);
  169. return $this->renderingFile;
  170. }
  171. public function display($__pug_php, array $__pug_parameters)
  172. {
  173. extract($__pug_parameters);
  174. include $this->getCompiledFile($__pug_php);
  175. }
  176. public function getRenderingFile()
  177. {
  178. return $this->renderingFile;
  179. }
  180. /**
  181. * Return a file path in the cache for a given name.
  182. *
  183. * @param string $name
  184. *
  185. * @return string
  186. */
  187. private function getCachePath($name)
  188. {
  189. $cacheDir = $this->getCacheDirectory();
  190. return str_replace('//', '/', $cacheDir.'/'.$name).'.php';
  191. }
  192. /**
  193. * Return a hashed print from input file or content.
  194. *
  195. * @param string $input
  196. *
  197. * @return string
  198. */
  199. private function hashPrint($input)
  200. {
  201. // Get the stronger hashing algorithm available to minimize collision risks
  202. $algorithms = hash_algos();
  203. $algorithm = $algorithms[0];
  204. $number = 0;
  205. foreach ($algorithms as $hashAlgorithm) {
  206. $lettersLength = substr($hashAlgorithm, 0, 2) === 'md'
  207. ? 2
  208. : (substr($hashAlgorithm, 0, 3) === 'sha'
  209. ? 3
  210. : 0
  211. );
  212. if ($lettersLength) {
  213. $hashNumber = substr($hashAlgorithm, $lettersLength);
  214. if ($hashNumber > $number) {
  215. $number = $hashNumber;
  216. $algorithm = $hashAlgorithm;
  217. }
  218. continue;
  219. }
  220. }
  221. return rtrim(strtr(base64_encode(hash($algorithm, $input, true)), '+/', '-_'), '=');
  222. }
  223. /**
  224. * Returns true if the path has an expired imports linked.
  225. *
  226. * @param $path
  227. *
  228. * @return bool
  229. */
  230. private function hasExpiredImport($sourcePath, $cachePath)
  231. {
  232. $importsMap = $cachePath.'.imports.serialize.txt';
  233. if (!file_exists($importsMap)) {
  234. return true;
  235. }
  236. $importPaths = unserialize(file_get_contents($importsMap)) ?: [];
  237. $importPaths[] = $sourcePath;
  238. $time = filemtime($cachePath);
  239. foreach ($importPaths as $importPath) {
  240. if (!file_exists($importPath) || filemtime($importPath) >= $time) {
  241. // If only one file has changed, expires
  242. return true;
  243. }
  244. }
  245. // If only no files changed, it's up to date
  246. return false;
  247. }
  248. /**
  249. * Return true if the file or content is up to date in the cache folder,
  250. * false else.
  251. *
  252. * @param &string $path to be filled
  253. * @param string $input file or pug code
  254. *
  255. * @return bool
  256. */
  257. private function isCacheUpToDate(&$path, $input = null)
  258. {
  259. if (!$input) {
  260. $compiler = $this->getRenderer()->getCompiler();
  261. $input = $compiler->resolve($path);
  262. $path = $this->getCachePath(
  263. ($this->getOption('keep_base_name') ? basename($path) : '').
  264. $this->hashPrint($input)
  265. );
  266. // If up_to_date_check never refresh the cache
  267. if (!$this->getOption('up_to_date_check')) {
  268. return true;
  269. }
  270. // If there is no cache file, create it
  271. if (!file_exists($path)) {
  272. return false;
  273. }
  274. // Else check the main input path and all imported paths in the template
  275. return !$this->hasExpiredImport($input, $path);
  276. }
  277. $path = $this->getCachePath($this->hashPrint($input));
  278. // Do not re-parse file if the same hash exists
  279. return file_exists($path);
  280. }
  281. private function getCacheDirectory()
  282. {
  283. $cacheFolder = $this->hasOption('cache_dir')
  284. ? $this->getOption('cache_dir')
  285. : null;
  286. if (!$cacheFolder && $cacheFolder !== false) {
  287. $cacheFolder = $this->getRenderer()->hasOption('cache_dir')
  288. ? $this->getRenderer()->getOption('cache_dir')
  289. : null;
  290. }
  291. if ($cacheFolder === true) {
  292. $cacheFolder = $this->getOption('tmp_dir');
  293. }
  294. if (!is_dir($cacheFolder) && !@mkdir($cacheFolder, 0777, true)) {
  295. throw new RuntimeException(
  296. $cacheFolder.': Cache directory doesn\'t exist.'."\n".
  297. 'Create it with:'."\n".
  298. 'mkdir -p '.escapeshellarg(realpath($cacheFolder))."\n".
  299. 'Or replace your cache setting with a valid writable folder path.',
  300. 5
  301. );
  302. }
  303. return $cacheFolder;
  304. }
  305. }