| Total Complexity | 82 | 
| Total Lines | 1018 | 
| Duplicated Lines | 0 % | 
| Changes | 2 | ||
| Bugs | 1 | Features | 0 | 
Complex classes like Seomatic often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Seomatic, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 90 | class Seomatic extends Plugin  | 
            ||
| 91 | { | 
            ||
| 92 | // Traits  | 
            ||
| 93 | // =========================================================================  | 
            ||
| 94 | |||
| 95 | use ServicesTrait;  | 
            ||
| 96 | |||
| 97 | // Constants  | 
            ||
| 98 | // =========================================================================  | 
            ||
| 99 | |||
| 100 | public const SEOMATIC_HANDLE = 'Seomatic';  | 
            ||
| 101 | |||
| 102 | public const DEVMODE_CACHE_DURATION = 30;  | 
            ||
| 103 | |||
| 104 | public const SEOMATIC_PREVIEW_AUTHORIZATION_KEY = 'seomaticPreviewAuthorizationKey';  | 
            ||
| 105 | |||
| 106 | protected const FRONTEND_SEO_FILE_LINK = 'seomatic/seo-file-link/<url:[^\/]+>/<robots:[^\/]+>/<canonical:[^\/]+>/<inline:\d+>/<fileName:[-\w\.*]+>';  | 
            ||
| 107 | |||
| 108 | protected const FRONTEND_PREVIEW_PATH = 'seomatic/preview-social-media';  | 
            ||
| 109 | |||
| 110 | public const SEOMATIC_EXPRESSION_FIELD_TYPE = 'SeomaticExpressionField';  | 
            ||
| 111 | public const SEOMATIC_TRACKING_FIELD_TYPE = 'SeomaticTrackingField';  | 
            ||
| 112 | |||
| 113 | // Static Properties  | 
            ||
| 114 | // =========================================================================  | 
            ||
| 115 | |||
| 116 | /**  | 
            ||
| 117 | * @var ?Seomatic  | 
            ||
| 118 | */  | 
            ||
| 119 | public static ?Seomatic $plugin;  | 
            ||
| 120 | |||
| 121 | /**  | 
            ||
| 122 | * @var null|SeomaticVariable  | 
            ||
| 123 | */  | 
            ||
| 124 | public static ?SeomaticVariable $seomaticVariable = null;  | 
            ||
| 125 | |||
| 126 | /**  | 
            ||
| 127 | * @var null|Settings  | 
            ||
| 128 | */  | 
            ||
| 129 | public static ?Settings $settings = null;  | 
            ||
| 130 | |||
| 131 | /**  | 
            ||
| 132 | * @var null|ElementInterface  | 
            ||
| 133 | */  | 
            ||
| 134 | public static ?ElementInterface $matchedElement = null;  | 
            ||
| 135 | |||
| 136 | /**  | 
            ||
| 137 | * @var bool  | 
            ||
| 138 | */  | 
            ||
| 139 | public static bool $devMode;  | 
            ||
| 140 | |||
| 141 | /**  | 
            ||
| 142 | * @var null|View  | 
            ||
| 143 | */  | 
            ||
| 144 | public static ?View $view = null;  | 
            ||
| 145 | |||
| 146 | /**  | 
            ||
| 147 | * @var null|View  | 
            ||
| 148 | */  | 
            ||
| 149 | public static ?View $sandboxView = null;  | 
            ||
| 150 | |||
| 151 | /**  | 
            ||
| 152 | * @var string  | 
            ||
| 153 | */  | 
            ||
| 154 | public static string $language = '';  | 
            ||
| 155 | |||
| 156 | /**  | 
            ||
| 157 | * @var string  | 
            ||
| 158 | */  | 
            ||
| 159 | public static string $environment = '';  | 
            ||
| 160 | |||
| 161 | /**  | 
            ||
| 162 | * @var int  | 
            ||
| 163 | */  | 
            ||
| 164 | public static int $cacheDuration = 0;  | 
            ||
| 165 | |||
| 166 | /**  | 
            ||
| 167 | * @var bool  | 
            ||
| 168 | */  | 
            ||
| 169 | public static bool $previewingMetaContainers = false;  | 
            ||
| 170 | |||
| 171 | /**  | 
            ||
| 172 | * @var bool  | 
            ||
| 173 | */  | 
            ||
| 174 | public static bool $loadingMetaContainers = false;  | 
            ||
| 175 | |||
| 176 | /**  | 
            ||
| 177 | * @var bool  | 
            ||
| 178 | */  | 
            ||
| 179 | public static bool $savingSettings = false;  | 
            ||
| 180 | |||
| 181 | /**  | 
            ||
| 182 | * @var bool  | 
            ||
| 183 | */  | 
            ||
| 184 | public static bool $headlessRequest = false;  | 
            ||
| 185 | |||
| 186 | // Public Properties  | 
            ||
| 187 | // =========================================================================  | 
            ||
| 188 | |||
| 189 | /**  | 
            ||
| 190 | * @var string  | 
            ||
| 191 | */  | 
            ||
| 192 | public string $schemaVersion = '3.0.13';  | 
            ||
| 193 | |||
| 194 | /**  | 
            ||
| 195 | * @var bool  | 
            ||
| 196 | */  | 
            ||
| 197 | public bool $hasCpSection = true;  | 
            ||
| 198 | |||
| 199 | /**  | 
            ||
| 200 | * @var bool  | 
            ||
| 201 | */  | 
            ||
| 202 | public bool $hasCpSettings = true;  | 
            ||
| 203 | |||
| 204 | /**  | 
            ||
| 205 | * Set the matched element  | 
            ||
| 206 | *  | 
            ||
| 207 | * @param $element null|ElementInterface  | 
            ||
| 208 | */  | 
            ||
| 209 | public static function setMatchedElement(?ElementInterface $element): void  | 
            ||
| 210 |     { | 
            ||
| 211 | self::$matchedElement = $element;  | 
            ||
| 212 | /** @var $element Element */  | 
            ||
| 213 |         if ($element) { | 
            ||
| 214 | self::$language = MetaValueHelper::getSiteLanguage($element->siteId);  | 
            ||
| 215 |         } else { | 
            ||
| 216 | self::$language = MetaValueHelper::getSiteLanguage(null);  | 
            ||
| 217 | }  | 
            ||
| 218 | MetaValueHelper::cache();  | 
            ||
| 219 | }  | 
            ||
| 220 | |||
| 221 | // Public Methods  | 
            ||
| 222 | // =========================================================================  | 
            ||
| 223 | |||
| 224 | /**  | 
            ||
| 225 | * @inheritdoc  | 
            ||
| 226 | */  | 
            ||
| 227 | public function init(): void  | 
            ||
| 228 |     { | 
            ||
| 229 | parent::init();  | 
            ||
| 230 | self::$plugin = $this;  | 
            ||
| 231 | // Handle any console commands  | 
            ||
| 232 | $request = Craft::$app->getRequest();  | 
            ||
| 233 |         if ($request->getIsConsoleRequest()) { | 
            ||
| 234 | $this->controllerNamespace = 'nystudio107\seomatic\console\controllers';  | 
            ||
| 235 | }  | 
            ||
| 236 | // Initialize properties  | 
            ||
| 237 | /** @var Settings $settings */  | 
            ||
| 238 | $settings = self::$plugin->getSettings();  | 
            ||
| 239 | self::$settings = $settings;  | 
            ||
| 240 | self::$devMode = Craft::$app->getConfig()->getGeneral()->devMode;  | 
            ||
| 241 | self::$view = Craft::$app->getView();  | 
            ||
| 242 | // Use a Twig sandbox for SEOmatic rendering  | 
            ||
| 243 |         $securityPolicy = SecurityPolicy::createFromFile('seomatic-sandbox', '@nystudio107/seomatic'); | 
            ||
| 244 | self::$sandboxView = new SandboxView(['securityPolicy' => $securityPolicy]);  | 
            ||
| 245 | self::$cacheDuration = self::$devMode  | 
            ||
| 246 | ? self::DEVMODE_CACHE_DURATION  | 
            ||
| 247 | : self::$settings->metaCacheDuration ?? 0;  | 
            ||
| 248 | self::$environment = EnvironmentHelper::determineEnvironment();  | 
            ||
| 249 | MetaValueHelper::cache();  | 
            ||
| 250 | $this->name = self::$settings->pluginName;  | 
            ||
| 251 | // Install our event listeners  | 
            ||
| 252 | $this->installEventListeners();  | 
            ||
| 253 | // We're loaded  | 
            ||
| 254 | Craft::info(  | 
            ||
| 255 | Craft::t(  | 
            ||
| 256 | 'seomatic',  | 
            ||
| 257 |                 '{name} plugin loaded', | 
            ||
| 258 | ['name' => $this->name]  | 
            ||
| 259 | ),  | 
            ||
| 260 | __METHOD__  | 
            ||
| 261 | );  | 
            ||
| 262 | }  | 
            ||
| 263 | |||
| 264 | /**  | 
            ||
| 265 | * @inheritdoc  | 
            ||
| 266 | */  | 
            ||
| 267 | public function getSettings(): ?Model  | 
            ||
| 289 | }  | 
            ||
