*/ private const REQUIRED_PROPERTIES = array( 'Article' => array( 'headline' ), 'WebPage' => array( 'name' ), 'WebSite' => array( 'name', 'url' ), 'Organization' => array( 'name' ), 'Person' => array( 'name' ), 'BreadcrumbList' => array( 'itemListElement' ), // M66 — user-forced main-node types (ForcedSchemaType). 'BlogPosting' => array( 'headline' ), 'NewsArticle' => array( 'headline' ), 'Blog' => array( 'name' ), 'CollectionPage' => array( 'name' ), 'AboutPage' => array( 'name' ), 'ContactPage' => array( 'name' ), ); /** * @return array */ private static function required_properties(): array { /** * Filters the required-property map used by the local validator — * the extension point premium schema types (Pro, M30) add their own * Google Rich Results requirements through, without touching this class. * * @param array $required_properties */ return (array) apply_filters( 'acez_seo_schema_required_properties', self::REQUIRED_PROPERTIES ); } /** * @param array $graph_document The full {"@context":...,"@graph":[...]} array. * * @return string[] Human-readable issues; empty array means valid. */ public function validate( array $graph_document ): array { $issues = array(); if ( ! isset( $graph_document['@graph'] ) || ! is_array( $graph_document['@graph'] ) ) { $issues[] = __( 'Missing @graph node.', 'acez-seo' ); return $issues; } $seen_ids = array(); $required_properties = self::required_properties(); foreach ( $graph_document['@graph'] as $index => $node ) { if ( ! is_array( $node ) ) { $issues[] = sprintf( /* translators: %d: node index */ __( 'Node #%d is not a valid object.', 'acez-seo' ), (int) $index ); continue; } $type = isset( $node['@type'] ) ? (string) $node['@type'] : ''; if ( '' === $type ) { $issues[] = sprintf( /* translators: %d: node index */ __( 'Node #%d has no @type field.', 'acez-seo' ), (int) $index ); continue; } if ( isset( $node['@id'] ) ) { $id = (string) $node['@id']; if ( isset( $seen_ids[ $id ] ) ) { $issues[] = sprintf( /* translators: 1: @id value, 2: schema type */ __( 'Duplicate @id: %1$s (type %2$s).', 'acez-seo' ), $id, $type ); } $seen_ids[ $id ] = true; } foreach ( $required_properties[ $type ] ?? array() as $required_property ) { if ( empty( $node[ $required_property ] ) ) { $issues[] = sprintf( /* translators: 1: schema type, 2: missing property name */ __( '%1$s: missing required property "%2$s".', 'acez-seo' ), $type, $required_property ); } } } $json = wp_json_encode( $graph_document ); if ( ! is_string( $json ) || null === json_decode( $json ) ) { $issues[] = __( 'The generated JSON-LD has a syntax error.', 'acez-seo' ); } return $issues; } }