Blueprints.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace Grav\Common\Config;
  3. use Grav\Common\File\CompiledYamlFile;
  4. use Grav\Common\Grav;
  5. use Grav\Common\Filesystem\Folder;
  6. use RocketTheme\Toolbox\Blueprints\Blueprints as BaseBlueprints;
  7. use RocketTheme\Toolbox\File\PhpFile;
  8. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  9. /**
  10. * The Blueprints class contains configuration rules.
  11. *
  12. * @author RocketTheme
  13. * @license MIT
  14. */
  15. class Blueprints extends BaseBlueprints
  16. {
  17. protected $grav;
  18. protected $files = [];
  19. protected $blueprints;
  20. public function __construct(array $serialized = null, Grav $grav = null)
  21. {
  22. parent::__construct($serialized);
  23. $this->grav = $grav ?: Grav::instance();
  24. }
  25. public function init()
  26. {
  27. /** @var UniformResourceLocator $locator */
  28. $locator = $this->grav['locator'];
  29. $blueprints = $locator->findResources('blueprints://config');
  30. $plugins = $locator->findResources('plugins://');
  31. $blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
  32. $this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles);
  33. }
  34. protected function loadCompiledBlueprints($blueprints, $blueprintFiles)
  35. {
  36. $checksum = md5(serialize($blueprints));
  37. $filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
  38. $checksum .= ':'.md5(serialize($blueprintFiles));
  39. $class = get_class($this);
  40. $file = PhpFile::instance($filename);
  41. if ($file->exists()) {
  42. $cache = $file->exists() ? $file->content() : null;
  43. } else {
  44. $cache = null;
  45. }
  46. // Load real file if cache isn't up to date (or is invalid).
  47. if (
  48. !is_array($cache)
  49. || empty($cache['checksum'])
  50. || empty($cache['$class'])
  51. || $cache['checksum'] != $checksum
  52. || $cache['@class'] != $class
  53. ) {
  54. // Attempt to lock the file for writing.
  55. $file->lock(false);
  56. // Load blueprints.
  57. $this->blueprints = new Blueprints();
  58. foreach ($blueprintFiles as $key => $files) {
  59. $this->loadBlueprints($key);
  60. }
  61. $cache = [
  62. '@class' => $class,
  63. 'checksum' => $checksum,
  64. 'files' => $blueprintFiles,
  65. 'data' => $this->blueprints->toArray()
  66. ];
  67. // If compiled file wasn't already locked by another process, save it.
  68. if ($file->locked() !== false) {
  69. $file->save($cache);
  70. $file->unlock();
  71. }
  72. } else {
  73. $this->blueprints = new Blueprints($cache['data']);
  74. }
  75. }
  76. /**
  77. * Load global blueprints.
  78. *
  79. * @param string $key
  80. * @param array $files
  81. */
  82. public function loadBlueprints($key, array $files = null)
  83. {
  84. if (is_null($files)) {
  85. $files = $this->files[$key];
  86. }
  87. foreach ($files as $name => $item) {
  88. $file = CompiledYamlFile::instance($item['file']);
  89. $this->blueprints->embed($name, $file->content(), '/');
  90. }
  91. }
  92. /**
  93. * Get all blueprint files (including plugins).
  94. *
  95. * @param array $blueprints
  96. * @param array $plugins
  97. * @return array
  98. */
  99. protected function getBlueprintFiles(array $blueprints, array $plugins)
  100. {
  101. $list = [];
  102. foreach (array_reverse($plugins) as $folder) {
  103. $list += $this->detectPlugins($folder, true);
  104. }
  105. foreach (array_reverse($blueprints) as $folder) {
  106. $list += $this->detectConfig($folder, true);
  107. }
  108. return $list;
  109. }
  110. /**
  111. * Detects all plugins with a configuration file and returns last modification time.
  112. *
  113. * @param string $lookup Location to look up from.
  114. * @param bool $blueprints
  115. * @return array
  116. * @internal
  117. */
  118. protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
  119. {
  120. $find = $blueprints ? 'blueprints.yaml' : '.yaml';
  121. $location = $blueprints ? 'blueprintFiles' : 'configFiles';
  122. $path = trim(Folder::getRelativePath($lookup), '/');
  123. if (isset($this->{$location}[$path])) {
  124. return [$path => $this->{$location}[$path]];
  125. }
  126. $list = [];
  127. if (is_dir($lookup)) {
  128. $iterator = new \DirectoryIterator($lookup);
  129. /** @var \DirectoryIterator $directory */
  130. foreach ($iterator as $directory) {
  131. if (!$directory->isDir() || $directory->isDot()) {
  132. continue;
  133. }
  134. $name = $directory->getBasename();
  135. $filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
  136. if (is_file($filename)) {
  137. $list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
  138. }
  139. }
  140. }
  141. $this->{$location}[$path] = $list;
  142. return [$path => $list];
  143. }
  144. /**
  145. * Detects all plugins with a configuration file and returns last modification time.
  146. *
  147. * @param string $lookup Location to look up from.
  148. * @param bool $blueprints
  149. * @return array
  150. * @internal
  151. */
  152. protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
  153. {
  154. $location = $blueprints ? 'blueprintFiles' : 'configFiles';
  155. $path = trim(Folder::getRelativePath($lookup), '/');
  156. if (isset($this->{$location}[$path])) {
  157. return [$path => $this->{$location}[$path]];
  158. }
  159. if (is_dir($lookup)) {
  160. // Find all system and user configuration files.
  161. $options = [
  162. 'compare' => 'Filename',
  163. 'pattern' => '|\.yaml$|',
  164. 'filters' => [
  165. 'key' => '|\.yaml$|',
  166. 'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
  167. return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
  168. }],
  169. 'key' => 'SubPathname'
  170. ];
  171. $list = Folder::all($lookup, $options);
  172. } else {
  173. $list = [];
  174. }
  175. $this->{$location}[$path] = $list;
  176. return [$path => $list];
  177. }
  178. }