| @@ -155,8 +155,11 @@ | ||
| 155 | 155 | break; |
| 156 | 156 | case 'FAQPage': |
| 157 | 157 | $validation = $this->validate_faq_schema($schema, $validation); |
| 158 | 158 | break; |
| 159 | + case 'VideoObject': | |
| 160 | + $validation = $this->validate_video_object_schema($schema, $validation); | |
| 161 | + break; | |
| 159 | 162 | } |
| 160 | 163 | |
| 161 | 164 | return $validation; |
| 162 | 165 | } |
| @@ -161,8 +164,35 @@ | ||
| 161 | 164 | return $validation; |
| 162 | 165 | } |
| 163 | 166 | |
| 164 | 167 | /** |
| 168 | + * Validate VideoObject schema specific requirements. | |
| 169 | + * | |
| 170 | + * Google requires name, description, thumbnailUrl and uploadDate. A video is | |
| 171 | + * only playable with a contentUrl or embedUrl, so warn when both are missing. | |
| 172 | + * | |
| 173 | + * @since 1.0.0 | |
| 174 | + * | |
| 175 | + * @param array $schema Schema markup | |
| 176 | + * @param array $validation Current validation results | |
| 177 | + * @return array Updated validation results | |
| 178 | + */ | |
| 179 | + private function validate_video_object_schema(array $schema, array $validation): array { | |
| 180 | + foreach (['name', 'description', 'thumbnailUrl', 'uploadDate'] as $field) { | |
| 181 | + if (empty($schema[$field])) { | |
| 182 | + $validation['errors'][] = sprintf('VideoObject schema requires a %s', $field); | |
| 183 | + $validation['valid'] = false; | |
| 184 | + } | |
| 185 | + } | |
| 186 | + | |
| 187 | + if (empty($schema['contentUrl']) && empty($schema['embedUrl'])) { | |
| 188 | + $validation['warnings'][] = 'VideoObject should include a contentUrl or embedUrl so the video is playable'; | |
| 189 | + } | |
| 190 | + | |
| 191 | + return $validation; | |
| 192 | + } | |
| 193 | + | |
| 194 | + /** | |
| 165 | 195 | * Validate Article schema specific requirements |
| 166 | 196 | * PRESERVED: Exact same method logic from original Schema_Generator |
| 167 | 197 | * |
| 168 | 198 | * @since 1.0.0 |
| @@ -172,9 +202,9 @@ | ||
| 172 | 202 | * @return array Updated validation results |
| 173 | 203 | */ |
| 174 | 204 | private function validate_article_schema(array $schema, array $validation): array { |
| 175 | 205 | // Check headline length (Google recommends under 110 characters) |
| 176 | - if (isset($schema['headline']) && strlen($schema['headline']) > 110) { | |
| 206 | + if (isset($schema['headline']) && is_string($schema['headline']) && strlen($schema['headline']) > 110) { | |
| 177 | 207 | $validation['warnings'][] = 'Headline is longer than 110 characters, may be truncated in search results'; |
| 178 | 208 | } |
| 179 | 209 | |
| 180 | 210 | // Check for image |
| @@ -341,9 +371,9 @@ | ||
| 341 | 371 | if (isset($schema['logo']) && !empty($schema['logo']) && |
| 342 | 372 | (is_string($schema['logo']) || (is_array($schema['logo']) && !empty($schema['logo']['url'])))) { |
| 343 | 373 | $has_logo = true; |
| 344 | 374 | } elseif (isset($schema['publisher']['logo']) && !empty($schema['publisher']['logo']) && |
| 345 | - (is_string($schema['publisher']['logo']) || (is_array($schema['publisher']['logo']) && !empty($schema['publisher']['logo']['url'])))) { | |
| 375 | + (is_string($schema['publisher']['logo']) || (is_array($schema['publisher']['logo']) && !empty($schema['publisher']['logo']['url'])))) { | |
| 346 | 376 | $has_logo = true; |
| 347 | 377 | } |
| 348 | 378 | |
| 349 | 379 | if (!$has_logo) { |
| @@ -376,9 +406,9 @@ | ||
| 376 | 406 | * @return array Updated validation results |
| 377 | 407 | */ |
| 378 | 408 | private function validate_faq_schema(array $schema, array $validation): array { |
| 379 | 409 | // Check minimum number of questions |
| 380 | - if (isset($schema['mainEntity']) && count($schema['mainEntity']) < 2) { | |
| 410 | + if (isset($schema['mainEntity']) && is_array($schema['mainEntity']) && count($schema['mainEntity']) < 2) { | |
| 381 | 411 | $validation['warnings'][] = 'FAQ pages should have at least 2 questions for optimal SEO'; |
| 382 | 412 | } |
| 383 | 413 | |
| 384 | 414 | // Validate question structure |