| 290 | |||
| 291 | /**  | 
            ||
| 292 | * Determine whether our table schema exists or not; this is needed because  | 
            ||
| 293 | * migrations such as the install migration and base_install migration may  | 
            ||
| 294 | * not have been run by the time our init() method has been called  | 
            ||
| 295 | *  | 
            ||
| 296 | * @return bool  | 
            ||
| 297 | */  | 
            ||
| 298 | public function migrationsAndSchemaReady(): bool  | 
            ||
| 299 |     { | 
            ||
| 300 | $pluginsService = Craft::$app->getPlugins();  | 
            ||
| 301 |         if ($pluginsService->isPluginUpdatePending(self::$plugin)) { | 
            ||
| 302 | return false;  | 
            ||
| 303 | }  | 
            ||
| 304 |         if (Craft::$app->db->schema->getTableSchema('{{%seomatic_metabundles}}') === null) { | 
            ||
| 305 | return false;  | 
            ||
| 306 | }  | 
            ||
| 307 | |||
| 308 | return true;  | 
            ||
| 309 | }  | 
            ||
| 310 | |||
| 311 | /**  | 
            ||
| 312 | * Clear all the caches!  | 
            ||
| 313 | */  | 
            ||
| 314 | public function clearAllCaches(): void  | 
            ||
| 331 | }  | 
            ||
| 332 | }  | 
            ||
| 333 | |||
| 334 | // Protected Methods  | 
            ||
| 335 | // =========================================================================  | 
            ||
| 336 | |||
| 337 | /**  | 
            ||
| 338 | * @inheritdoc  | 
            ||
| 339 | */  | 
            ||
| 340 | public function getSettingsResponse(): mixed  | 
            ||
| 341 |     { | 
            ||
| 342 | // Just redirect to the plugin settings page  | 
            ||
| 343 |         return Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('seomatic/plugin')); | 
            ||
| 344 | }  | 
            ||
| 345 | |||
| 346 | /**  | 
            ||
| 347 | * @inheritdoc  | 
            ||
| 348 | */  | 
            ||
