CompiledFile.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <?php
  2. /**
  3. * @package Grav\Common\File
  4. *
  5. * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\File;
  9. use Exception;
  10. use Grav\Common\Utils;
  11. use RocketTheme\Toolbox\File\PhpFile;
  12. use RuntimeException;
  13. use Throwable;
  14. use function function_exists;
  15. use function get_class;
  16. /**
  17. * Trait CompiledFile
  18. * @package Grav\Common\File
  19. */
  20. trait CompiledFile
  21. {
  22. /**
  23. * Get/set parsed file contents.
  24. *
  25. * @param mixed $var
  26. * @return array
  27. */
  28. public function content($var = null)
  29. {
  30. try {
  31. // If nothing has been loaded, attempt to get pre-compiled version of the file first.
  32. if ($var === null && $this->raw === null && $this->content === null) {
  33. $key = md5($this->filename);
  34. $file = PhpFile::instance(CACHE_DIR . "compiled/files/{$key}{$this->extension}.php");
  35. $modified = $this->modified();
  36. if (!$modified) {
  37. try {
  38. return $this->decode($this->raw());
  39. } catch (Throwable $e) {
  40. // If the compiled file is broken, we can safely ignore the error and continue.
  41. }
  42. }
  43. $class = get_class($this);
  44. $cache = $file->exists() ? $file->content() : null;
  45. // Load real file if cache isn't up to date (or is invalid).
  46. if (!isset($cache['@class'])
  47. || $cache['@class'] !== $class
  48. || $cache['modified'] !== $modified
  49. || $cache['filename'] !== $this->filename
  50. ) {
  51. // Attempt to lock the file for writing.
  52. try {
  53. $file->lock(false);
  54. } catch (Exception $e) {
  55. // Another process has locked the file; we will check this in a bit.
  56. }
  57. // Decode RAW file into compiled array.
  58. $data = (array)$this->decode($this->raw());
  59. $cache = [
  60. '@class' => $class,
  61. 'filename' => $this->filename,
  62. 'modified' => $modified,
  63. 'data' => $data
  64. ];
  65. // If compiled file wasn't already locked by another process, save it.
  66. if ($file->locked() !== false) {
  67. $file->save($cache);
  68. $file->unlock();
  69. // Compile cached file into bytecode cache
  70. if (function_exists('opcache_invalidate')) {
  71. // Silence error if function exists, but is restricted.
  72. @opcache_invalidate($file->filename(), true);
  73. }
  74. }
  75. }
  76. $file->free();
  77. $this->content = $cache['data'];
  78. }
  79. } catch (Exception $e) {
  80. throw new RuntimeException(sprintf('Failed to read %s: %s', Utils::basename($this->filename), $e->getMessage()), 500, $e);
  81. }
  82. return parent::content($var);
  83. }
  84. /**
  85. * Serialize file.
  86. *
  87. * @return array
  88. */
  89. public function __sleep()
  90. {
  91. return [
  92. 'filename',
  93. 'extension',
  94. 'raw',
  95. 'content',
  96. 'settings'
  97. ];
  98. }
  99. /**
  100. * Unserialize file.
  101. */
  102. public function __wakeup()
  103. {
  104. if (!isset(static::$instances[$this->filename])) {
  105. static::$instances[$this->filename] = $this;
  106. }
  107. }
  108. }