Source.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\View\Asset;
  7. use Magento\Framework\App\Filesystem\DirectoryList;
  8. use Magento\Framework\Filesystem\Directory\ReadFactory;
  9. use Magento\Framework\App\ObjectManager;
  10. use Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface;
  11. use Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple;
  12. use Magento\Framework\View\Design\Theme\ThemeProviderInterface;
  13. /**
  14. * A service for preprocessing content of assets
  15. *
  16. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  17. */
  18. class Source
  19. {
  20. /**
  21. * @var \Magento\Framework\Filesystem
  22. */
  23. private $filesystem;
  24. /**
  25. * @var \Magento\Framework\Filesystem\Directory\ReadInterface
  26. */
  27. protected $rootDir;
  28. /**
  29. * @var \Magento\Framework\Filesystem\Directory\WriteInterface
  30. */
  31. protected $tmpDir;
  32. /**
  33. * @var \Magento\Framework\View\Asset\PreProcessor\Pool
  34. */
  35. private $preProcessorPool;
  36. /**
  37. * @var \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile
  38. */
  39. protected $fallback;
  40. /**
  41. * @var \Magento\Framework\View\Design\Theme\ListInterface
  42. * @deprecated 100.0.2
  43. */
  44. private $themeList;
  45. /**
  46. * @var ChainFactoryInterface
  47. */
  48. private $chainFactory;
  49. /**
  50. * @var ReadFactory
  51. */
  52. private $readFactory;
  53. /**
  54. * @var ThemeProviderInterface
  55. */
  56. private $themeProvider;
  57. /**
  58. * Constructor
  59. *
  60. * @param \Magento\Framework\Filesystem $filesystem
  61. * @param ReadFactory $readFactory
  62. * @param PreProcessor\Pool $preProcessorPool
  63. * @param \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile $fallback
  64. * @param \Magento\Framework\View\Design\Theme\ListInterface $themeList
  65. * @param ChainFactoryInterface $chainFactory
  66. */
  67. public function __construct(
  68. \Magento\Framework\Filesystem $filesystem,
  69. ReadFactory $readFactory,
  70. PreProcessor\Pool $preProcessorPool,
  71. \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile $fallback,
  72. \Magento\Framework\View\Design\Theme\ListInterface $themeList,
  73. ChainFactoryInterface $chainFactory
  74. ) {
  75. $this->filesystem = $filesystem;
  76. $this->readFactory = $readFactory;
  77. $this->rootDir = $filesystem->getDirectoryRead(DirectoryList::ROOT);
  78. $this->tmpDir = $filesystem->getDirectoryWrite(DirectoryList::TMP_MATERIALIZATION_DIR);
  79. $this->preProcessorPool = $preProcessorPool;
  80. $this->fallback = $fallback;
  81. $this->themeList = $themeList;
  82. $this->chainFactory = $chainFactory;
  83. }
  84. /**
  85. * Get absolute path to the asset file
  86. *
  87. * @param LocalInterface $asset
  88. * @return bool|string
  89. */
  90. public function getFile(LocalInterface $asset)
  91. {
  92. $result = $this->preProcess($asset);
  93. if (!$result) {
  94. return false;
  95. }
  96. list($dir, $path) = $result;
  97. return $this->readFactory->create($dir)->getAbsolutePath($path);
  98. }
  99. /**
  100. * Get content of an asset
  101. *
  102. * @param LocalInterface $asset
  103. * @return bool|string
  104. */
  105. public function getContent(LocalInterface $asset)
  106. {
  107. $result = $this->preProcess($asset);
  108. if (!$result) {
  109. return false;
  110. }
  111. list($dir, $path) = $result;
  112. return $this->readFactory->create($dir)->readFile($path);
  113. }
  114. /**
  115. * Perform necessary preprocessing and materialization when the specified asset is requested
  116. *
  117. * Returns an array of two elements:
  118. * - directory where the file is supposed to be found
  119. * - relative path to the file
  120. *
  121. * returns false if source file was not found
  122. *
  123. * @param LocalInterface $asset
  124. * @return array|bool
  125. */
  126. private function preProcess(LocalInterface $asset)
  127. {
  128. $sourceFile = $this->findSourceFile($asset);
  129. $dir = $this->rootDir->getAbsolutePath();
  130. $path = '';
  131. if ($sourceFile) {
  132. $path = basename($sourceFile);
  133. $dir = dirname($sourceFile);
  134. }
  135. $chain = $this->createChain($asset, $dir, $path);
  136. $this->preProcessorPool->process($chain);
  137. $chain->assertValid();
  138. if ($chain->isChanged()) {
  139. $dir = $this->tmpDir->getAbsolutePath();
  140. $path = $chain->getTargetAssetPath();
  141. $this->tmpDir->writeFile($path, $chain->getContent());
  142. }
  143. if (empty($path)) {
  144. $result = false;
  145. } else {
  146. $result = [$dir, $path, $chain->getContentType()];
  147. }
  148. return $result;
  149. }
  150. /**
  151. * @param LocalInterface $asset
  152. * @return string
  153. */
  154. public function getSourceContentType(LocalInterface $asset)
  155. {
  156. list(,,$type) = $this->preProcess($asset);
  157. return $type;
  158. }
  159. /**
  160. * @param LocalInterface $asset
  161. * @return bool|string
  162. */
  163. public function findSource(LocalInterface $asset)
  164. {
  165. return $this->findSourceFile($asset);
  166. }
  167. /**
  168. * Infer a content type from the specified path
  169. *
  170. * @param string $path
  171. * @return string
  172. */
  173. public function getContentType($path)
  174. {
  175. return strtolower(pathinfo($path, PATHINFO_EXTENSION));
  176. }
  177. /**
  178. * Search for asset file depending on its context type
  179. *
  180. * @param LocalInterface $asset
  181. * @return bool|string
  182. * @throws \LogicException
  183. */
  184. private function findSourceFile(LocalInterface $asset)
  185. {
  186. $context = $asset->getContext();
  187. if ($context instanceof \Magento\Framework\View\Asset\File\FallbackContext) {
  188. $result = $this->findFileThroughFallback($asset, $context);
  189. } elseif ($context instanceof \Magento\Framework\View\Asset\File\Context) {
  190. $result = $this->findFile($asset, $context);
  191. } else {
  192. $type = get_class($context);
  193. throw new \LogicException("Support for {$type} is not implemented.");
  194. }
  195. return $result;
  196. }
  197. /**
  198. * Find asset file via fallback mechanism
  199. *
  200. * @param LocalInterface $asset
  201. * @param \Magento\Framework\View\Asset\File\FallbackContext $context
  202. * @return bool|string
  203. */
  204. private function findFileThroughFallback(
  205. LocalInterface $asset,
  206. \Magento\Framework\View\Asset\File\FallbackContext $context
  207. ) {
  208. $themeModel = $this->getThemeProvider()->getThemeByFullPath(
  209. $context->getAreaCode() . '/' . $context->getThemePath()
  210. );
  211. $sourceFile = $this->fallback->getFile(
  212. $context->getAreaCode(),
  213. $themeModel,
  214. $context->getLocale(),
  215. $asset->getFilePath(),
  216. $asset->getModule()
  217. );
  218. return $sourceFile;
  219. }
  220. /**
  221. * @return ThemeProviderInterface
  222. */
  223. private function getThemeProvider()
  224. {
  225. if (null === $this->themeProvider) {
  226. $this->themeProvider = ObjectManager::getInstance()->get(ThemeProviderInterface::class);
  227. }
  228. return $this->themeProvider;
  229. }
  230. /**
  231. * Find asset file by simply appending its path to the directory in context
  232. *
  233. * @param LocalInterface $asset
  234. * @param \Magento\Framework\View\Asset\File\Context $context
  235. * @return string
  236. */
  237. private function findFile(LocalInterface $asset, \Magento\Framework\View\Asset\File\Context $context)
  238. {
  239. $dir = $this->filesystem->getDirectoryRead($context->getBaseDirType());
  240. Simple::assertFilePathFormat($asset->getFilePath());
  241. return $dir->getAbsolutePath($asset->getPath());
  242. }
  243. /**
  244. * @param \Magento\Framework\View\Asset\LocalInterface $asset
  245. *
  246. * @return bool|string
  247. * @deprecated 100.1.0 If custom vendor directory is outside Magento root,
  248. * then this method will return unexpected result.
  249. */
  250. public function findRelativeSourceFilePath(LocalInterface $asset)
  251. {
  252. $sourceFile = $this->findSourceFile($asset);
  253. if (!$sourceFile) {
  254. return false;
  255. }
  256. return $this->rootDir->getRelativePath($sourceFile);
  257. }
  258. /**
  259. * Creates a chain for pre-processing
  260. *
  261. * @param LocalInterface $asset
  262. * @param string|bool $dir
  263. * @param string|bool $path
  264. * @return PreProcessor\Chain
  265. */
  266. private function createChain(LocalInterface $asset, $dir, $path)
  267. {
  268. if ($path) {
  269. $origContent = $this->readFactory->create($dir)->readFile($path);
  270. $origContentType = $this->getContentType($path);
  271. } else {
  272. $origContent = '';
  273. $origContentType = $asset->getContentType();
  274. }
  275. $chain = $this->chainFactory->create(
  276. [
  277. 'asset' => $asset,
  278. 'origContent' => $origContent,
  279. 'origContentType' => $origContentType,
  280. 'origAssetPath' => $dir . '/' . $path
  281. ]
  282. );
  283. return $chain;
  284. }
  285. }