| 349 | public function getCpNavItem(): ?array  | 
            ||
| 417 | ]);  | 
            ||
| 418 | }  | 
            ||
| 419 | |||
| 420 | /**  | 
            ||
| 421 | * Install our event listeners.  | 
            ||
| 422 | */  | 
            ||
| 423 | protected function installEventListeners(): void  | 
            ||
| 424 |     { | 
            ||
| 425 | // Install our event listeners only if our table schema exists  | 
            ||
| 426 |         if ($this->migrationsAndSchemaReady()) { | 
            ||
| 427 | // Add in our Twig extensions  | 
            ||
| 428 | $seomaticTwigExtension = new SeomaticTwigExtension();  | 
            ||
| 429 | self::$view->registerTwigExtension($seomaticTwigExtension);  | 
            ||
| 430 | self::$sandboxView->registerTwigExtension($seomaticTwigExtension);  | 
            ||
| 431 | // Register the additional TwigExtension classes  | 
            ||
| 432 |             foreach (Seomatic::$settings->twigExtensionClasses as $className) { | 
            ||
| 433 |                 if (class_exists($className)) { | 
            ||
| 434 | self::$sandboxView->registerTwigExtension(new $className());  | 
            ||
| 435 | }  | 
            ||
| 436 | }  | 
            ||
| 437 | $request = Craft::$app->getRequest();  | 
            ||
| 438 | // Add in our event listeners that are needed for every request  | 
            ||
| 439 | $this->installGlobalEventListeners();  | 
            ||
| 440 | // Install only for non-console site requests  | 
            ||
| 441 |             if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { | 
            ||
| 442 | $this->installSiteEventListeners();  | 
            ||
| 443 | }  | 
            ||
| 444 | // Install only for non-console Control Panel requests  | 
            ||
| 445 |             if ($request->getIsCpRequest() && !$request->getIsConsoleRequest()) { | 
            ||
| 446 | $this->installCpEventListeners();  | 
            ||
| 447 | }  | 
            ||
| 448 | }  | 
            ||
| 449 | // Handler: EVENT_AFTER_INSTALL_PLUGIN  | 
            ||
| 450 | Event::on(  | 
            ||
| 451 | Plugins::class,  | 
            ||
| 452 | Plugins::EVENT_AFTER_INSTALL_PLUGIN,  | 
            ||
| 453 |             function(PluginEvent $event) { | 
            ||
| 454 |                 if ($event->plugin === $this) { | 
            ||
| 455 | // Invalidate our caches after we've been installed  | 
            ||
| 456 | $this->clearAllCaches();  | 
            ||
| 457 | // Send them to our welcome screen  | 
            ||
| 458 | $request = Craft::$app->getRequest();  | 
            ||
| 459 |                     if ($request->isCpRequest) { | 
            ||
| 460 | Craft::$app->getResponse()->redirect(UrlHelper::cpUrl(  | 
            ||
| 461 | 'seomatic/dashboard',  | 
            ||
| 462 | [  | 
            ||
| 463 | 'showWelcome' => true,  | 
            ||
| 464 | ]  | 
            ||
| 465 | ))->send();  | 
            ||
| 466 | }  | 
            ||
| 467 | }  | 
            ||
| 468 | }  | 
            ||
| 469 | );  | 
            ||
| 470 | // Handler: ClearCaches::EVENT_REGISTER_CACHE_OPTIONS  | 
            ||
| 471 | Event::on(  | 
            ||
| 472 | ClearCaches::class,  | 
            ||
| 473 | ClearCaches::EVENT_REGISTER_CACHE_OPTIONS,  | 
            ||
| 474 |             function(RegisterCacheOptionsEvent $event) { | 
            ||
| 475 | Craft::debug(  | 
            ||
| 476 | 'ClearCaches::EVENT_REGISTER_CACHE_OPTIONS',  | 
            ||
| 477 | __METHOD__  | 
            ||
| 478 | );  | 
            ||
| 479 | // Register our Cache Options  | 
            ||
| 480 | $event->options = array_merge(  | 
            ||
| 481 | $event->options,  | 
            ||
| 482 | $this->customAdminCpCacheOptions()  | 
            ||
| 483 | );  | 
            ||
| 484 | }  | 
            ||
| 485 | );  | 
            ||
| 486 | // Handler: EVENT_BEFORE_SAVE_PLUGIN_SETTINGS  | 
            ||
| 487 | Event::on(  | 
            ||
| 488 | Plugins::class,  | 
            ||
| 489 | Plugins::EVENT_BEFORE_SAVE_PLUGIN_SETTINGS,  | 
            ||
| 490 |             function(PluginEvent $event) { | 
            ||
| 491 |                 if ($event->plugin === $this && !Craft::$app->getDb()->getSupportsMb4()) { | 
            ||
| 492 | // For all the emojis  | 
            ||
| 493 | $settingsModel = $this->getSettings();  | 
            ||
| 494 | self::$savingSettings = true;  | 
            ||
| 495 |                     if ($settingsModel !== null) { | 
            ||
| 496 | $attributes = $settingsModel->attributes();  | 
            ||
| 497 |                         if ($attributes !== null) { | 
            ||
| 498 |                             foreach ($attributes as $attribute) { | 
            ||
| 499 |                                 if (is_string($settingsModel->$attribute)) { | 
            ||
| 500 | $settingsModel->$attribute =  | 
            ||
| 501 | StringHelper::encodeMb4($settingsModel->$attribute);  | 
            ||
| 502 | }  | 
            ||
| 503 | }  | 
            ||
| 504 | }  | 
            ||
| 505 | }  | 
            ||
| 506 | }  | 
            ||
| 507 | }  | 
            ||
| 508 | );  | 
            ||
| 509 | }  | 
            ||
