PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.14
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.14
1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 1.3.47 1.3.46 1.3.45 All 177 releases
disco / vendor / inpsyde / assets / src / AssetManager.php

AssetManager.php in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.4.14, at vendor/inpsyde/assets/src/AssetManager.php

321 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Assets package.
5 *
6 * (c) Inpsyde GmbH
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 declare(strict_types=1);
13
14 namespace Inpsyde\Assets;
15
16 use Inpsyde\Assets\Handler\AssetHandler;
17 use Inpsyde\Assets\Handler\OutputFilterAwareAssetHandler;
18 use Inpsyde\Assets\Handler\ScriptHandler;
19 use Inpsyde\Assets\Handler\StyleHandler;
20 use Inpsyde\Assets\Util\AssetHookResolver;
21 use Inpsyde\Assets\Asset;
22
23 final class AssetManager
24 {
25 public const ACTION_SETUP = 'inpsyde.assets.setup';
26
27 /**
28 * Contains the state of the AssetManager, where keys are hook names that are already added
29 * to avoid add them more than once.
30 *
31 * @var array<string, bool>
32 */
33 private $hooksAdded = [];
34
35 /**
36 * @var \SplObjectStorage<Asset, array{string, string}>
37 */
38 private $assets;
39
40 /**
41 * @var array<AssetHandler>
42 */
43 private $handlers = [];
44
45 /**
46 * @var AssetHookResolver
47 */
48 private $hookResolver;
49
50 /**
51 * @var bool
52 */
53 private $setupDone = false;
54
55 /**
56 * @param AssetHookResolver|null $hookResolver
57 */
58 public function __construct(AssetHookResolver $hookResolver = null)
59 {
60 $this->hookResolver = $hookResolver ?? new AssetHookResolver();
61 $this->assets = new \SplObjectStorage();
62 }
63
64 /**
65 * @return static
66 */
67 public function useDefaultHandlers(): AssetManager
68 {
69 empty($this->handlers[StyleHandler::class])
70 and $this->handlers[StyleHandler::class] = new StyleHandler(wp_styles());
71
72 empty($this->handlers[ScriptHandler::class])
73 and $this->handlers[ScriptHandler::class] = new ScriptHandler(wp_scripts());
74
75 return $this;
76 }
77
78 /**
79 * @param string $name
80 * @param AssetHandler $handler
81 *
82 * @return static
83 */
84 public function withHandler(string $name, AssetHandler $handler): AssetManager
85 {
86 $this->handlers[$name] = $handler;
87
88 return $this;
89 }
90
91 /**
92 * @return array<AssetHandler>
93 */
94 public function handlers(): array
95 {
96 return $this->handlers;
97 }
98
99 /**
100 * @param Asset $asset
101 * @param Asset ...$assets
102 *
103 * @return static
104 */
105 public function register(Asset $asset, Asset ...$assets): AssetManager
106 {
107 array_unshift($assets, $asset);
108
109 foreach ($assets as $asset) {
110 $handle = $asset->handle();
111 if ($handle) {
112 $this->assets->attach($asset, [$handle, get_class($asset)]);
113 }
114 }
115
116 return $this;
117 }
118
119 /**
120 * @return array<string, array<string, Asset>>
121 */
122 public function assets(): array
123 {
124 $this->ensureSetup();
125
126 $found = [];
127 $this->assets->rewind();
128 while ($this->assets->valid()) {
129 $asset = $this->assets->current();
130 [$handle, $class] = $this->assets->getInfo();
131 isset($found[$class]) or $found[$class] = [];
132 $found[$class][$handle] = $asset;
133
134 $this->assets->next();
135 }
136
137 return $found;
138 }
139
140 /**
141 * Retrieve an `Asset` instance by a given asset handle and type (class).
142 *
143 * If the handle is unique by type, type can be omitted.
144 * It is possible to use a parent class as type.
145 * E.g an asset of a type `MyScript` that extends `Script` can be also retrieved passing
146 * either `MyScript or `Script` as type.
147 *
148 * @param string $handle
149 * @param class-string|null $type
150 *
151 * @return Asset|null
152 */
153 public function asset(string $handle, ?string $type = null): ?Asset
154 {
155 $this->ensureSetup();
156
157 /** @var Asset|null $found */
158 $found = null;
159 $this->assets->rewind();
160
161 while ($this->assets->valid()) {
162 $asset = $this->assets->current();
163 $this->assets->next();
164
165 if (($asset->handle() !== $handle) || ($type && !is_a($asset, $type))) {
166 continue;
167 }
168
169 if ($found) {
170 // only one asset can match
171 return null;
172 }
173
174 $found = $asset;
175 }
176
177 return $found;
178 }
179
180 /**
181 * @return bool
182 */
183 public function setup(): bool
184 {
185 $hooksAdded = 0;
186
187 /**
188 * It is possible to execute AssetManager::setup() at a specific hook to only process assets
189 * specific of that hook.
190 *
191 * E.g. `add_action('enqueue_block_editor_assets', [new AssetManager, 'setup']);`
192 *
193 * `$this->hookResolver->resolve()` will return current hook if it is one of the assets
194 * enqueuing hook.
195 */
196 foreach ($this->hookResolver->resolve() as $hook) {
197 // If the hook was already added, or it is in the past, don't bother adding.
198 if (!empty($this->hooksAdded[$hook]) || (did_action($hook) && !doing_action($hook))) {
199 continue;
200 }
201
202 $hooksAdded++;
203 $this->hooksAdded[$hook] = true;
204
205 add_action(
206 $hook,
207 function () use ($hook) {
208 $this->processAssets($hook);
209 }
210 );
211 }
212
213 return $hooksAdded > 0;
214 }
215
216 /**
217 * Returning all matching assets to given hook.
218 *
219 * @param string $currentHook
220 *
221 * @return array<Asset>
222 */
223 public function currentAssets(string $currentHook): array
224 {
225 return $this->loopCurrentHookAssets($currentHook, false);
226 }
227
228 /**
229 * @param string $currentHook
230 *
231 * @return void
232 */
233 private function processAssets(string $currentHook): void
234 {
235 $this->loopCurrentHookAssets($currentHook, true);
236 }
237
238 /**
239 * @param string $currentHook
240 * @param bool $process
241 *
242 * @return array<Asset>
243 *
244 * phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
245 */
246 private function loopCurrentHookAssets(string $currentHook, bool $process): array
247 {
248 $this->ensureSetup();
249 if (!$this->assets->count()) {
250 return [];
251 }
252
253 /** @var int|null $locationId */
254 $locationId = Asset::HOOK_TO_LOCATION[$currentHook] ?? null;
255 if (!$locationId) {
256 return [];
257 }
258
259 $found = [];
260
261 $this->assets->rewind();
262 while ($this->assets->valid()) {
263 $asset = $this->assets->current();
264 $this->assets->next();
265
266 $handlerName = $asset->handler();
267 $handler = $this->handlers[$handlerName] ?? null;
268 if (!$handler) {
269 continue;
270 }
271
272 $location = $asset->location();
273 if (($location & $locationId) !== $locationId) {
274 continue;
275 }
276
277 $found[] = $asset;
278 if (!$process) {
279 continue;
280 }
281
282 $done = $asset->enqueue()
283 ? $handler->enqueue($asset)
284 : $handler->register($asset);
285 if ($done && ($handler instanceof OutputFilterAwareAssetHandler)) {
286 $handler->filter($asset);
287 }
288 }
289
290 return $found;
291 }
292
293 /**
294 * @return void
295 */
296 private function ensureSetup(): void
297 {
298 if ($this->setupDone) {
299 return;
300 }
301
302 $this->setupDone = true;
303
304 $lastHook = $this->hookResolver->lastHook();
305
306 /**
307 * We should not setup if there's no hook or last hook already fired.
308 *
309 * @psalm-suppress PossiblyNullArgument
310 */
311 if (!$lastHook && did_action($lastHook) && !doing_action($lastHook)) {
312 $this->assets = new \SplObjectStorage();
313
314 return;
315 }
316
317 $this->useDefaultHandlers();
318 do_action(self::ACTION_SETUP, $this);
319 }
320 }
321