diff --git a/composer.json b/composer.json index 9caa0cd..f1ce8c4 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "php": "^7.4|^8.1", "composer/installers": "^1|^2", "jazzman/autoload-interface": "^0.3.1", + "jazzman/singleton-trait": "^1.1", "jazzman/wp-app-config": "^2.3", "jazzman/wp-db-pdo": "^0.1.4" }, @@ -50,14 +51,12 @@ }, "description": "", "require-dev": { - "jazzman/php-cs-fixer-rules": "^0.1.2", - "phpmd/phpmd": "@stable", - "phpstan/extension-installer": "^1.1", - "rector/rector": "^0.14.5", + "jazzman/php-cs-fixer-rules": "^0.1.3", + "phpstan/extension-installer": "^1.2", + "rector/rector": "^0.14.8", "roave/security-advisories": "@dev", - "roots/wordpress": "^6.0", - "szepeviktor/phpstan-wordpress": "^1.1", - "vimeo/psalm": "^4.27" + "roots/wordpress": "^6.1", + "szepeviktor/phpstan-wordpress": "^1.1" }, "scripts": { "rm-cache" : [ diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7813d0a..89b50f2 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,10 @@ parameters: ignoreErrors: + - + message: "#^Method JazzMan\\\\WpNavMenuCache\\\\MenuItems\\:\\:setMenuClasses\\(\\) should return array\\ but returns mixed\\.$#" + count: 1 + path: src/MenuItems.php + - message: "#^Parameter \\#2 \\$fetch_argument of method PDOStatement\\:\\:fetchAll\\(\\) expects \\(callable\\(\\)\\: mixed\\)\\|int\\|string, array given\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 47da514..629a406 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,2 +1,2 @@ - + diff --git a/rector.php b/rector.php index 0f98f01..274c7ac 100644 --- a/rector.php +++ b/rector.php @@ -33,8 +33,8 @@ $config->cacheDirectory(__DIR__.'/cache/rector'); $config->paths([ + __DIR__, __DIR__.'/src', - __DIR__.'/wp-performance.php', ]); $config->skip( @@ -42,6 +42,7 @@ // or fnmatch __DIR__.'/vendor', __DIR__.'/cache', + __DIR__.'/rector.php', CallableThisArrayToAnonymousFunctionRector::class, ClassConstantToSelfClassRector::class, RemoveExtraParametersRector::class, diff --git a/src/MenuItemClasses.php b/src/MenuItemClasses.php index c5691b6..8cae3c5 100644 --- a/src/MenuItemClasses.php +++ b/src/MenuItemClasses.php @@ -2,23 +2,14 @@ namespace JazzMan\WpNavMenuCache; +use JazzMan\Traits\SingletonTrait; use JazzMan\WpNavMenuCacheStub\MenuItem; -use WP_Error; -use WP_Post; -use WP_Post_Type; -use WP_Query; -use WP_Rewrite; -use WP_Taxonomy; -use WP_Term; -use WP_User; -class MenuItemClasses { - private WP_Rewrite $wpRewrite; - - private WP_Query $wpQuery; +final class MenuItemClasses { + use SingletonTrait; /** - * @var null|WP_Post|WP_Post_Type|WP_Term|WP_User + * @var null|\WP_Post|\WP_Post_Type|\WP_Term|\WP_User */ private $queriedObject; @@ -29,6 +20,10 @@ class MenuItemClasses { */ private int $frontId; + private bool $isFrontPage; + + private int $privacyPageId; + /** * Home page ID. */ @@ -49,22 +44,25 @@ class MenuItemClasses { */ private array $possibleTaxonomyAncestors = []; - private bool $queriedPostTypeHierarchical; + private bool $isPostTypeHierarchical; public function __construct() { - global $wp_query, $wp_rewrite; + global $wp_query; - $this->wpRewrite = $wp_rewrite; - $this->wpQuery = $wp_query; - $this->queriedObjectId = $this->wpQuery->get_queried_object_id(); + $this->queriedObjectId = $wp_query->get_queried_object_id(); - $this->queriedObject = $this->wpQuery->get_queried_object(); + $this->queriedObject = $wp_query->get_queried_object(); - $this->queriedPostTypeHierarchical = !empty($this->queriedObject->post_type) && is_post_type_hierarchical((string) $this->queriedObject->post_type); + $this->isPostTypeHierarchical = !empty($this->queriedObject->post_type) && is_post_type_hierarchical((string) $this->queriedObject->post_type); $this->frontId = self::getIntOption('page_on_front'); + + $this->isFrontPage = $wp_query->is_front_page(); + $this->homeId = self::getIntOption('page_for_posts'); + $this->privacyPageId = self::getIntOption('wp_page_for_privacy_policy'); + $this->frontPageUrl = home_url(); $this->setTaxonomyAncestors(); @@ -73,16 +71,18 @@ public function __construct() { /** * @param MenuItem[]|\stdClass[] $menuItems */ - public function setMenuItemClassesByContext(array &$menuItems): void { + public static function setMenuItemClassesByContext(array &$menuItems): void { + global $wp_rewrite, $wp_query; + + $_this = self::getInstance(); + $activeObject = ''; $ancestorItemIds = []; $activeParentItemIds = []; $activeParentObjectIds = []; - $privacyPolicyPageId = self::getIntOption('wp_page_for_privacy_policy'); - - foreach ($menuItems as $key => $menuItem) { - $menuItems[$key]->current = false; + foreach ($menuItems as $menuItem) { + $menuItem->current = false; $menuItemType = (string) $menuItem->type; $menuItemObject = (string) $menuItem->object; @@ -94,25 +94,25 @@ public function setMenuItemClassesByContext(array &$menuItems): void { $classes[] = sprintf('menu-item-object-%s', $menuItemObject); if ('post_type' === $menuItemType) { - if ($this->frontId === (int) $menuItem->object_id) { + if ($_this->frontId === (int) $menuItem->object_id) { $classes[] = 'menu-item-home'; } - if ($privacyPolicyPageId === (int) $menuItem->object_id) { + if ($_this->privacyPageId === (int) $menuItem->object_id) { $classes[] = 'menu-item-privacy-policy'; } } - if ($this->wpQuery->is_singular && 'taxonomy' === $menuItemType && \in_array((int) $menuItem->object_id, $this->possibleObjectParents, true)) { + if ($wp_query->is_singular && 'taxonomy' === $menuItemType && \in_array((int) $menuItem->object_id, $_this->possibleObjectParents, true)) { $activeParentObjectIds[] = (int) $menuItem->object_id; $activeParentItemIds[] = (int) $menuItem->db_id; - if (!empty($this->queriedObject->post_type)) { - $activeObject = (string) $this->queriedObject->post_type; + if (!empty($_this->queriedObject->post_type)) { + $activeObject = (string) $_this->queriedObject->post_type; } - } elseif ($this->isCurrentMenuItemt($menuItem)) { + } elseif ($_this->isCurrentMenuItem($menuItem)) { $classes[] = 'current-menu-item'; - $menuItems[$key]->current = true; + $menuItem->current = true; if (!\in_array((int) $menuItem->db_id, $ancestorItemIds, true)) { $ancestorItemIds[] = (int) $menuItem->db_id; @@ -127,9 +127,9 @@ public function setMenuItemClassesByContext(array &$menuItems): void { $activeParentItemIds[] = (int) $menuItem->menu_item_parent; $activeParentObjectIds[] = (int) $menuItem->post_parent; $activeObject = $menuItemObject; - } elseif ('post_type_archive' === $menuItemType && $this->wpQuery->is_post_type_archive($menuItemObject)) { + } elseif ('post_type_archive' === $menuItemType && $wp_query->is_post_type_archive($menuItemObject)) { $classes[] = 'current-menu-item'; - $menuItems[$key]->current = true; + $menuItem->current = true; if (!\in_array((int) $menuItem->db_id, $ancestorItemIds, true)) { $ancestorItemIds[] = (int) $menuItem->db_id; @@ -147,7 +147,7 @@ public function setMenuItemClassesByContext(array &$menuItems): void { $itemUrl = set_url_scheme(untrailingslashit($rawItemUrl)); $indexlessCurrent = untrailingslashit( - (string) preg_replace('/'.preg_quote($this->wpRewrite->index, '/').'$/', '', $currentUrl) + (string) preg_replace('/'.preg_quote($wp_rewrite->index, '/').'$/', '', $currentUrl) ); $matches = [ @@ -161,14 +161,14 @@ public function setMenuItemClassesByContext(array &$menuItems): void { if ($rawItemUrl && \in_array($itemUrl, $matches, true)) { $classes[] = 'current-menu-item'; - $menuItems[$key]->current = true; + $menuItem->current = true; if (!\in_array((int) $menuItem->db_id, $ancestorItemIds, true)) { $ancestorItemIds[] = (int) $menuItem->db_id; } if (\in_array( - $this->frontPageUrl, + $_this->frontPageUrl, [ untrailingslashit($currentUrl), untrailingslashit($indexlessCurrent), @@ -181,43 +181,44 @@ public function setMenuItemClassesByContext(array &$menuItems): void { $activeParentItemIds[] = (int) $menuItem->menu_item_parent; $activeParentObjectIds[] = (int) $menuItem->post_parent; $activeObject = $menuItemObject; - } elseif ($itemUrl === $this->frontPageUrl && $this->wpQuery->is_front_page()) { + } elseif ($itemUrl === $_this->frontPageUrl && $_this->isFrontPage) { $classes[] = 'current-menu-item'; } - if (untrailingslashit($itemUrl) === $this->frontPageUrl) { + if (untrailingslashit($itemUrl) === $_this->frontPageUrl) { $classes[] = 'menu-item-home'; } } // Back-compat with wp_page_menu(): add "current_page_parent" to static home page link for any non-page query. - if ('post_type' === $menuItemType && empty($this->wpQuery->is_page) && $this->homeId === (int) $menuItem->object_id) { + if ('post_type' === $menuItemType && empty($wp_query->is_page) && $_this->homeId === (int) $menuItem->object_id) { $classes[] = 'current_page_parent'; } /** @var string[] $classes */ - $menuItems[$key]->classes = array_unique($classes); + $menuItem->classes = array_unique($classes); } + $ancestorItemIds = array_filter(array_unique($ancestorItemIds)); $activeParentItemIds = array_filter(array_unique($activeParentItemIds)); $activeParentObjectIds = array_filter(array_unique($activeParentObjectIds)); // Set parent's class. - foreach ($menuItems as $key => $parentItem) { + foreach ($menuItems as $parentItem) { /** @var string[] $classes */ $classes = (array) $parentItem->classes; - $menuItems[$key]->current_item_ancestor = false; - $menuItems[$key]->current_item_parent = false; + $parentItem->current_item_ancestor = false; + $parentItem->current_item_parent = false; - if ($this->isCurrentMenuItemtAncestor($parentItem)) { + if ($_this->isCurrentMenuItemAncestor($parentItem)) { $ancestorType = false; - if (!empty($this->queriedObject->taxonomy)) { - $ancestorType = (string) $this->queriedObject->taxonomy; + if (!empty($_this->queriedObject->taxonomy)) { + $ancestorType = (string) $_this->queriedObject->taxonomy; } - if (!empty($this->queriedObject->post_type)) { - $ancestorType = (string) $this->queriedObject->post_type; + if (!empty($_this->queriedObject->post_type)) { + $ancestorType = (string) $_this->queriedObject->post_type; } if (!empty($ancestorType)) { @@ -230,13 +231,13 @@ public function setMenuItemClassesByContext(array &$menuItems): void { if (\in_array((int) $parentItem->db_id, $ancestorItemIds, true)) { $classes[] = 'current-menu-ancestor'; - $menuItems[$key]->current_item_ancestor = true; + $parentItem->current_item_ancestor = true; } if (\in_array((int) $parentItem->db_id, $activeParentItemIds, true)) { $classes[] = 'current-menu-parent'; - $menuItems[$key]->current_item_parent = true; + $parentItem->current_item_parent = true; } if (\in_array((int) $parentItem->object_id, $activeParentObjectIds, true)) { @@ -254,13 +255,22 @@ public function setMenuItemClassesByContext(array &$menuItems): void { } } - $menuItems[$key]->classes = array_unique($classes); + $parentItem->classes = array_unique($classes); } } + private static function getIntOption(string $optionName): int { + /** @var false|string $option */ + $option = get_option($optionName); + + return empty($option) ? 0 : (int) $option; + } + private function setTaxonomyAncestors(): void { - if ($this->wpQuery->is_singular && !empty($this->queriedObject->post_type) && !$this->queriedPostTypeHierarchical) { - /** @var array $taxonomies */ + global $wp_query; + + if ($wp_query->is_singular && !empty($this->queriedObject->post_type) && !$this->isPostTypeHierarchical) { + /** @var array $taxonomies */ $taxonomies = get_object_taxonomies((string) $this->queriedObject->post_type, 'objects'); foreach ($taxonomies as $taxonomy => $taxonomyObject) { @@ -268,10 +278,10 @@ private function setTaxonomyAncestors(): void { /** @var array $termHierarchy */ $termHierarchy = _get_term_hierarchy($taxonomy); - /** @var int[]|WP_Error $terms */ + /** @var int[]|\WP_Error $terms */ $terms = wp_get_object_terms($this->queriedObjectId, $taxonomy, ['fields' => 'ids']); - if (!$terms instanceof WP_Error) { + if (!$terms instanceof \WP_Error) { $this->possibleObjectParents = array_merge($this->possibleObjectParents, $terms); /** @var array $termToAncestor */ @@ -299,7 +309,7 @@ private function setTaxonomyAncestors(): void { } } } - } elseif ($this->queriedObject instanceof WP_Term && is_taxonomy_hierarchical($this->queriedObject->taxonomy)) { + } elseif ($this->queriedObject instanceof \WP_Term && is_taxonomy_hierarchical($this->queriedObject->taxonomy)) { /** @var array $termHierarchy */ $termHierarchy = _get_term_hierarchy($this->queriedObject->taxonomy); @@ -333,18 +343,20 @@ private function setTaxonomyAncestors(): void { /** * @param MenuItem|\stdClass $menuItem */ - private function isCurrentMenuItemt($menuItem): bool { + private function isCurrentMenuItem($menuItem): bool { + global $wp_query; + $postTypeCondition = false; $taxonomyCondition = false; switch ((string) $menuItem->type) { case 'post_type': - $postTypeCondition = ($this->wpQuery->is_home && $this->homeId === (int) $menuItem->object_id) || $this->wpQuery->is_singular; + $postTypeCondition = ($wp_query->is_home && $this->homeId === (int) $menuItem->object_id) || $wp_query->is_singular; break; case 'taxonomy': - $isCategory = $this->wpQuery->is_category || $this->wpQuery->is_tag || $this->wpQuery->is_tax; + $isCategory = $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax; $taxonomyCondition = $isCategory && (!empty($this->queriedObject->taxonomy) && $this->queriedObject->taxonomy === (string) $menuItem->object); @@ -357,18 +369,18 @@ private function isCurrentMenuItemt($menuItem): bool { /** * @param MenuItem|\stdClass $parent */ - private function isCurrentMenuItemtAncestor($parent): bool { - if (!isset($parent->type)) { + private function isCurrentMenuItemAncestor($parent): bool { + if (empty($parent->type)) { return false; } switch ((string) $parent->type) { case 'post_type': - if (!$this->queriedPostTypeHierarchical) { + if (!$this->isPostTypeHierarchical) { return false; } - if ($this->queriedObject instanceof WP_Post) { + if ($this->queriedObject instanceof \WP_Post) { return \in_array((int) $parent->object_id, $this->queriedObject->ancestors, true) && $parent->object != $this->queriedObject->ID; } @@ -383,11 +395,4 @@ private function isCurrentMenuItemtAncestor($parent): bool { return false; } - - private static function getIntOption(string $optionName): int { - /** @var false|string $option */ - $option = get_option($optionName); - - return !empty($option) ? (int) $option : 0; - } } diff --git a/src/MenuItems.php b/src/MenuItems.php index 787605f..d938912 100644 --- a/src/MenuItems.php +++ b/src/MenuItems.php @@ -2,7 +2,6 @@ namespace JazzMan\WpNavMenuCache; -use Exception; use JazzMan\WpNavMenuCacheStub\MenuItem; use function Latitude\QueryBuilder\alias; @@ -11,18 +10,12 @@ use Latitude\QueryBuilder\Query; use Latitude\QueryBuilder\QueryFactory; -use PDO; -use stdClass; -use WP_Post; -use WP_Post_Type; -use WP_Taxonomy; -use WP_Term; - -class MenuItems { + +final class MenuItems { /** * @return MenuItem[]|\stdClass[] */ - public static function getItems(WP_Term $wpTerm): array { + public static function getItems(\WP_Term $wpTerm): array { $cacheKey = NavMenuCache::getMenuItemCacheKey($wpTerm); /** @var false|MenuItem[]|\stdClass[] $menuItems */ @@ -39,16 +32,16 @@ public static function getItems(WP_Term $wpTerm): array { $pdoStatement->execute($query->params()); /** @var MenuItem[]|\stdClass[] $menuItems */ - $menuItems = $pdoStatement->fetchAll(PDO::FETCH_OBJ); + $menuItems = $pdoStatement->fetchAll(\PDO::FETCH_OBJ); foreach ($menuItems as $key => $item) { $menuItems[$key] = self::setupNavMenuItem($item); } wp_cache_set($cacheKey, $menuItems, 'menu_items'); - } catch (Exception $exception) { + } catch (\Exception $exception) { /** @var MenuItem|\stdClass $item */ - $item = new stdClass(); + $item = new \stdClass(); $item->_invalid = true; $menuItems = []; @@ -58,15 +51,19 @@ public static function getItems(WP_Term $wpTerm): array { } } + if (!empty($menuItems) && !is_admin()) { + return array_filter($menuItems, '_is_valid_nav_menu_item'); + } + return $menuItems; } - private static function generateSql(WP_Term $wpTerm): Query { + private static function generateSql(\WP_Term $wpTerm): Query { global $wpdb; - $factory = new QueryFactory(); + $queryFactory = new QueryFactory(); - $query = $factory + $selectQuery = $queryFactory ->select( 'menu.*', alias('menu.post_content', 'description'), @@ -99,8 +96,8 @@ private static function generateSql(WP_Term $wpTerm): Query { ]; foreach ($menuMetaFields as $field => $metaKey) { - $query->addColumns(alias("{$field}.meta_value", $field)); - $query->leftJoin( + $selectQuery->addColumns(alias("{$field}.meta_value", $field)); + $selectQuery->leftJoin( alias($wpdb->postmeta, $field), on('menu.ID', "{$field}.post_id") ->and( @@ -109,7 +106,7 @@ private static function generateSql(WP_Term $wpTerm): Query { ); } - $query->addColumns( + $selectQuery->addColumns( 'term_tax.taxonomy', 'term_tax.term_id', alias('term.name', 'term_name'), @@ -119,24 +116,24 @@ private static function generateSql(WP_Term $wpTerm): Query { alias('original_post.post_status', 'original_post_status'), alias('original_post.post_title', 'original_post_title'), ); - $query->leftJoin( + $selectQuery->leftJoin( alias($wpdb->term_taxonomy, 'term_tax'), on('term_tax.term_id', 'object_id.meta_value') ->and(field('type.meta_value')->eq('taxonomy')) ); - $query->leftJoin( + $selectQuery->leftJoin( alias($wpdb->terms, 'term'), on('term_tax.term_id', 'term.term_id') ); - $query->leftJoin( + $selectQuery->leftJoin( alias($wpdb->posts, 'original_post'), on('original_post.ID', 'object_id.meta_value') ->and(field('type.meta_value')->eq('post_type')) ); - return $query->compile(); + return $selectQuery->compile(); } /** @@ -150,7 +147,7 @@ private static function setupNavMenuItem($menuItem) { * * @var MenuItem|\stdClass $menuItem */ - $menuItem = new WP_Post($menuItem); + $menuItem = new \WP_Post($menuItem); self::setMenuItemLabels($menuItem); @@ -189,7 +186,7 @@ private static function setMenuItemLabels(&$menuItem): void { case 'taxonomy': $typeLabel = sprintf('Taxonomy%s', self::getMenuTaxonomyLabels($menuItem)); - $menuTitle = !empty($currentMenuTitle) ? $currentMenuTitle : (string) $menuItem->term_name; + $menuTitle = empty($currentMenuTitle) ? (string) $menuItem->term_name : $currentMenuTitle; break; @@ -198,8 +195,8 @@ private static function setMenuItemLabels(&$menuItem): void { $postTypeObject = get_post_type_object((string) $menuItem->object); - if ($postTypeObject instanceof WP_Post_Type) { - $menuTitle = !empty($currentMenuTitle) ? $currentMenuTitle : (string) $postTypeObject->labels->archives; + if ($postTypeObject instanceof \WP_Post_Type) { + $menuTitle = empty($currentMenuTitle) ? (string) $postTypeObject->labels->archives : $currentMenuTitle; $postTypeLabel .= sprintf(': %s', $postTypeObject->label); } @@ -213,7 +210,7 @@ private static function setMenuItemLabels(&$menuItem): void { $postTypeObject = get_post_type_object((string) $menuItem->object); - if ($postTypeObject instanceof WP_Post_Type) { + if ($postTypeObject instanceof \WP_Post_Type) { $typeLabel = (string) $postTypeObject->labels->singular_name; } @@ -230,7 +227,7 @@ private static function setMenuItemLabels(&$menuItem): void { } if (!empty($menuItem->original_post_status) && 'publish' !== $menuItem->original_post_status && \function_exists('get_post_states')) { - /** @var WP_Post $originalPost */ + /** @var \WP_Post $originalPost */ $originalPost = get_post((int) $menuItem->object_id); $typeLabel = wp_strip_all_tags(implode(', ', get_post_states($originalPost))); @@ -257,12 +254,12 @@ private static function getMenuTaxonomyLabels($menuItem): string { $taxonomy = get_taxonomy((string) $menuItem->object); - if ($taxonomy instanceof WP_Taxonomy) { + if ($taxonomy instanceof \WP_Taxonomy) { $labels = get_taxonomy_labels($taxonomy); $label = sprintf( ': %s', - !empty($labels->singular_name) ? (string) $labels->singular_name : $taxonomy->label + empty($labels->singular_name) ? $taxonomy->label : (string) $labels->singular_name ); } @@ -288,14 +285,14 @@ private static function setMenuItemIds(&$menuItem): void { private static function getMenuItemDescription($menuItem): string { switch ($menuItem->type) { case 'taxonomy': - $description = !empty($menuItem->term_description) ? (string) $menuItem->term_description : ''; + $description = empty($menuItem->term_description) ? '' : (string) $menuItem->term_description; break; case 'post_type_archive': $object = get_post_type_object((string) $menuItem->object); - $description = $object instanceof WP_Post_Type ? $object->description : ''; + $description = $object instanceof \WP_Post_Type ? $object->description : ''; break; @@ -306,7 +303,7 @@ private static function getMenuItemDescription($menuItem): string { break; } - $description = !empty($description) ? wp_trim_words($description, 200) : ''; + $description = empty($description) ? '' : wp_trim_words($description, 200); return (string) apply_filters('nav_menu_description', $description); } @@ -337,7 +334,7 @@ private static function getMenuItemUrl($menuItem): string { break; } - return !empty($url) ? $url : ''; + return empty($url) ? '' : $url; } /** @@ -354,7 +351,7 @@ private static function checkIfInvalid($menuItem): bool { case 'taxonomy': $term = get_term((int) $menuItem->object_id, (string) $menuItem->object); - return !$term instanceof WP_Term; + return !$term instanceof \WP_Term; default: return false; @@ -367,14 +364,14 @@ private static function checkIfInvalid($menuItem): bool { * @return string[] */ private static function setMenuClasses($menuItem): array { - /** @var string[] $classes */ - $classes = []; + if (empty($menuItem->classes)) { + return []; + } - if (!empty($menuItem->classes) && \is_string($menuItem->classes)) { - /** @var string[] $classes */ - $classes = maybe_unserialize($menuItem->classes); + if (!\is_string($menuItem->classes)) { + return []; } - return $classes; + return maybe_unserialize($menuItem->classes); } } diff --git a/src/NavMenuCache.php b/src/NavMenuCache.php index 07c0f50..73b670f 100644 --- a/src/NavMenuCache.php +++ b/src/NavMenuCache.php @@ -5,41 +5,22 @@ use JazzMan\AutoloadInterface\AutoloadInterface; use JazzMan\WpNavMenuCacheStub\MenuItem; use JazzMan\WpNavMenuCacheStub\NavMenuArgs; -use WP_Error; -use WP_Term; -class NavMenuCache implements AutoloadInterface { +final class NavMenuCache implements AutoloadInterface { /** * @var string */ public const CACHE_GROUP = 'wp-nav-menu-cache'; public function load(): void { - /** - * We clear the cache when the post is updated. - */ - add_action('delete_post', [__CLASS__, 'resetMenuCacheByMenuId']); - add_action('wp_update_nav_menu_item', [__CLASS__, 'resetMenuCacheByMenuId']); - add_action('wp_add_nav_menu_item', [__CLASS__, 'resetMenuCacheByMenuId']); - - /** - * We clear the cache when the term is updated. - */ - add_action('delete_term', [__CLASS__, 'resetMenuCacheByTermId']); - add_action('wp_create_nav_menu', [__CLASS__, 'resetMenuCacheByTermId']); - add_action('saved_nav_menu', [__CLASS__, 'resetMenuCacheByTermId']); - - add_filter('wp_nav_menu_args', [__CLASS__, 'setMenuFallbackParams']); - add_filter('pre_wp_nav_menu', [$this, 'buildWpNavMenu'], 10, 2); - - add_action('saved_term', [__CLASS__, 'termsCache'], 10, 3); - } + self::clearMenuCache(); - public static function termsCache(int $termId, int $termTaxId, string $taxonomy): void { - wp_cache_delete(sprintf('taxonomy_ancestors_%d_%s', $termId, $taxonomy), self::CACHE_GROUP); - wp_cache_delete(sprintf('term_all_children_%d', $termId), self::CACHE_GROUP); + add_filter('wp_nav_menu_args', static function (array $args): array { + $args['fallback_cb'] = '__return_empty_string'; - app_term_get_all_children($termId); + return $args; + }); + add_filter('pre_wp_nav_menu', [self::class, 'buildWpNavMenu'], 10, 2); } /** @@ -47,7 +28,7 @@ public static function termsCache(int $termId, int $termTaxId, string $taxonomy) * * @return false|mixed|string */ - public function buildWpNavMenu(?string $output, $args) { + public static function buildWpNavMenu(?string $output, $args) { $menu = wp_get_nav_menu_object((string) $args->menu); if (false === $menu) { @@ -56,10 +37,6 @@ public function buildWpNavMenu(?string $output, $args) { $menuItems = MenuItems::getItems($menu); - if (!empty($menuItems) && !is_admin()) { - $menuItems = array_filter($menuItems, '_is_valid_nav_menu_item'); - } - /** @var MenuItem[]|\stdClass[] $menuItems */ $menuItems = apply_filters('wp_get_nav_menu_items', $menuItems, $menu, $args); @@ -67,9 +44,7 @@ public function buildWpNavMenu(?string $output, $args) { return \call_user_func($args->fallback_cb, (array) $args); } - $menuCssClassses = new MenuItemClasses(); - - $menuCssClassses->setMenuItemClassesByContext($menuItems); + MenuItemClasses::setMenuItemClassesByContext($menuItems); /** @var array $sortedMenuItems */ $sortedMenuItems = []; @@ -107,9 +82,9 @@ public function buildWpNavMenu(?string $output, $args) { $items = walk_nav_menu_tree($sortedMenuItems, (int) $args->depth, $args); unset($sortedMenuItems); - $wrapId = $this->getMenuWrapId($menu, $args); + $wrapId = self::getMenuWrapId($menu, $args); - $wrapClass = !empty($args->menu_class) ? (string) $args->menu_class : ''; + $wrapClass = empty($args->menu_class) ? '' : (string) $args->menu_class; $items = (string) apply_filters('wp_nav_menu_items', $items, $args); @@ -127,52 +102,58 @@ public function buildWpNavMenu(?string $output, $args) { ); unset($items); - $navMenu = $this->wrapToContainer($args, $menu, $navMenu); + $navMenu = self::wrapToContainer($args, $menu, $navMenu); return (string) apply_filters('wp_nav_menu', $navMenu, $args); } + public static function getMenuItemCacheKey(\WP_Term $wpTerm): string { + return sprintf('%s_%s', $wpTerm->taxonomy, $wpTerm->slug); + } + /** - * @param array $args - * - * @psalm-return array - * - * @return array + * We clear the cache when the term or post is updated. */ - public static function setMenuFallbackParams(array $args): array { - $args['fallback_cb'] = '__return_empty_string'; + private static function clearMenuCache(): void { + add_action('saved_term', static function (int $termId, int $termTaxId, string $taxonomy): void { + wp_cache_delete(sprintf('taxonomy_ancestors_%d_%s', $termId, $taxonomy), self::CACHE_GROUP); + wp_cache_delete(sprintf('term_all_children_%d', $termId), self::CACHE_GROUP); + app_term_get_all_children($termId); + }, 10, 3); - return $args; - } + $postActions = ['delete_post', 'wp_update_nav_menu_item', 'wp_add_nav_menu_item']; + + $termActions = ['delete_term', 'wp_create_nav_menu', 'saved_nav_menu']; - public static function resetMenuCacheByTermId(int $termId): void { - /** @var WP_Error|WP_Term $term */ - $term = get_term($termId, 'nav_menu'); + foreach ($termActions as $termAction) { + add_action($termAction, static function (int $termId): void { + /** @var \WP_Error|\WP_Term $term */ + $term = get_term($termId, 'nav_menu'); - if ($term instanceof WP_Term) { - self::deleteMenuItemCache($term); + if ($term instanceof \WP_Term) { + self::deleteMenuItemCache($term); + } + }); } - } - public static function resetMenuCacheByMenuId(int $menuId): void { - /** @var WP_Error|WP_Term[] $terms */ - $terms = wp_get_post_terms($menuId, 'nav_menu'); + foreach ($postActions as $postAction) { + add_action($postAction, static function (int $menuId): void { + /** @var \WP_Error|\WP_Term[] $terms */ + $terms = wp_get_post_terms($menuId, 'nav_menu'); - if (!$terms instanceof WP_Error) { - foreach ($terms as $term) { - self::deleteMenuItemCache($term); - } + if (!$terms instanceof \WP_Error) { + foreach ($terms as $term) { + self::deleteMenuItemCache($term); + } + } + }); } } - public static function getMenuItemCacheKey(WP_Term $wpTerm): string { - return sprintf('%s_%s', $wpTerm->taxonomy, $wpTerm->slug); - } - /** * @param NavMenuArgs|\stdClass $args */ - private function getMenuWrapId(WP_Term $wpTerm, $args): string { + private static function getMenuWrapId(\WP_Term $wpTerm, $args): string { /** @var string[] $menuIdSlugs */ static $menuIdSlugs = []; @@ -199,7 +180,7 @@ private function getMenuWrapId(WP_Term $wpTerm, $args): string { /** * @param NavMenuArgs|\stdClass $args */ - private function wrapToContainer($args, WP_Term $wpTerm, string $navMenu): string { + private static function wrapToContainer($args, \WP_Term $wpTerm, string $navMenu): string { /** @var string[] $allowedTags */ $allowedTags = (array) apply_filters('wp_nav_menu_container_allowedtags', ['div', 'nav']); @@ -228,7 +209,7 @@ private function wrapToContainer($args, WP_Term $wpTerm, string $navMenu): strin return $navMenu; } - private static function deleteMenuItemCache(WP_Term $wpTerm): void { + private static function deleteMenuItemCache(\WP_Term $wpTerm): void { wp_cache_delete(self::getMenuItemCacheKey($wpTerm), 'menu_items'); } } diff --git a/src/helper.php b/src/helper.php index 3eb5337..01c7246 100644 --- a/src/helper.php +++ b/src/helper.php @@ -76,6 +76,7 @@ function app_get_term_link(int $termId, string $termTaxonomy) { wp_cache_set($ancestorsKey, $result, NavMenuCache::CACHE_GROUP); } + $hierarchicalSlugs = array_reverse($hierarchicalSlugs); } @@ -94,16 +95,16 @@ function app_get_term_link(int $termId, string $termTaxonomy) { if (!function_exists('app_term_link_filter')) { /** - * @param \WP_Term $term + * @param \WP_Term $wpTerm */ - function app_term_link_filter(WP_Term $term, string $termlink): string { - if ('post_tag' == $term->taxonomy) { - $termlink = (string) apply_filters('tag_link', $termlink, $term->term_id); - } elseif ('category' == $term->taxonomy) { - $termlink = (string) apply_filters('category_link', $termlink, $term->term_id); + function app_term_link_filter(WP_Term $wpTerm, string $termlink): string { + if ('post_tag' == $wpTerm->taxonomy) { + $termlink = (string) apply_filters('tag_link', $termlink, $wpTerm->term_id); + } elseif ('category' == $wpTerm->taxonomy) { + $termlink = (string) apply_filters('category_link', $termlink, $wpTerm->term_id); } - return (string) apply_filters('term_link', $termlink, $term, $term->taxonomy); + return (string) apply_filters('term_link', $termlink, $wpTerm, $wpTerm->taxonomy); } } diff --git a/stubs/MenuItem.php b/stubs/MenuItem.php index 1154eeb..f0f525d 100644 --- a/stubs/MenuItem.php +++ b/stubs/MenuItem.php @@ -2,14 +2,12 @@ namespace JazzMan\WpNavMenuCacheStub; -use stdClass; - /** * Class MenuItem. * * @SuppressWarnings(PHPMD) */ -class MenuItem extends stdClass { +class MenuItem extends \stdClass { /** * The term_id if the menu item represents a taxonomy term. */ diff --git a/stubs/NavMenuArgs.php b/stubs/NavMenuArgs.php index c310d4c..5669239 100644 --- a/stubs/NavMenuArgs.php +++ b/stubs/NavMenuArgs.php @@ -2,20 +2,16 @@ namespace JazzMan\WpNavMenuCacheStub; -use stdClass; -use Walker_Nav_Menu; -use WP_Term; - /** * Class NavMenuArgs. * * @SuppressWarnings(PHPMD) */ -class NavMenuArgs extends stdClass { +class NavMenuArgs extends \stdClass { /** * Desired menu. Accepts a menu ID, slug, name, or object. Default empty. * - * @var int|string|WP_Term + * @var int|string|\WP_Term * * @phpstan-ignore-next-line */ @@ -96,7 +92,7 @@ class NavMenuArgs extends stdClass { * * @phpstan-ignore-next-line */ - public ?Walker_Nav_Menu $walker = null; + public ?\Walker_Nav_Menu $walker = null; /** * Theme location to be used. Must be registered with register_nav_menu() diff --git a/wp-nav-menu-cache.php b/wp-nav-menu-cache.php index ce8cee9..31769de 100644 --- a/wp-nav-menu-cache.php +++ b/wp-nav-menu-cache.php @@ -13,8 +13,13 @@ use JazzMan\WpNavMenuCache\NavMenuCache; -if (function_exists('app_autoload_classes') && class_exists(NavMenuCache::class)) { - app_autoload_classes([ - NavMenuCache::class, - ]); +if (!function_exists('app_autoload_classes')) { + return; } + +if (!class_exists(NavMenuCache::class)) { + return; +} +app_autoload_classes([ + NavMenuCache::class, +]);