| 510 | |||
| 511 | /**  | 
            ||
| 512 | * Install global event listeners for all request types  | 
            ||
| 513 | */  | 
            ||
| 514 | protected function installGlobalEventListeners(): void  | 
            ||
| 666 | }  | 
            ||
| 667 | );  | 
            ||
| 668 | }  | 
            ||
| 669 | |||
| 670 | /**  | 
            ||
| 671 | * Register our GraphQL handlers  | 
            ||
| 672 | *  | 
            ||
| 673 | * @return void  | 
            ||
| 674 | */  | 
            ||
| 675 | protected function installGqlHandlers(): void  | 
            ||
| 676 |     { | 
            ||
| 677 | // Handler: Gql::EVENT_REGISTER_GQL_TYPES  | 
            ||
| 678 | Event::on(  | 
            ||
| 679 | Gql::class,  | 
            ||
| 680 | Gql::EVENT_REGISTER_GQL_TYPES,  | 
            ||
| 681 |             static function(RegisterGqlTypesEvent $event) { | 
            ||
| 682 | Craft::debug(  | 
            ||
| 683 | 'Gql::EVENT_REGISTER_GQL_TYPES',  | 
            ||
| 684 | __METHOD__  | 
            ||
| 685 | );  | 
            ||
| 686 | $event->types[] = SeomaticInterface::class;  | 
            ||
| 687 | $event->types[] = SeomaticEnvironmentType::class;  | 
            ||
| 688 | }  | 
            ||
| 689 | );  | 
            ||
| 690 | // Handler: Gql::EVENT_REGISTER_GQL_QUERIES  | 
            ||
| 691 | Event::on(  | 
            ||
| 692 | Gql::class,  | 
            ||
| 693 | Gql::EVENT_REGISTER_GQL_QUERIES,  | 
            ||
| 694 |             static function(RegisterGqlQueriesEvent $event) { | 
            ||
| 695 | Craft::debug(  | 
            ||
| 696 | 'Gql::EVENT_REGISTER_GQL_QUERIES',  | 
            ||
| 697 | __METHOD__  | 
            ||
| 698 | );  | 
            ||
| 699 | $queries = SeomaticQuery::getQueries();  | 
            ||
| 700 |                 foreach ($queries as $key => $value) { | 
            ||
| 701 | $event->queries[$key] = $value;  | 
            ||
| 702 | }  | 
            ||
| 703 | }  | 
            ||
| 704 | );  | 
            ||
| 705 | // Handler: Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS  | 
            ||
| 706 | Event::on(  | 
            ||
| 707 | Gql::class,  | 
            ||
| 708 | Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS,  | 
            ||
| 709 |             static function(RegisterGqlSchemaComponentsEvent $event) { | 
            ||
| 710 | Craft::debug(  | 
            ||
| 711 | 'Gql::EVENT_REGISTER_GQL_SCHEMA_COMPONENTS',  | 
            ||
| 712 | __METHOD__  | 
            ||
| 713 | );  | 
            ||
| 714 |                 $label = Craft::t('seomatic', 'Seomatic'); | 
            ||
| 715 |                 $event->queries[$label]['seomatic.all:read'] = ['label' => Craft::t('seomatic', 'Query Seomatic data')]; | 
            ||
| 716 | }  | 
            ||
| 717 | );  | 
            ||
| 718 | // Add support for querying for SEOmatic metadata inside of element queries  | 
            ||
| 719 | // Handler: TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS  | 
            ||
| 720 | $knownInterfaceNames = self::$plugin->seoElements->getAllSeoElementGqlInterfaceNames();  | 
            ||
| 721 | Event::on(  | 
            ||
| 722 | TypeManager::class,  | 
            ||
| 723 | TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS,  | 
            ||
| 724 |             static function(DefineGqlTypeFieldsEvent $event) use ($knownInterfaceNames) { | 
            ||
| 725 |                 if (in_array($event->typeName, $knownInterfaceNames, true)) { | 
            ||
| 726 | Craft::debug(  | 
            ||
| 727 | 'TypeManager::EVENT_DEFINE_GQL_TYPE_FIELDS',  | 
            ||
| 728 | __METHOD__  | 
            ||
| 729 | );  | 
            ||
| 730 | |||
| 731 |                     if (GqlHelper::canQuerySeo()) { | 
            ||
| 732 | // Make Seomatic tags available to all entries.  | 
            ||
| 733 | $event->fields['seomatic'] = [  | 
            ||
| 734 | 'name' => 'seomatic',  | 
            ||
| 735 | 'type' => SeomaticInterface::getType(),  | 
            ||
| 736 | 'args' => SeomaticArguments::getArguments(),  | 
            ||
| 737 | 'resolve' => SeomaticResolver::class . '::resolve',  | 
            ||
| 738 |                             'description' => Craft::t('seomatic', 'This query is used to query for SEOmatic meta data.'), | 
            ||
| 739 | ];  | 
            ||
| 740 | }  | 
            ||
| 741 | }  | 
            ||
| 742 | });  | 
            ||
| 743 | }  | 
            ||
| 744 | |||
| 745 | /**  | 
            ||
| 746 | * Handle site requests. We do it only after we receive the event  | 
            ||
| 747 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run  | 
            ||
| 748 | * before our event listeners kick in  | 
            ||
| 749 | */  | 
            ||
| 750 | protected function handleSiteRequest(): void  | 
            ||
