> */ private array $fieldCache = []; /** * @param list $sources Highest priority first. */ public function __construct(private array $sources, ?callable $diviEnabled = null) { $this->diviEnabled = $diviEnabled ?? DiviModules::builderEnabledFor(...); } /** @return list */ public function postTypeNames(): array { $excluded = apply_filters('lit_mcp_excluded_post_types', self::EXCLUDED); $excluded = is_array($excluded) ? $excluded : self::EXCLUDED; $names = array_values(array_diff( array_map('strval', array_keys(get_post_types(['show_ui' => true], 'names'))), $excluded )); sort($names); return $names; } public function isManaged(string $postType): bool { return in_array($postType, $this->postTypeNames(), true); } /** @return list> */ public function describePostTypes(): array { $types = []; foreach ($this->postTypeNames() as $name) { $description = $this->describe($name, false); if ($description !== null) { $types[] = $description; } } return $types; } /** @return array|null Null when the post type is not managed. */ public function describe(string $postType, bool $withFields): ?array { $object = $this->isManaged($postType) ? get_post_type_object($postType) : null; if ($object === null) { return null; } $description = [ 'name' => $postType, 'label' => (string) $object->labels->name, 'singular_label' => (string) $object->labels->singular_name, 'hierarchical' => (bool) $object->hierarchical, 'supports' => array_map('strval', array_keys(get_all_post_type_supports($postType))), 'divi' => $this->diviEnabled($postType), 'count' => $this->count($postType), 'taxonomies' => $this->taxonomies($postType), ]; if ($withFields) { $visible = array_filter( $this->fields($postType), static fn (FieldDefinition $field): bool => $field->status !== FieldDefinition::HIDDEN ); $description['fields'] = array_values(array_map( static fn (FieldDefinition $field): array => $field->toArray(), $visible )); } return $description; } /** @return array */ public function fields(string $postType): array { if (! isset($this->fieldCache[$postType])) { $merged = []; foreach ($this->sources as $source) { foreach ($source->fieldsFor($postType) as $field) { $merged[$field->key] ??= $field; } } ksort($merged); $this->fieldCache[$postType] = $merged; } return $this->fieldCache[$postType]; } public function sourceOf(FieldDefinition $field): FieldSource { foreach ($this->sources as $source) { if ($source->id() === $field->source) { return $source; } } throw new LogicException(sprintf('No field source "%s" registered.', $field->source)); } /** * Keys of fields that store attachment IDs (file fields, relations to media) over all * managed post types, sorted – used by the media usage search. * * @return list */ public function mediaFieldKeys(): array { $keys = []; foreach ($this->postTypeNames() as $postType) { foreach ($this->fields($postType) as $field) { if ($field->type === 'file' || ($field->related['object_type'] ?? null) === 'media') { $keys[$field->key] = true; } } } $keys = array_map('strval', array_keys($keys)); sort($keys); return $keys; } /** @return list */ public function taxonomies(string $postType): array { $result = []; foreach (get_object_taxonomies($postType, 'objects') as $taxonomy) { if (! $taxonomy->show_ui) { continue; } $result[] = [ 'name' => (string) $taxonomy->name, 'label' => (string) $taxonomy->labels->name, 'hierarchical' => (bool) $taxonomy->hierarchical, ]; } return $result; } public function diviEnabled(string $postType): bool { return ($this->diviEnabled)($postType); } private function count(string $postType): int { $counts = (array) wp_count_posts($postType); $total = 0; foreach (self::COUNTED_STATUSES as $status) { $total += (int) ($counts[$status] ?? 0); } return $total; } }