WebPage schema, or empty array if not applicable. */ public function generate( \WP_Post $post ): array { $permalink = get_permalink( $post ); if ( ! $permalink ) { return array(); } $schema = array( '@context' => 'https://schema.org', '@type' => $this->detect_type( $post ), '@id' => $permalink, 'url' => $permalink, 'name' => wp_strip_all_tags( get_the_title( $post ) ), 'isPartOf' => array( '@type' => 'WebSite', '@id' => home_url( '/' ), 'name' => get_bloginfo( 'name' ), 'url' => home_url( '/' ), ), 'datePublished' => $this->date( $post->post_date_gmt ), 'dateModified' => $this->date( $post->post_modified_gmt ), ); $description = $this->description( $post ); if ( '' !== $description ) { $schema['description'] = $description; } $thumb_id = get_post_thumbnail_id( $post ); if ( $thumb_id ) { $image_url = wp_get_attachment_image_url( $thumb_id, 'full' ); if ( $image_url ) { $schema['primaryImageOfPage'] = array( '@type' => 'ImageObject', 'url' => $image_url, ); } } $language = get_bloginfo( 'language' ); if ( ! empty( $language ) ) { $schema['inLanguage'] = $language; } /** * Filters the WebPage schema for a page. * * @param array $schema The WebPage schema. * @param \WP_Post $post The page. */ return (array) apply_filters( 'aicommerce_webpage_schema', $schema, $post ); } /** * Detect a more specific WebPage subtype from the page. * * @param \WP_Post $post The page. * * @return string A schema.org WebPage type. */ private function detect_type( \WP_Post $post ): string { $type = 'WebPage'; $slug = $post->post_name; $title = strtolower( get_the_title( $post ) ); $front_id = (int) get_option( 'page_on_front' ); if ( $front_id && $front_id === $post->ID ) { $type = 'WebPage'; } elseif ( $this->matches( $slug, $title, array( 'about', 'about-us', 'our-story', 'company' ) ) ) { $type = 'AboutPage'; } elseif ( $this->matches( $slug, $title, array( 'contact', 'contact-us', 'get-in-touch' ) ) ) { $type = 'ContactPage'; } elseif ( $this->matches( $slug, $title, array( 'faq', 'faqs', 'help', 'support' ) ) ) { $type = 'FAQPage'; } elseif ( $this->matches( $slug, $title, array( 'checkout', 'cart' ) ) ) { $type = 'CheckoutPage'; } elseif ( $this->matches( $slug, $title, array( 'privacy', 'terms', 'policy', 'legal' ) ) ) { $type = 'WebPage'; } /** * Filters the detected WebPage subtype. * * @param string $type The detected type. * @param \WP_Post $post The page. */ return (string) apply_filters( 'aicommerce_webpage_type', $type, $post ); } /** * Whether a slug or title matches any of the given keywords. * * @param string $slug The page slug. * @param string $title The lowercased page title. * @param array $keywords Keywords to match. * * @return bool */ private function matches( string $slug, string $title, array $keywords ): bool { foreach ( $keywords as $keyword ) { if ( $slug === $keyword || str_contains( $title, $keyword ) ) { return true; } } return false; } /** * Build a plain-text description from the excerpt or content. * * @param \WP_Post $post The page. * * @return string */ private function description( \WP_Post $post ): string { $text = has_excerpt( $post ) ? get_the_excerpt( $post ) : $post->post_content; $text = wp_strip_all_tags( (string) $text ); $text = trim( preg_replace( '/\s+/', ' ', $text ) ?? '' ); if ( mb_strlen( $text ) > 300 ) { $text = mb_substr( $text, 0, 297 ) . '...'; } return $text; } /** * Convert a GMT date string to ISO 8601. * * @param string $gmt_date The GMT date (Y-m-d H:i:s). * * @return string */ private function date( string $gmt_date ): string { if ( '' === $gmt_date || '0000-00-00 00:00:00' === $gmt_date ) { return ''; } $timestamp = strtotime( $gmt_date . ' UTC' ); return $timestamp ? gmdate( 'c', $timestamp ) : ''; } }