| 751 |     { | 
            ||
| 752 | // Handler: View::EVENT_END_PAGE  | 
            ||
| 753 | Event::on(  | 
            ||
| 754 | View::class,  | 
            ||
| 755 | BaseView::EVENT_END_PAGE,  | 
            ||
| 756 |             static function() { | 
            ||
| 757 | Craft::debug(  | 
            ||
| 758 | 'View::EVENT_END_PAGE',  | 
            ||
| 759 | __METHOD__  | 
            ||
| 760 | );  | 
            ||
| 761 | // The page is done rendering, include our meta containers  | 
            ||
| 762 |                 if (self::$settings->renderEnabled && self::$seomaticVariable) { | 
            ||
| 763 | self::$plugin->metaContainers->includeMetaContainers();  | 
            ||
| 764 | }  | 
            ||
| 765 | }  | 
            ||
| 766 | );  | 
            ||
| 767 | }  | 
            ||
| 768 | |||
| 769 | /**  | 
            ||
| 770 | * Handle Control Panel requests. We do it only after we receive the event  | 
            ||
| 771 | * EVENT_AFTER_LOAD_PLUGINS so that any pending db migrations can be run  | 
            ||
| 772 | * before our event listeners kick in  | 
            ||
| 773 | */  | 
            ||
| 774 | protected function handleAdminCpRequest(): void  | 
            ||
| 775 |     { | 
            ||
| 776 | // Don't cache Control Panel requests  | 
            ||
| 777 | self::$cacheDuration = 1;  | 
            ||
| 778 | // Prefix the Control Panel title  | 
            ||
| 779 |         self::$view->hook('cp.layouts.base', function(&$context) { | 
            ||
| 780 |             if (self::$devMode) { | 
            ||
| 781 | $context['docTitle'] = self::$settings->devModeCpTitlePrefix . $context['docTitle'];  | 
            ||
| 782 |             } else { | 
            ||
| 783 | $context['docTitle'] = self::$settings->cpTitlePrefix . $context['docTitle'];  | 
            ||
| 784 | }  | 
            ||
| 785 | });  | 
            ||
| 786 | }  | 
            ||
| 787 | |||
| 788 | /**  | 
            ||
| 789 | * Install site event listeners for site requests only  | 
            ||
| 790 | */  | 
            ||
| 791 | protected function installSiteEventListeners(): void  | 
            ||
| 792 |     { | 
            ||
| 793 | // Load the sitemap containers  | 
            ||
| 794 | self::$plugin->sitemaps->loadSitemapContainers();  | 
            ||
| 795 | // Load the frontend template containers  | 
            ||
| 796 | self::$plugin->frontendTemplates->loadFrontendTemplateContainers();  | 
            ||
| 797 | // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES  | 
            ||
| 798 | Event::on(  | 
            ||
| 799 | UrlManager::class,  | 
            ||
| 800 | UrlManager::EVENT_REGISTER_SITE_URL_RULES,  | 
            ||
| 801 |             function(RegisterUrlRulesEvent $event) { | 
            ||
| 802 | Craft::debug(  | 
            ||
| 803 | 'UrlManager::EVENT_REGISTER_SITE_URL_RULES',  | 
            ||
| 804 | __METHOD__  | 
            ||
| 805 | );  | 
            ||
| 806 | // FileController  | 
            ||
| 807 | $route = self::$plugin->handle . '/file/seo-file-link';  | 
            ||
| 808 | $event->rules[self::FRONTEND_SEO_FILE_LINK] = ['route' => $route];  | 
            ||
| 809 | // PreviewController  | 
            ||
| 810 | $route = self::$plugin->handle . '/preview/social-media';  | 
            ||
| 811 | $event->rules[self::FRONTEND_PREVIEW_PATH] = ['route' => $route];  | 
            ||
| 812 | // Register our Control Panel routes  | 
            ||
| 813 | $event->rules = array_merge(  | 
            ||
| 814 | $event->rules,  | 
            ||
| 815 | $this->customFrontendRoutes()  | 
            ||
| 816 | );  | 
            ||
| 817 | }  | 
            ||
| 818 | );  | 
            ||
| 819 | }  | 
            ||
| 820 | |||
| 821 | /**  | 
            ||
| 822 | * Return the custom frontend routes  | 
            ||
| 823 | *  | 
            ||
| 824 | * @return array  | 
            ||
| 825 | */  | 
            ||
| 826 | protected function customFrontendRoutes(): array  | 
            ||
| 827 |     { | 
            ||
| 828 | return [  | 
            ||
| 829 | ];  | 
            ||
| 830 | }  | 
            ||
| 831 | |||
| 832 | /**  | 
            ||
| 833 | * Install site event listeners for Control Panel requests only  | 
            ||
| 834 | */  | 
            ||
| 835 | protected function installCpEventListeners(): void  | 
            ||
