1 | <?php |
||
2 | /** |
||
3 | * ImageOptimize plugin for Craft CMS |
||
4 | * |
||
5 | * Automatically optimize images after they've been transformed |
||
6 | * |
||
7 | * @link https://nystudio107.com |
||
8 | * @copyright Copyright (c) 2017 nystudio107 |
||
9 | */ |
||
10 | |||
11 | namespace nystudio107\imageoptimize; |
||
12 | |||
13 | use Craft; |
||
14 | use craft\base\Field; |
||
15 | use craft\base\Model; |
||
16 | use craft\base\Plugin; |
||
17 | use craft\elements\Asset; |
||
18 | use craft\events\DefineAssetThumbUrlEvent; |
||
19 | use craft\events\DefineAssetUrlEvent; |
||
20 | use craft\events\ElementEvent; |
||
21 | use craft\events\FieldEvent; |
||
22 | use craft\events\ImageTransformerOperationEvent; |
||
23 | use craft\events\PluginEvent; |
||
24 | use craft\events\RegisterComponentTypesEvent; |
||
25 | use craft\events\RegisterTemplateRootsEvent; |
||
26 | use craft\events\RegisterUrlRulesEvent; |
||
27 | use craft\events\ReplaceAssetEvent; |
||
28 | use craft\events\VolumeEvent; |
||
29 | use craft\helpers\ArrayHelper; |
||
30 | use craft\helpers\UrlHelper; |
||
31 | use craft\imagetransforms\ImageTransformer; |
||
32 | use craft\models\FieldLayout; |
||
33 | use craft\services\Assets; |
||
34 | use craft\services\Elements; |
||
35 | use craft\services\Fields; |
||
36 | use craft\services\Plugins; |
||
37 | use craft\services\Utilities; |
||
38 | use craft\services\Volumes; |
||
39 | use craft\web\Controller; |
||
40 | use craft\web\TemplateResponseBehavior; |
||
41 | use craft\web\twig\variables\CraftVariable; |
||
42 | use craft\web\UrlManager; |
||
43 | use craft\web\View; |
||
44 | use nystudio107\imageoptimize\fields\OptimizedImages; |
||
45 | use nystudio107\imageoptimize\imagetransforms\CraftImageTransform; |
||
46 | use nystudio107\imageoptimize\imagetransforms\ImageTransformInterface; |
||
47 | use nystudio107\imageoptimize\models\Settings; |
||
48 | use nystudio107\imageoptimize\services\ServicesTrait; |
||
49 | use nystudio107\imageoptimize\utilities\ImageOptimizeUtility; |
||
50 | use nystudio107\imageoptimize\variables\ImageOptimizeVariable; |
||
51 | use ReflectionClassConstant; |
||
52 | use ReflectionException; |
||
53 | use yii\base\Event; |
||
54 | use yii\base\Exception; |
||
55 | use yii\base\InvalidConfigException; |
||
56 | use yii\web\Response; |
||
57 | use function function_exists; |
||
58 | |||
59 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||
60 | |||
61 | /** |
||
62 | * Class ImageOptimize |
||
63 | * |
||
64 | * @author nystudio107 |
||
65 | * @package ImageOptimize |
||
66 | * @since 1.0.0 |
||
67 | * |
||
68 | * @property ImageTransformInterface $transformMethod |
||
69 | */ |
||
70 | class ImageOptimize extends Plugin |
||
71 | { |
||
72 | // Traits |
||
73 | // ========================================================================= |
||
74 | |||
75 | use ServicesTrait; |
||
76 | |||
77 | // Static Properties |
||
78 | // ========================================================================= |
||
79 | |||
80 | /** |
||
81 | * @var ?ImageOptimize |
||
82 | */ |
||
83 | public static ?ImageOptimize $plugin = null; |
||
84 | |||
85 | /** |
||
86 | * @var bool |
||
87 | */ |
||
88 | public static bool $generatePlaceholders = true; |
||
89 | |||
90 | // Public Properties |
||
91 | // ========================================================================= |
||
92 | /** |
||
93 | * @var string |
||
94 | */ |
||
95 | public string $schemaVersion = '1.0.0'; |
||
96 | |||
97 | /** |
||
98 | * @var bool |
||
99 | */ |
||
100 | public bool $hasCpSection = false; |
||
101 | |||
102 | /** |
||
103 | * @var bool |
||
104 | */ |
||
105 | public bool $hasCpSettings = true; |
||
106 | |||
107 | // Public Methods |
||
108 | // ========================================================================= |
||
109 | |||
110 | /** |
||
111 | * @inheritdoc |
||
112 | */ |
||
113 | public function init(): void |
||
114 | { |
||
115 | parent::init(); |
||
116 | self::$plugin = $this; |
||
117 | // Handle any console commands |
||
118 | $request = Craft::$app->getRequest(); |
||
119 | if ($request->getIsConsoleRequest()) { |
||
120 | $this->controllerNamespace = 'nystudio107\imageoptimize\console\controllers'; |
||
121 | } |
||
122 | // Set the image transform component |
||
123 | $this->setImageTransformComponent(); |
||
124 | // Add in our Craft components |
||
125 | $this->addComponents(); |
||
126 | // Install our global event handlers |
||
127 | $this->installEventHandlers(); |
||
128 | // Log that the plugin has loaded |
||
129 | Craft::info( |
||
130 | Craft::t( |
||
131 | 'image-optimize', |
||
132 | '{name} plugin loaded', |
||
133 | ['name' => $this->name] |
||
134 | ), |
||
135 | __METHOD__ |
||
136 | ); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * @inheritdoc |
||
141 | */ |
||
142 | public function getSettingsResponse(): TemplateResponseBehavior|Response |
||
143 | { |
||
144 | $view = Craft::$app->getView(); |
||
145 | $namespace = $view->getNamespace(); |
||
146 | $view->setNamespace('settings'); |
||
147 | $settingsHtml = $this->settingsHtml(); |
||
148 | $view->setNamespace($namespace); |
||
149 | /** @var Controller $controller */ |
||
150 | $controller = Craft::$app->controller; |
||
0 ignored issues
–
show
|
|||
151 | |||
152 | return $controller->renderTemplate('image-optimize/settings/index.twig', [ |
||
153 | 'plugin' => $this, |
||
154 | 'settingsHtml' => $settingsHtml, |
||
155 | ]); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * @inheritdoc |
||
160 | */ |
||
161 | public function settingsHtml(): ?string |
||
162 | { |
||
163 | // Get only the user-editable settings |
||
164 | /** @var Settings $settings */ |
||
165 | $settings = $this->getSettings(); |
||
166 | |||
167 | // Get the image transform types |
||
168 | $allImageTransformTypes = self::$plugin->optimize->getAllImageTransformTypes(); |
||
169 | $imageTransformTypeOptions = []; |
||
170 | /** @var ImageTransformInterface $class */ |
||
171 | foreach ($allImageTransformTypes as $class) { |
||
172 | if ($class::isSelectable()) { |
||
173 | $imageTransformTypeOptions[] = [ |
||
174 | 'value' => $class, |
||
175 | 'label' => $class::displayName(), |
||
176 | ]; |
||
177 | } |
||
178 | } |
||
179 | // Sort them by name |
||
180 | ArrayHelper::multisort($imageTransformTypeOptions, 'label'); |
||
181 | |||
182 | // Render the settings template |
||
183 | try { |
||
184 | return Craft::$app->getView()->renderTemplate( |
||
185 | 'image-optimize/settings/_settings.twig', |
||
186 | [ |
||
187 | 'settings' => $settings, |
||
188 | 'gdInstalled' => function_exists('imagecreatefromjpeg'), |
||
189 | 'imageTransformTypeOptions' => $imageTransformTypeOptions, |
||
190 | 'allImageTransformTypes' => $allImageTransformTypes, |
||
191 | 'imageTransform' => self::$plugin->transformMethod, |
||
192 | ] |
||
193 | ); |
||
194 | } catch (Exception $e) { |
||
195 | Craft::error($e->getMessage(), __METHOD__); |
||
196 | } |
||
197 | |||
198 | return ''; |
||
199 | } |
||
200 | |||
201 | // Protected Methods |
||
202 | // ========================================================================= |
||
203 | |||
204 | /** |
||
205 | * @inheritdoc |
||
206 | */ |
||
207 | protected function createSettingsModel(): ?Model |
||
208 | { |
||
209 | return new Settings(); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Set the transformMethod component |
||
214 | */ |
||
215 | protected function setImageTransformComponent(): void |
||
216 | { |
||
217 | /** @var Settings $settings */ |
||
218 | $settings = $this->getSettings(); |
||
219 | $definition = array_merge( |
||
220 | $settings->imageTransformTypeSettings[$settings->transformClass] ?? [], |
||
221 | ['class' => $settings->transformClass] |
||
222 | ); |
||
223 | try { |
||
224 | $this->set('transformMethod', $definition); |
||
225 | } catch (InvalidConfigException $e) { |
||
226 | Craft::error($e->getMessage(), __METHOD__); |
||
227 | } |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Add in our Craft components |
||
232 | */ |
||
233 | protected function addComponents(): void |
||
234 | { |
||
235 | // Register our variables |
||
236 | Event::on( |
||
237 | CraftVariable::class, |
||
238 | CraftVariable::EVENT_INIT, |
||
239 | function(Event $event) { |
||
240 | /** @var CraftVariable $variable */ |
||
241 | $variable = $event->sender; |
||
242 | $variable->set('imageOptimize', [ |
||
243 | 'class' => ImageOptimizeVariable::class, |
||
244 | 'viteService' => $this->vite, |
||
245 | ]); |
||
246 | } |
||
247 | ); |
||
248 | |||
249 | // Register our Field |
||
250 | Event::on( |
||
251 | Fields::class, |
||
252 | Fields::EVENT_REGISTER_FIELD_TYPES, |
||
253 | static function(RegisterComponentTypesEvent $event) { |
||
254 | Craft::debug( |
||
255 | 'Fields::EVENT_REGISTER_FIELD_TYPES', |
||
256 | __METHOD__ |
||
257 | ); |
||
258 | $event->types[] = OptimizedImages::class; |
||
259 | } |
||
260 | ); |
||
261 | |||
262 | // Register our Utility only if they are using the CraftImageTransform method |
||
263 | if (self::$plugin->transformMethod instanceof CraftImageTransform) { |
||
264 | Event::on( |
||
265 | Utilities::class, |
||
266 | Utilities::EVENT_REGISTER_UTILITIES, |
||
267 | static function(RegisterComponentTypesEvent $event) { |
||
268 | $event->types[] = ImageOptimizeUtility::class; |
||
269 | } |
||
270 | ); |
||
271 | } |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * Install our event handlers |
||
276 | */ |
||
277 | protected function installEventHandlers(): void |
||
278 | { |
||
279 | $this->installAssetEventHandlers(); |
||
280 | $this->installElementEventHandlers(); |
||
281 | $this->installMiscEventHandlers(); |
||
282 | $request = Craft::$app->getRequest(); |
||
283 | // Install only for non-console site requests |
||
284 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||
285 | $this->installSiteEventListeners(); |
||
286 | } |
||
287 | // Install only for non-console cp requests |
||
288 | if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { |
||
289 | $this->installCpEventListeners(); |
||
290 | } |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Install our Asset event handlers |
||
295 | */ |
||
296 | protected function installAssetEventHandlers(): void |
||
297 | { |
||
298 | // Use Asset::EVENT_BEFORE_DEFINE_URL if it's available |
||
299 | // ref: https://github.com/craftcms/cms/issues/13018 |
||
300 | try { |
||
301 | $ref = new ReflectionClassConstant(Asset::class, 'EVENT_BEFORE_DEFINE_URL'); |
||
302 | } /** @noinspection PhpRedundantCatchClauseInspection */ catch (ReflectionException) { |
||
303 | $ref = null; |
||
304 | } |
||
305 | $eventName = $ref?->getDeclaringClass()->name === Asset::class |
||
306 | ? Asset::EVENT_BEFORE_DEFINE_URL |
||
307 | : Asset::EVENT_DEFINE_URL; |
||
308 | // Handler: Assets::EVENT_DEFINE_URL |
||
309 | Event::on( |
||
310 | Asset::class, |
||
311 | $eventName, |
||
312 | static function(DefineAssetUrlEvent $event): void { |
||
313 | Craft::debug( |
||
314 | 'Asset::EVENT_DEFINE_URL', |
||
315 | __METHOD__ |
||
316 | ); |
||
317 | // Return the URL to the asset URL or null to let Craft handle it |
||
318 | $event->url = ImageOptimize::$plugin->optimize->handleGetAssetUrlEvent( |
||
319 | $event |
||
320 | ); |
||
321 | } |
||
322 | ); |
||
323 | |||
324 | // Handler: Assets::EVENT_GET_ASSET_THUMB_URL |
||
325 | Event::on( |
||
326 | Assets::class, |
||
327 | Assets::EVENT_DEFINE_THUMB_URL, |
||
328 | static function(DefineAssetThumbUrlEvent $event): void { |
||
329 | Craft::debug( |
||
330 | 'Assets::EVENT_DEFINE_THUMB_URL', |
||
331 | __METHOD__ |
||
332 | ); |
||
333 | // Return the URL to the asset URL or null to let Craft handle it |
||
334 | $event->url = ImageOptimize::$plugin->optimize->handleGetAssetThumbUrlEvent( |
||
335 | $event |
||
336 | ); |
||
337 | } |
||
338 | ); |
||
339 | |||
340 | // Handler: ImageTransformer::EVENT_TRANSFORM_IMAGE |
||
341 | Event::on( |
||
342 | ImageTransformer::class, |
||
343 | ImageTransformer::EVENT_TRANSFORM_IMAGE, |
||
344 | static function(ImageTransformerOperationEvent $event): void { |
||
345 | Craft::debug( |
||
346 | 'ImageTransformer::EVENT_TRANSFORM_IMAGE', |
||
347 | __METHOD__ |
||
348 | ); |
||
349 | // Return the path to the optimized image to _createTransformForAsset() |
||
350 | $tempPath = ImageOptimize::$plugin->optimize->handleGenerateTransformEvent( |
||
351 | $event |
||
352 | ); |
||
353 | if ($tempPath) { |
||
354 | // Remove the old Craft generated transform that's still sitting in the temp directory. |
||
355 | @unlink($event->tempPath); |
||
356 | $event->tempPath = $tempPath; |
||
357 | } |
||
358 | } |
||
359 | ); |
||
360 | |||
361 | // Handler: ImageTransformer::EVENT_DELETE_TRANSFORMED_IMAGE |
||
362 | Event::on( |
||
363 | ImageTransformer::class, |
||
364 | ImageTransformer::EVENT_DELETE_TRANSFORMED_IMAGE, |
||
365 | static function(ImageTransformerOperationEvent $event): void { |
||
366 | Craft::debug( |
||
367 | 'ImageTransformer::EVENT_DELETE_TRANSFORMED_IMAGE', |
||
368 | __METHOD__ |
||
369 | ); |
||
370 | // Clean up any stray variant files |
||
371 | ImageOptimize::$plugin->optimize->handleAfterDeleteTransformsEvent( |
||
372 | $event |
||
373 | ); |
||
374 | } |
||
375 | ); |
||
376 | |||
377 | // Handler: Assets::EVENT_BEFORE_REPLACE_ASSET |
||
378 | Event::on( |
||
379 | Assets::class, |
||
380 | Assets::EVENT_BEFORE_REPLACE_ASSET, |
||
381 | static function(ReplaceAssetEvent $event) { |
||
382 | Craft::debug( |
||
383 | 'Assets::EVENT_BEFORE_REPLACE_ASSET', |
||
384 | __METHOD__ |
||
385 | ); |
||
386 | $element = $event->asset; |
||
387 | // Purge the URL |
||
388 | $purgeUrl = ImageOptimize::$plugin->transformMethod->getPurgeUrl($element); |
||
389 | if ($purgeUrl) { |
||
390 | ImageOptimize::$plugin->transformMethod->purgeUrl($purgeUrl); |
||
391 | } |
||
392 | } |
||
393 | ); |
||
394 | |||
395 | // Handler: Assets::EVENT_AFTER_REPLACE_ASSET |
||
396 | Event::on( |
||
397 | Assets::class, |
||
398 | Assets::EVENT_AFTER_REPLACE_ASSET, |
||
399 | static function(ReplaceAssetEvent $event) { |
||
400 | Craft::debug( |
||
401 | 'Assets::EVENT_AFTER_REPLACE_ASSET', |
||
402 | __METHOD__ |
||
403 | ); |
||
404 | $element = $event->asset; |
||
405 | if ($element->id !== null) { |
||
406 | ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id, true); |
||
407 | } |
||
408 | } |
||
409 | ); |
||
410 | } |
||
411 | |||
412 | /** |
||
413 | * Install our Element event handlers |
||
414 | */ |
||
415 | protected function installElementEventHandlers(): void |
||
416 | { |
||
417 | // Handler: Elements::EVENT_BEFORE_SAVE_ELEMENT |
||
418 | Event::on( |
||
419 | Assets::class, |
||
420 | Elements::EVENT_BEFORE_SAVE_ELEMENT, |
||
421 | static function(ElementEvent $event) { |
||
422 | Craft::debug( |
||
423 | 'Elements::EVENT_BEFORE_SAVE_ELEMENT', |
||
424 | __METHOD__ |
||
425 | ); |
||
426 | /** @var Asset $asset */ |
||
427 | $asset = $event->element; |
||
428 | if (!$event->isNew) { |
||
429 | // Purge the URL |
||
430 | $purgeUrl = ImageOptimize::$plugin->transformMethod->getPurgeUrl($asset); |
||
431 | if ($purgeUrl) { |
||
432 | ImageOptimize::$plugin->transformMethod->purgeUrl($purgeUrl); |
||
433 | } |
||
434 | } |
||
435 | } |
||
436 | ); |
||
437 | |||
438 | // Handler: Elements::EVENT_BEFORE_DELETE_ELEMENT |
||
439 | Event::on( |
||
440 | Asset::class, |
||
441 | Elements::EVENT_BEFORE_DELETE_ELEMENT, |
||
442 | static function(ElementEvent $event) { |
||
443 | Craft::debug( |
||
444 | 'Elements::EVENT_BEFORE_DELETE_ELEMENT', |
||
445 | __METHOD__ |
||
446 | ); |
||
447 | /** @var Asset $asset */ |
||
448 | $asset = $event->element; |
||
449 | // Purge the URL |
||
450 | $purgeUrl = ImageOptimize::$plugin->transformMethod->getPurgeUrl($asset); |
||
451 | if ($purgeUrl) { |
||
452 | ImageOptimize::$plugin->transformMethod->purgeUrl($purgeUrl); |
||
453 | } |
||
454 | } |
||
455 | ); |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Install our miscellaneous event handlers |
||
460 | */ |
||
461 | protected function installMiscEventHandlers(): void |
||
462 | { |
||
463 | // Handler: Fields::EVENT_AFTER_SAVE_FIELD |
||
464 | Event::on( |
||
465 | Fields::class, |
||
466 | Fields::EVENT_AFTER_SAVE_FIELD, |
||
467 | function(FieldEvent $event) { |
||
468 | Craft::debug( |
||
469 | 'Fields::EVENT_AFTER_SAVE_FIELD', |
||
470 | __METHOD__ |
||
471 | ); |
||
472 | /** @var Settings $settings */ |
||
473 | $settings = $this->getSettings(); |
||
474 | if (!$event->isNew && $settings->automaticallyResaveImageVariants) { |
||
475 | $this->checkForOptimizedImagesField($event); |
||
476 | } |
||
477 | } |
||
478 | ); |
||
479 | |||
480 | // Handler: Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS |
||
481 | Event::on( |
||
482 | Plugins::class, |
||
483 | Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS, |
||
484 | function(PluginEvent $event) { |
||
485 | if ($event->plugin === $this) { |
||
486 | Craft::debug( |
||
487 | 'Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS', |
||
488 | __METHOD__ |
||
489 | ); |
||
490 | /** @var ?Settings $settings */ |
||
491 | $settings = $this->getSettings(); |
||
492 | if (($settings !== null) && $settings->automaticallyResaveImageVariants) { |
||
493 | // After they have changed the settings, resave all the assets |
||
494 | ImageOptimize::$plugin->optimizedImages->resaveAllVolumesAssets(); |
||
495 | } |
||
496 | } |
||
497 | } |
||
498 | ); |
||
499 | |||
500 | // Handler: Volumes::EVENT_AFTER_SAVE_VOLUME |
||
501 | Event::on( |
||
502 | Volumes::class, |
||
503 | Volumes::EVENT_AFTER_SAVE_VOLUME, |
||
504 | function(VolumeEvent $event) { |
||
505 | Craft::debug( |
||
506 | 'Volumes::EVENT_AFTER_SAVE_VOLUME', |
||
507 | __METHOD__ |
||
508 | ); |
||
509 | /** @var ?Settings $settings */ |
||
510 | $settings = $this->getSettings(); |
||
511 | // Only worry about this volume if it's not new |
||
512 | if (($settings !== null) && !$event->isNew && $settings->automaticallyResaveImageVariants) { |
||
513 | $volume = $event->volume; |
||
514 | ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume); |
||
515 | } |
||
516 | } |
||
517 | ); |
||
518 | |||
519 | // Handler: Plugins::EVENT_AFTER_INSTALL_PLUGIN |
||
520 | Event::on( |
||
521 | Plugins::class, |
||
522 | Plugins::EVENT_AFTER_INSTALL_PLUGIN, |
||
523 | function(PluginEvent $event) { |
||
524 | if ($event->plugin === $this) { |
||
525 | $request = Craft::$app->getRequest(); |
||
526 | if ($request->isCpRequest) { |
||
527 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('image-optimize/welcome'))->send(); |
||
528 | } |
||
529 | } |
||
530 | } |
||
531 | ); |
||
532 | } |
||
533 | |||
534 | /** |
||
535 | * Install site event listeners for site requests only |
||
536 | */ |
||
537 | protected function installSiteEventListeners(): void |
||
538 | { |
||
539 | // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES |
||
540 | Event::on( |
||
541 | UrlManager::class, |
||
542 | UrlManager::EVENT_REGISTER_SITE_URL_RULES, |
||
543 | function(RegisterUrlRulesEvent $event) { |
||
544 | Craft::debug( |
||
545 | 'UrlManager::EVENT_REGISTER_SITE_URL_RULES', |
||
546 | __METHOD__ |
||
547 | ); |
||
548 | // Register our Control Panel routes |
||
549 | $event->rules = array_merge( |
||
550 | $event->rules, |
||
551 | $this->customFrontendRoutes() |
||
552 | ); |
||
553 | } |
||
554 | ); |
||
555 | } |
||
556 | |||
557 | /** |
||
558 | * Install site event listeners for cp requests only |
||
559 | */ |
||
560 | protected function installCpEventListeners(): void |
||
561 | { |
||
562 | // Handler: Plugins::EVENT_AFTER_LOAD_PLUGINS |
||
563 | Event::on( |
||
564 | Plugins::class, |
||
565 | Plugins::EVENT_AFTER_LOAD_PLUGINS, |
||
566 | static function() { |
||
567 | // Install these only after all other plugins have loaded |
||
568 | Event::on( |
||
569 | View::class, |
||
570 | View::EVENT_REGISTER_CP_TEMPLATE_ROOTS, |
||
571 | static function(RegisterTemplateRootsEvent $e) { |
||
572 | // Register the root directodies |
||
573 | $allImageTransformTypes = ImageOptimize::$plugin->optimize->getAllImageTransformTypes(); |
||
574 | /** @var ImageTransformInterface $imageTransformType */ |
||
575 | foreach ($allImageTransformTypes as $imageTransformType) { |
||
576 | [$id, $baseDir] = $imageTransformType::getTemplatesRoot(); |
||
577 | if (is_dir($baseDir)) { |
||
578 | $e->roots[$id] = $baseDir; |
||
579 | } |
||
580 | } |
||
581 | } |
||
582 | ); |
||
583 | } |
||
584 | ); |
||
585 | } |
||
586 | |||
587 | /** |
||
588 | * Return the custom frontend routes |
||
589 | * |
||
590 | * @return array |
||
591 | */ |
||
592 | protected function customFrontendRoutes(): array |
||
593 | { |
||
594 | return [ |
||
595 | ]; |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * If the Field being saved is an OptimizedImages field, re-save the |
||
600 | * responsive image variants automatically |
||
601 | * |
||
602 | * @param FieldEvent $event |
||
603 | */ |
||
604 | protected function checkForOptimizedImagesField(FieldEvent $event): void |
||
605 | { |
||
606 | $thisField = $event->field; |
||
607 | if ($thisField instanceof OptimizedImages) { |
||
608 | $volumes = Craft::$app->getVolumes()->getAllVolumes(); |
||
609 | foreach ($volumes as $volume) { |
||
610 | $needToReSave = false; |
||
611 | /** @var ?FieldLayout $fieldLayout */ |
||
612 | $fieldLayout = $volume->getFieldLayout(); |
||
613 | // Loop through the fields in the layout to see if it contains our field |
||
614 | if ($fieldLayout) { |
||
615 | $fields = $fieldLayout->getCustomFields(); |
||
616 | foreach ($fields as $field) { |
||
617 | /** @var Field $field */ |
||
618 | if ($thisField->handle === $field->handle) { |
||
619 | $needToReSave = true; |
||
620 | } |
||
621 | } |
||
622 | if ($needToReSave) { |
||
623 | self::$plugin->optimizedImages->resaveVolumeAssets($volume, $thisField->id); |
||
624 | } |
||
625 | } |
||
626 | } |
||
627 | } |
||
628 | } |
||
629 | } |
||
630 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.