Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
66.67% |
62 / 93 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
AssetFunction | |
66.67% |
62 / 93 |
|
75.00% |
3 / 4 |
80.37 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isIgnoreCaching | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
getFunctionName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
functionBodyImpl | |
62.20% |
51 / 82 |
|
0.00% |
0 / 1 |
74.44 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Mvc\Template\Plugin; |
6 | |
7 | use PeServer\Core\Environment; |
8 | use PeServer\Core\IO\IOUtility; |
9 | use PeServer\Core\IO\Path; |
10 | use PeServer\Core\IO\File; |
11 | use PeServer\Core\Collection\Arr; |
12 | use PeServer\Core\Text; |
13 | use PeServer\Core\TypeUtility; |
14 | use PeServer\Core\Html\HtmlDocument; |
15 | use PeServer\Core\Html\HtmlTagElement; |
16 | use PeServer\Core\Throws\TemplateException; |
17 | use PeServer\Core\Mvc\Template\Plugin\TemplateFunctionBase; |
18 | use PeServer\Core\Mvc\Template\Plugin\TemplatePluginArgument; |
19 | use PeServer\Core\Web\UrlUtility; |
20 | |
21 | /** |
22 | * 指定されたリソースをHTMLとして読み込む。 |
23 | * |
24 | * * 本番環境であればミニファイされたリソースを読もうとする |
25 | * * リビジョンをキャッシュバスターとして適用する |
26 | * |
27 | * $params |
28 | * * file: 対象リソース |
29 | * * auto_size: true/false trueの場合に実イメージサイズを読み込む(未指定は false)。 |
30 | * * include: true/false trueの場合にファイルの中身を使用する(結構適当)(未指定は false)。 |
31 | * * その他: 全部設定される |
32 | */ |
33 | class AssetFunction extends TemplateFunctionBase |
34 | { |
35 | public function __construct(TemplatePluginArgument $argument) |
36 | { |
37 | parent::__construct($argument); |
38 | } |
39 | |
40 | #region function |
41 | |
42 | /** |
43 | * キャッシュ考慮不要な(HTTP)パスか。 |
44 | * |
45 | * @param string $path |
46 | * @return bool |
47 | */ |
48 | private function isIgnoreCaching(string $path): bool |
49 | { |
50 | $isExternal = |
51 | Text::startsWith($path, '//', false) |
52 | || |
53 | Text::startsWith($path, 'https://', false) |
54 | || |
55 | Text::startsWith($path, 'http://', false) |
56 | || |
57 | Text::contains($path, '?', false); |
58 | |
59 | return $isExternal; |
60 | } |
61 | |
62 | #endregion |
63 | |
64 | #region TemplateFunctionBase |
65 | |
66 | public function getFunctionName(): string |
67 | { |
68 | return 'asset'; |
69 | } |
70 | |
71 | /** |
72 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
73 | */ |
74 | protected function functionBodyImpl(): string |
75 | { |
76 | /** @var string */ |
77 | $sourcePath = $this->params['file'] ?? Text::EMPTY; |
78 | if (Text::isNullOrEmpty($sourcePath)) { |
79 | return Text::EMPTY; |
80 | } |
81 | |
82 | $isProduction = $this->argument->environment->isProduction(); |
83 | |
84 | $fileExtension = Path::getFileExtension($sourcePath); |
85 | $extension = Text::toLower($fileExtension); |
86 | |
87 | $ignoreAsset = $this->isIgnoreCaching($sourcePath); |
88 | |
89 | $resourcePath = $sourcePath; |
90 | if (!$ignoreAsset) { |
91 | if ($isProduction) { |
92 | $minimalPath = Text::trim(Path::setEnvironmentName($sourcePath, 'min')); |
93 | $physicalPath = Path::combine($this->argument->programContext->publicDirectory, $minimalPath); |
94 | if (IOUtility::exists($physicalPath)) { |
95 | $resourcePath = Text::replace($minimalPath, "\\", '/'); |
96 | } |
97 | } |
98 | |
99 | $resourcePath .= '?' . $this->argument->environment->getRevision(); |
100 | } |
101 | |
102 | $dom = new HtmlDocument(); |
103 | if (!$isProduction) { |
104 | $dom->addComment(Text::dump($this->params)); |
105 | } |
106 | |
107 | $autoSize = TypeUtility::parseBoolean($this->params['auto_size'] ?? false); |
108 | $include = TypeUtility::parseBoolean($this->params['include'] ?? false); |
109 | |
110 | $filePath = Path::combine($this->argument->rootDirectoryPath, $sourcePath); |
111 | if (($autoSize || $include) || !FIle::exists($filePath)) { |
112 | // @phpstan-ignore-next-line nullは全取得だからOK |
113 | foreach ($this->argument->engine->getTemplateDir(null) as $dir) { |
114 | $path = Path::combine($dir, $sourcePath); |
115 | if (IOUtility::exists($path)) { |
116 | $filePath = $path; |
117 | break; |
118 | } |
119 | } |
120 | } |
121 | |
122 | $skipAttributes = [ |
123 | 'file', |
124 | 'auto_size', |
125 | 'include', |
126 | ]; |
127 | /** @var HtmlTagElement|null */ |
128 | $element = null; |
129 | |
130 | if (Arr::tryGet($this->params, 'rel', $relValue)) { |
131 | switch ($relValue) { |
132 | case 'icon': |
133 | $element = $dom->addTagElement('link'); |
134 | $element->setAttribute('href', $resourcePath); |
135 | $skipAttributes = array_merge($skipAttributes, ['href']); |
136 | break; |
137 | } |
138 | } |
139 | |
140 | if ($element === null) { |
141 | switch ($extension) { |
142 | case 'css': |
143 | if ($include) { |
144 | $element = $dom->addTagElement('style'); |
145 | |
146 | $content = File::readContent($filePath); |
147 | $element->addText($content->toString()); |
148 | } else { |
149 | $element = $dom->addTagElement('link'); |
150 | |
151 | $element->setAttribute('rel', 'stylesheet'); |
152 | $element->setAttribute('href', $resourcePath); |
153 | $skipAttributes = array_merge($skipAttributes, ['rel', 'href']); |
154 | } |
155 | break; |
156 | |
157 | case 'js': |
158 | $element = $dom->addTagElement('script'); |
159 | |
160 | if ($include) { |
161 | $content = File::readContent($filePath); |
162 | $element->addText($content->toString()); |
163 | } else { |
164 | $element->setAttribute('src', $resourcePath); |
165 | $skipAttributes = array_merge($skipAttributes, ['src']); |
166 | } |
167 | break; |
168 | |
169 | case 'png': |
170 | case 'jpeg': |
171 | case 'jpg': |
172 | $element = $dom->addTagElement('img'); |
173 | |
174 | $element->setAttribute('src', $resourcePath); |
175 | $skipAttributes = array_merge($skipAttributes, ['src']); |
176 | |
177 | if (!$ignoreAsset && ($autoSize || $include)) { |
178 | $imageSize = getimagesize($filePath); |
179 | if ($imageSize !== false) { |
180 | $element->setAttribute('width', strval($imageSize[0])); |
181 | $element->setAttribute('height', strval($imageSize[1])); |
182 | $skipAttributes = array_merge($skipAttributes, ['width', 'height']); |
183 | |
184 | if ($include) { |
185 | $content = File::readContent($filePath); |
186 | $base64 = $content->toBase64(); |
187 | $inline = 'data:' . $imageSize['mime'] . ';base64,' . $base64; |
188 | $element->setAttribute('src', $inline); |
189 | } |
190 | } |
191 | } |
192 | break; |
193 | |
194 | default: |
195 | throw new TemplateException($resourcePath); |
196 | } |
197 | } |
198 | |
199 | foreach ($this->params as $key => $value) { |
200 | if (Arr::containsValue($skipAttributes, $key)) { |
201 | continue; |
202 | } |
203 | $element->setAttribute($key, $value); |
204 | } |
205 | |
206 | return $dom->build(); |
207 | } |
208 | |
209 | #endregion |
210 | } |