| 836 |     { | 
            ||
| 837 | // Load the frontend template containers  | 
            ||
| 838 | self::$plugin->frontendTemplates->loadFrontendTemplateContainers();  | 
            ||
| 839 | // Handler: UrlManager::EVENT_REGISTER_CP_URL_RULES  | 
            ||
| 840 | Event::on(  | 
            ||
| 841 | UrlManager::class,  | 
            ||
| 842 | UrlManager::EVENT_REGISTER_CP_URL_RULES,  | 
            ||
| 843 |             function(RegisterUrlRulesEvent $event) { | 
            ||
| 844 | Craft::debug(  | 
            ||
| 845 | 'UrlManager::EVENT_REGISTER_CP_URL_RULES',  | 
            ||
| 846 | __METHOD__  | 
            ||
| 847 | );  | 
            ||
| 848 | // Register our Control Panel routes  | 
            ||
| 849 | $event->rules = array_merge(  | 
            ||
| 850 | $event->rules,  | 
            ||
| 851 | $this->customAdminCpRoutes()  | 
            ||
| 852 | );  | 
            ||
| 853 | }  | 
            ||
| 854 | );  | 
            ||
| 855 | // Handler: UserPermissions::EVENT_REGISTER_PERMISSIONS  | 
            ||
| 856 | Event::on(  | 
            ||
| 857 | UserPermissions::class,  | 
            ||
| 858 | UserPermissions::EVENT_REGISTER_PERMISSIONS,  | 
            ||
| 859 |             function(RegisterUserPermissionsEvent $event) { | 
            ||
| 860 | Craft::debug(  | 
            ||
| 861 | 'UserPermissions::EVENT_REGISTER_PERMISSIONS',  | 
            ||
| 862 | __METHOD__  | 
            ||
| 863 | );  | 
            ||
| 864 | // Register our custom permissions  | 
            ||
| 865 | $event->permissions[] = [  | 
            ||
| 866 |                     'heading' => Craft::t('seomatic', 'SEOmatic'), | 
            ||
| 867 | 'permissions' => $this->customAdminCpPermissions(),  | 
            ||
| 868 | ];  | 
            ||
| 869 | }  | 
            ||
| 870 | );  | 
            ||
| 871 | // Handler: AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES  | 
            ||
| 872 | Event::on(AutocompleteService::class, AutocompleteService::EVENT_REGISTER_CODEEDITOR_AUTOCOMPLETES,  | 
            ||
| 873 |             function(RegisterCodeEditorAutocompletesEvent $event) { | 
            ||
| 874 |                 if ($event->fieldType === self::SEOMATIC_EXPRESSION_FIELD_TYPE) { | 
            ||
| 875 | $event->types[] = EnvironmentVariableAutocomplete::class;  | 
            ||
| 876 | }  | 
            ||
| 877 |                 if ($event->fieldType === self::SEOMATIC_TRACKING_FIELD_TYPE) { | 
            ||
| 878 | $event->types[] = TrackingVarsAutocomplete::class;  | 
            ||
| 879 | }  | 
            ||
| 880 | }  | 
            ||
| 881 | );  | 
            ||
| 882 | // Handler: TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES  | 
            ||
| 883 | Event::on(TwigTemplateValidator::class,  | 
            ||
| 884 | TwigTemplateValidator::EVENT_REGISTER_TWIG_VALIDATOR_VARIABLES,  | 
            ||
| 885 |             function(RegisterTwigValidatorVariablesEvent $event) { | 
            ||
| 886 |                 if (Seomatic::$seomaticVariable === null) { | 
            ||
| 887 | Seomatic::$seomaticVariable = new SeomaticVariable();  | 
            ||
| 888 | Seomatic::$plugin->metaContainers->loadGlobalMetaContainers();  | 
            ||
| 889 | Seomatic::$seomaticVariable->init();  | 
            ||
| 890 | }  | 
            ||
| 891 | $event->variables['seomatic'] = Seomatic::$seomaticVariable;  | 
            ||
| 892 | }  | 
            ||
| 893 | );  | 
            ||
| 894 | }  | 
            ||
| 895 | |||
| 896 | /**  | 
            ||
| 897 | * Return the custom Control Panel routes  | 
            ||
| 898 | *  | 
            ||
| 899 | * @return array  | 
            ||
| 900 | */  | 
            ||
| 901 | protected function customAdminCpRoutes(): array  | 
            ||
| 902 |     { | 
            ||
| 903 | return [  | 
            ||
| 904 | 'seomatic' =>  | 
            ||
| 905 | '',  | 
            ||
| 906 | 'seomatic/dashboard' =>  | 
            ||
| 907 | 'seomatic/settings/dashboard',  | 
            ||
| 908 |             'seomatic/dashboard/<siteHandle:{handle}>' => | 
            ||
| 909 | 'seomatic/settings/dashboard',  | 
            ||
| 910 | |||
| 911 | 'seomatic/global' => [  | 
            ||
| 912 | 'route' => 'seomatic/settings/global',  | 
            ||
| 913 | 'defaults' => ['subSection' => 'general'],  | 
            ||
| 914 | ],  | 
            ||
| 915 |             'seomatic/global/<subSection:{handle}>' => | 
            ||
| 916 | 'seomatic/settings/global',  | 
            ||
| 917 |             'seomatic/global/<subSection:{handle}>/<siteHandle:{handle}>' => | 
            ||
| 918 | 'seomatic/settings/global',  | 
            ||
| 919 | |||
| 920 | 'seomatic/content' =>  | 
            ||
| 921 | 'seomatic/settings/content',  | 
            ||
| 922 |             'seomatic/content/<siteHandle:{handle}>' => | 
            ||
| 923 | 'seomatic/settings/content',  | 
            ||
| 924 | |||
| 925 |             'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>' => | 
            ||
| 926 | 'seomatic/settings/edit-content',  | 
            ||
| 927 |             'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{handle}>/<siteHandle:{handle}>' => | 
            ||
| 928 | 'seomatic/settings/edit-content',  | 
            ||
| 929 | |||
| 930 | // Seemingly duplicate route needed to handle Solspace Calendar, which allows characters like -'s  | 
            ||
| 931 | // in their handles  | 
            ||
| 932 |             'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>' => | 
            ||
| 933 | 'seomatic/settings/edit-content',  | 
            ||
| 934 |             'seomatic/edit-content/<subSection:{handle}>/<sourceBundleType:{handle}>/<sourceHandle:{slug}>/<siteHandle:{handle}>' => | 
            ||
| 935 | 'seomatic/settings/edit-content',  | 
            ||
| 936 | |||
| 937 | 'seomatic/site' => [  | 
            ||
| 938 | 'route' => 'seomatic/settings/site',  | 
            ||
| 939 | 'defaults' => ['subSection' => 'identity'],  | 
            ||
| 940 | ],  | 
            ||
| 941 |             'seomatic/site/<subSection:{handle}>' => | 
            ||
| 942 | 'seomatic/settings/site',  | 
            ||
| 943 |             'seomatic/site/<subSection:{handle}>/<siteHandle:{handle}>' => | 
            ||
| 944 | 'seomatic/settings/site',  | 
            ||
| 945 | |||
| 946 | 'seomatic/tracking' => [  | 
            ||
| 947 | 'route' => 'seomatic/settings/tracking',  | 
            ||
| 948 | 'defaults' => ['subSection' => 'googleAnalytics'],  | 
            ||
| 949 | ],  | 
            ||
| 950 |             'seomatic/tracking/<subSection:{handle}>' => | 
            ||
| 951 | 'seomatic/settings/tracking',  | 
            ||
| 952 |             'seomatic/tracking/<subSection:{handle}>/<siteHandle:{handle}>' => | 
            ||
| 953 | 'seomatic/settings/tracking',  | 
            ||
| 954 | |||
| 955 | 'seomatic/plugin' =>  | 
            ||
| 956 | 'seomatic/settings/plugin',  | 
            ||
| 957 | ];  | 
            ||
| 958 | }  | 
            ||
