BaseAsset.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <?php
  2. /**
  3. * @package Grav\Common\Assets
  4. *
  5. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Assets;
  9. use Grav\Common\Assets\Traits\AssetUtilsTrait;
  10. use Grav\Common\Config\Config;
  11. use Grav\Common\Grav;
  12. use Grav\Common\Uri;
  13. use Grav\Common\Utils;
  14. use Grav\Framework\Object\PropertyObject;
  15. use RocketTheme\Toolbox\File\File;
  16. use SplFileInfo;
  17. /**
  18. * Class BaseAsset
  19. * @package Grav\Common\Assets
  20. */
  21. abstract class BaseAsset extends PropertyObject
  22. {
  23. use AssetUtilsTrait;
  24. protected const CSS_ASSET = 1;
  25. protected const JS_ASSET = 2;
  26. protected const JS_MODULE_ASSET = 3;
  27. /** @var string|false */
  28. protected $asset;
  29. /** @var string */
  30. protected $asset_type;
  31. /** @var int */
  32. protected $order;
  33. /** @var string */
  34. protected $group;
  35. /** @var string */
  36. protected $position;
  37. /** @var int */
  38. protected $priority;
  39. /** @var array */
  40. protected $attributes = [];
  41. /** @var string */
  42. protected $timestamp;
  43. /** @var int|false */
  44. protected $modified;
  45. /** @var bool */
  46. protected $remote;
  47. /** @var string */
  48. protected $query = '';
  49. // Private Bits
  50. /** @var bool */
  51. private $css_rewrite = false;
  52. /** @var bool */
  53. private $css_minify = false;
  54. /**
  55. * @return string
  56. */
  57. abstract function render();
  58. /**
  59. * BaseAsset constructor.
  60. * @param array $elements
  61. * @param string|null $key
  62. */
  63. public function __construct(array $elements = [], ?string $key = null)
  64. {
  65. $base_config = [
  66. 'group' => 'head',
  67. 'position' => 'pipeline',
  68. 'priority' => 10,
  69. 'modified' => null,
  70. 'asset' => null
  71. ];
  72. // Merge base defaults
  73. $elements = array_merge($base_config, $elements);
  74. parent::__construct($elements, $key);
  75. }
  76. /**
  77. * @param string|false $asset
  78. * @param array $options
  79. * @return $this|false
  80. */
  81. public function init($asset, $options)
  82. {
  83. if (!$asset) {
  84. return false;
  85. }
  86. $config = Grav::instance()['config'];
  87. $uri = Grav::instance()['uri'];
  88. // set attributes
  89. foreach ($options as $key => $value) {
  90. if ($this->hasProperty($key)) {
  91. $this->setProperty($key, $value);
  92. } else {
  93. $this->attributes[$key] = $value;
  94. }
  95. }
  96. // Force priority to be an int
  97. $this->priority = (int) $this->priority;
  98. // Do some special stuff for CSS/JS (not inline)
  99. if (!Utils::startsWith($this->getType(), 'inline')) {
  100. $this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
  101. $this->remote = static::isRemoteLink($asset);
  102. // Move this to render?
  103. if (!$this->remote) {
  104. $asset_parts = parse_url($asset);
  105. if (isset($asset_parts['query'])) {
  106. $this->query = $asset_parts['query'];
  107. unset($asset_parts['query']);
  108. $asset = Uri::buildUrl($asset_parts);
  109. }
  110. $locator = Grav::instance()['locator'];
  111. if ($locator->isStream($asset)) {
  112. $path = $locator->findResource($asset, true);
  113. } else {
  114. $path = GRAV_WEBROOT . $asset;
  115. }
  116. // If local file is missing return
  117. if ($path === false) {
  118. return false;
  119. }
  120. $file = new SplFileInfo($path);
  121. $asset = $this->buildLocalLink($file->getPathname());
  122. $this->modified = $file->isFile() ? $file->getMTime() : false;
  123. }
  124. }
  125. $this->asset = $asset;
  126. return $this;
  127. }
  128. /**
  129. * @return string|false
  130. */
  131. public function getAsset()
  132. {
  133. return $this->asset;
  134. }
  135. /**
  136. * @return bool
  137. */
  138. public function getRemote()
  139. {
  140. return $this->remote;
  141. }
  142. /**
  143. * @param string $position
  144. * @return $this
  145. */
  146. public function setPosition($position)
  147. {
  148. $this->position = $position;
  149. return $this;
  150. }
  151. /**
  152. * Receive asset location and return the SRI integrity hash
  153. *
  154. * @param string $input
  155. * @return string
  156. */
  157. public static function integrityHash($input)
  158. {
  159. $grav = Grav::instance();
  160. $uri = $grav['uri'];
  161. $assetsConfig = $grav['config']->get('system.assets');
  162. if (!self::isRemoteLink($input) && !empty($assetsConfig['enable_asset_sri']) && $assetsConfig['enable_asset_sri']) {
  163. $input = preg_replace('#^' . $uri->rootUrl() . '#', '', $input);
  164. $asset = File::instance(GRAV_WEBROOT . $input);
  165. if ($asset->exists()) {
  166. $dataToHash = $asset->content();
  167. $hash = hash('sha256', $dataToHash, true);
  168. $hash_base64 = base64_encode($hash);
  169. return ' integrity="sha256-' . $hash_base64 . '"';
  170. }
  171. }
  172. return '';
  173. }
  174. /**
  175. *
  176. * Get the last modification time of asset
  177. *
  178. * @param string $asset the asset string reference
  179. *
  180. * @return string the last modifcation time or false on error
  181. */
  182. // protected function getLastModificationTime($asset)
  183. // {
  184. // $file = GRAV_WEBROOT . $asset;
  185. // if (Grav::instance()['locator']->isStream($asset)) {
  186. // $file = $this->buildLocalLink($asset, true);
  187. // }
  188. //
  189. // return file_exists($file) ? filemtime($file) : false;
  190. // }
  191. /**
  192. *
  193. * Build local links including grav asset shortcodes
  194. *
  195. * @param string $asset the asset string reference
  196. *
  197. * @return string|false the final link url to the asset
  198. */
  199. protected function buildLocalLink($asset)
  200. {
  201. if ($asset) {
  202. return $this->base_url . ltrim(Utils::replaceFirstOccurrence(GRAV_WEBROOT, '', $asset), '/');
  203. }
  204. return false;
  205. }
  206. /**
  207. * Implements JsonSerializable interface.
  208. *
  209. * @return array
  210. */
  211. #[\ReturnTypeWillChange]
  212. public function jsonSerialize()
  213. {
  214. return ['type' => $this->getType(), 'elements' => $this->getElements()];
  215. }
  216. /**
  217. * Placeholder for AssetUtilsTrait method
  218. *
  219. * @param string $file
  220. * @param string $dir
  221. * @param bool $local
  222. * @return string
  223. */
  224. protected function cssRewrite($file, $dir, $local)
  225. {
  226. return '';
  227. }
  228. /**
  229. * Finds relative JS urls() and rewrites the URL with an absolute one
  230. *
  231. * @param string $file the css source file
  232. * @param string $dir local relative path to the css file
  233. * @param bool $local is this a local or remote asset
  234. * @return string
  235. */
  236. protected function jsRewrite($file, $dir, $local)
  237. {
  238. return '';
  239. }
  240. }