initialize(); } public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Initialize all registries and build relationships */ private function initialize(): void { // Build initial caches $this->buildContentCache(); $this->buildTaxonomyCache(); $this->buildUserRoleCache(); $this->buildRelationships(); // Set up WordPress hooks for cache invalidation add_action('init', [$this, 'validateRegistrations'], 1); add_action('registered_post_type', [$this, 'invalidateContentCache']); add_action('registered_taxonomy', [$this, 'invalidateTaxonomyCache']); } /** * Get all content types with optional filtering */ public function getContentTypes(array $filters = []): array { $content = $this->cache[self::CACHE_CONTENT] ?? []; if (empty($filters)) { return $content; } return $this->applyFilters($content, $filters); } /** * Get all taxonomies with optional filtering */ public function getTaxonomies(array $filters = []): array { $taxonomies = $this->cache[self::CACHE_TAXONOMIES] ?? []; if (empty($filters)) { return $taxonomies; } return $this->applyFilters($taxonomies, $filters); } /** * Get user roles with specific capabilities */ public function getUserRoles(array $filters = []): array { $roles = $this->cache[self::CACHE_USER_ROLES] ?? []; if (empty($filters)) { return $roles; } return $this->applyFilters($roles, $filters); } /** * Get taxonomies for a specific content type */ public function getTaxonomiesForContent(string $contentType): array { $contentType = jvbNoBase($contentType); return $this->relationships['content_taxonomies'][$contentType] ?? []; } /** * Get content types for a specific taxonomy */ public function getContentForTaxonomy(string $taxonomy): array { $taxonomy = jvbNoBase($taxonomy); return $this->relationships['taxonomy_content'][$taxonomy] ?? []; } /** * Get content types a user role can create */ public function getCreatableContent(string $role): array { $role = jvbNoBase($role); return $this->relationships['role_content'][$role] ?? []; } /** * Check if a type has a specific feature */ public function hasFeature(string $type, string $feature, string $registry = 'content'): bool { $type = jvbNoBase($type); $data = match ($registry) { 'content' => $this->cache[self::CACHE_CONTENT][$type] ?? [], 'taxonomy' => $this->cache[self::CACHE_TAXONOMIES][$type] ?? [], 'user' => $this->cache[self::CACHE_USER_ROLES][$type] ?? [], default => [] }; return isset($data[$feature]) && $data[$feature] === true; } /** * Get all types with a specific feature */ public function getTypesWithFeature(string $feature, string $registry = 'content'): array { $filters = [$feature => true]; return match ($registry) { 'content' => $this->getContentTypes($filters), 'taxonomy' => $this->getTaxonomies($filters), 'user' => $this->getUserRoles($filters), default => [] }; } /** * Build content type cache */ private function buildContentCache(): void { $this->cache[self::CACHE_CONTENT] = JVB_CONTENT; // Add computed properties foreach ($this->cache[self::CACHE_CONTENT] as $slug => &$config) { $config['_slug'] = $slug; $config['_post_type'] = BASE . $slug; $config['_supports_dashboard'] = $this->computesDashboardSupport($config); $config['_is_user_type'] = $this->computesUserType($config); } } /** * Build taxonomy cache */ private function buildTaxonomyCache(): void { $this->cache[self::CACHE_TAXONOMIES] = JVB_TAXONOMY; foreach ($this->cache[self::CACHE_TAXONOMIES] as $slug => &$config) { $config['_slug'] = $slug; $config['_taxonomy'] = BASE . $slug; $config['_is_hierarchical'] = $config['hierarchical'] ?? true; } } /** * Build user role cache */ private function buildUserRoleCache(): void { $this->cache[self::CACHE_USER_ROLES] = JVB_USER; foreach ($this->cache[self::CACHE_USER_ROLES] as $slug => &$config) { $config['_slug'] = $slug; $config['_role'] = BASE . $slug; $config['_creatable_content'] = $this->extractCreatableContent($config); } } /** * Build relationships between types */ private function buildRelationships(): void { // Content -> Taxonomies foreach ($this->cache[self::CACHE_CONTENT] as $contentSlug => $content) { $this->relationships['content_taxonomies'][$contentSlug] = []; } // Taxonomy -> Content foreach ($this->cache[self::CACHE_TAXONOMIES] as $taxSlug => $taxonomy) { $this->relationships['taxonomy_content'][$taxSlug] = $taxonomy['for_content'] ?? []; // Build reverse relationship foreach ($taxonomy['for_content'] ?? [] as $contentType) { $this->relationships['content_taxonomies'][$contentType][] = $taxSlug; } } // User Role -> Content foreach ($this->cache[self::CACHE_USER_ROLES] as $roleSlug => $role) { $this->relationships['role_content'][$roleSlug] = $role['_creatable_content']; } $this->cache[self::CACHE_RELATIONSHIPS] = $this->relationships; } /** * Apply filters to a registry array */ private function applyFilters(array $data, array $filters): array { return array_filter($data, function ($item) use ($filters) { foreach ($filters as $key => $value) { if (!isset($item[$key]) || $item[$key] !== $value) { return false; } } return true; }); } /** * Extract creatable content from user role config */ private function extractCreatableContent(array $config): array { $content = []; foreach ($config['can_create'] ?? [] as $item) { if (is_array($item)) { foreach ($item as $type => $contents) { $content = array_merge($content, $contents); } } else { $content[] = $item; } } return array_unique($content); } /** * Check if content type supports dashboard */ private function computesDashboardSupport(array $config): bool { return !empty($config['fields']) || !empty($config['sections']) || ($config['show_dashboard'] ?? false); } /** * Check if content type is a user profile type */ private function computesUserType(array $config): bool { foreach ($this->cache[self::CACHE_USER_ROLES] ?? [] as $role) { if (($role['profile'] ?? '') === $config['_slug']) { return true; } } return false; } /** * Validate all registrations are properly set up */ public function validateRegistrations(): void { $errors = []; // Validate taxonomy relationships foreach ($this->getTaxonomies() as $taxSlug => $taxonomy) { foreach ($taxonomy['for_content'] ?? [] as $contentType) { if (!isset($this->cache[self::CACHE_CONTENT][$contentType])) { $errors[] = "Taxonomy '{$taxSlug}' references non-existent content type '{$contentType}'"; } } } // Validate user role content permissions foreach ($this->getUserRoles() as $roleSlug => $role) { foreach ($role['_creatable_content'] as $contentType) { if (!isset($this->cache[self::CACHE_CONTENT][$contentType]) && !isset($this->cache[self::CACHE_TAXONOMIES][$contentType])) { $errors[] = "Role '{$roleSlug}' references non-existent type '{$contentType}'"; } } } if (!empty($errors) && WP_DEBUG) { foreach ($errors as $error) { error_log("[Checker Validation] {$error}"); } } } /** * Invalidate content cache */ public function invalidateContentCache(): void { unset($this->cache[self::CACHE_CONTENT]); $this->buildContentCache(); $this->buildRelationships(); } /** * Invalidate taxonomy cache */ public function invalidateTaxonomyCache(): void { unset($this->cache[self::CACHE_TAXONOMIES]); $this->buildTaxonomyCache(); $this->buildRelationships(); } /** * Get registry statistics for debugging */ public function getStats(): array { return [ 'content_types' => count($this->cache[self::CACHE_CONTENT] ?? []), 'taxonomies' => count($this->cache[self::CACHE_TAXONOMIES] ?? []), 'user_roles' => count($this->cache[self::CACHE_USER_ROLES] ?? []), 'relationships' => count($this->relationships), 'cache_size' => strlen(serialize($this->cache)) ]; } }