| 959 | |||
| 960 | /**  | 
            ||
| 961 | * Returns the custom Control Panel user permissions.  | 
            ||
| 962 | *  | 
            ||
| 963 | * @return array  | 
            ||
| 964 | * @noinspection PhpArrayShapeAttributeCanBeAddedInspection  | 
            ||
| 965 | */  | 
            ||
| 966 | protected function customAdminCpPermissions(): array  | 
            ||
| 967 |     { | 
            ||
| 968 | // The script meta containers for the global meta bundle  | 
            ||
| 969 |         try { | 
            ||
| 970 | $currentSiteId = Craft::$app->getSites()->getCurrentSite()->id ?? 1;  | 
            ||
| 971 |         } catch (SiteNotFoundException $e) { | 
            ||
| 972 | $currentSiteId = 1;  | 
            ||
| 973 | Craft::error($e->getMessage(), __METHOD__);  | 
            ||
| 974 | }  | 
            ||
| 975 | // Dynamic permissions for the scripts  | 
            ||
| 976 | $metaBundle = self::$plugin->metaBundles->getGlobalMetaBundle($currentSiteId);  | 
            ||
| 977 | $scriptsPerms = [];  | 
            ||
| 978 |         if ($metaBundle !== null) { | 
            ||
| 979 | $scripts = self::$plugin->metaBundles->getContainerDataFromBundle(  | 
            ||
| 980 | $metaBundle,  | 
            ||
| 981 | MetaScriptContainer::CONTAINER_TYPE  | 
            ||
| 982 | );  | 
            ||
| 983 |             foreach ($scripts as $scriptHandle => $scriptData) { | 
            ||
| 984 | $scriptsPerms["seomatic:tracking-scripts:$scriptHandle"] = [  | 
            ||
| 985 |                     'label' => Craft::t('seomatic', $scriptData->name), | 
            ||
| 986 | ];  | 
            ||
| 987 | }  | 
            ||
| 988 | }  | 
            ||
| 989 | |||
| 990 | return [  | 
            ||
| 991 | 'seomatic:dashboard' => [  | 
            ||
| 992 |                 'label' => Craft::t('seomatic', 'Dashboard'), | 
            ||
| 993 | ],  | 
            ||
| 994 | 'seomatic:global-meta' => [  | 
            ||
| 995 |                 'label' => Craft::t('seomatic', 'Edit Global Meta'), | 
            ||
| 996 | 'nested' => [  | 
            ||
| 997 | 'seomatic:global-meta:general' => [  | 
            ||
| 998 |                         'label' => Craft::t('seomatic', 'General'), | 
            ||
| 999 | ],  | 
            ||
| 1000 | 'seomatic:global-meta:twitter' => [  | 
            ||
| 1001 |                         'label' => Craft::t('seomatic', 'Twitter'), | 
            ||
| 1002 | ],  | 
            ||
| 1003 | 'seomatic:global-meta:facebook' => [  | 
            ||
| 1004 |                         'label' => Craft::t('seomatic', 'Facebook'), | 
            ||
| 1005 | ],  | 
            ||
| 1006 | 'seomatic:global-meta:robots' => [  | 
            ||
| 1007 |                         'label' => Craft::t('seomatic', 'Robots'), | 
            ||
| 1008 | ],  | 
            ||
| 1009 | 'seomatic:global-meta:humans' => [  | 
            ||
| 1010 |                         'label' => Craft::t('seomatic', 'Humans'), | 
            ||
| 1011 | ],  | 
            ||
| 1012 | 'seomatic:global-meta:ads' => [  | 
            ||
| 1013 |                         'label' => Craft::t('seomatic', 'Ads'), | 
            ||
| 1014 | ],  | 
            ||
| 1015 | 'seomatic:global-meta:security' => [  | 
            ||
| 1016 |                         'label' => Craft::t('seomatic', 'Security'), | 
            ||
| 1017 | ],  | 
            ||
| 1018 | ],  | 
            ||
| 1019 | ],  | 
            ||
| 1020 | 'seomatic:content-meta' => [  | 
            ||
| 1021 |                 'label' => Craft::t('seomatic', 'Edit Content SEO'), | 
            ||
| 1022 | 'nested' => [  | 
            ||
| 1023 | 'seomatic:content-meta:general' => [  | 
            ||
| 1024 |                         'label' => Craft::t('seomatic', 'General'), | 
            ||
| 1025 | ],  | 
            ||
| 1026 | 'seomatic:content-meta:twitter' => [  | 
            ||
| 1027 |                         'label' => Craft::t('seomatic', 'Twitter'), | 
            ||
| 1028 | ],  | 
            ||
| 1029 | 'seomatic:content-meta:facebook' => [  | 
            ||
| 1030 |                         'label' => Craft::t('seomatic', 'Facebook'), | 
            ||
| 1031 | ],  | 
            ||
| 1032 | 'seomatic:content-meta:sitemap' => [  | 
            ||
| 1033 |                         'label' => Craft::t('seomatic', 'Sitemap'), | 
            ||
| 1034 | ],  | 
            ||
| 1035 | ],  | 
            ||
| 1036 | ],  | 
            ||
| 1037 | 'seomatic:site-settings' => [  | 
            ||
| 1038 |                 'label' => Craft::t('seomatic', 'Edit Site Settings'), | 
            ||
| 1039 | 'nested' => [  | 
            ||
| 1040 | 'seomatic:site-settings:identity' => [  | 
            ||
| 1041 |                         'label' => Craft::t('seomatic', 'Identity'), | 
            ||
| 1042 | ],  | 
            ||
| 1043 | 'seomatic:site-settings:creator' => [  | 
            ||
| 1044 |                         'label' => Craft::t('seomatic', 'Creator'), | 
            ||
| 1045 | ],  | 
            ||
| 1046 | 'seomatic:site-settings:social' => [  | 
            ||
| 1047 |                         'label' => Craft::t('seomatic', 'Social Media'), | 
            ||
| 1048 | ],  | 
            ||
| 1049 | 'seomatic:site-settings:sitemap' => [  | 
            ||
| 1050 |                         'label' => Craft::t('seomatic', 'Sitemap'), | 
            ||
| 1051 | ],  | 
            ||
| 1052 | 'seomatic:site-settings:miscellaneous' => [  | 
            ||
| 1053 |                         'label' => Craft::t('seomatic', 'Miscellaneous'), | 
            ||
| 1054 | ],  | 
            ||
| 1055 | ],  | 
            ||
| 1056 | ],  | 
            ||
| 1057 | 'seomatic:tracking-scripts' => [  | 
            ||
| 1058 |                 'label' => Craft::t('seomatic', 'Edit Tracking Scripts'), | 
            ||
| 1059 | 'nested' => $scriptsPerms,  | 
            ||
| 1060 | ],  | 
            ||
| 1061 | 'seomatic:plugin-settings' => [  | 
            ||
| 1062 |                 'label' => Craft::t('seomatic', 'Edit Plugin Settings'), | 
            ||
| 1063 | ],  | 
            ||
| 1064 | ];  | 
            ||
| 1065 | }  | 
            ||
| 1066 | |||
| 1067 | /**  | 
            ||
| 1068 | * Returns the custom Control Panel cache options.  | 
            ||
| 1069 | *  | 
            ||
| 1070 | * @return array  | 
            ||
| 1071 | */  | 
            ||
| 1072 | protected function customAdminCpCacheOptions(): array  | 
            ||
| 1073 |     { | 
            ||
| 1074 | return [  | 
            ||
| 1075 | // Frontend template caches  | 
            ||
| 1076 | [  | 
            ||
| 1077 | 'key' => 'seomatic-frontendtemplate-caches',  | 
            ||
| 1078 |                 'label' => Craft::t('seomatic', 'SEOmatic frontend template caches'), | 
            ||
| 1079 | 'action' => [self::$plugin->frontendTemplates, 'invalidateCaches'],  | 
            ||
| 1080 | ],  | 
            ||
| 1081 | // Meta bundle caches  | 
            ||
| 1082 | [  | 
            ||
| 1083 | 'key' => 'seomatic-metabundle-caches',  | 
            ||
| 1084 |                 'label' => Craft::t('seomatic', 'SEOmatic metadata caches'), | 
            ||
| 1085 | 'action' => [self::$plugin->metaContainers, 'invalidateCaches'],  | 
            ||
| 1086 | ],  | 
            ||
| 1087 | // Sitemap caches  | 
            ||
| 1088 | [  | 
            ||
| 1089 | 'key' => 'seomatic-sitemap-caches',  | 
            ||
| 1090 |                 'label' => Craft::t('seomatic', 'SEOmatic sitemap caches'), | 
            ||
| 1091 | 'action' => [self::$plugin->sitemaps, 'invalidateCaches'],  | 
            ||
| 1092 | ],  | 
            ||
| 1093 | // Schema caches  | 
            ||
| 1094 | [  | 
            ||
| 1095 | 'key' => 'seomatic-schema-caches',  | 
            ||
| 1096 |                 'label' => Craft::t('seomatic', 'SEOmatic schema caches'), | 
            ||
| 1097 | 'action' => [SchemaHelper::class, 'invalidateCaches'],  | 
            ||
| 1098 | ],  | 
            ||
| 1099 | ];  | 
            ||
| 1100 | }  | 
            ||
| 1101 | |||
| 1102 | /**  | 
            ||
| 1103 | * @inheritdoc  | 
            ||
| 1104 | */  | 
            ||
| 1105 | protected function createSettingsModel(): ?Model  | 
            ||
| 1108 | }  | 
            ||
| 1109 | }  | 
            ||
| 1110 | 
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths