← All changes
|
includes/diagnostics/class-foreign-schema-detector.php
+43
-3
2.9.0
→
2.14.1
View file →
| @@ -58,11 +58,12 @@ | ||
| 58 | 58 | * |
| 59 | 59 | * `class` matches a token in the script tag's class attribute, which is the |
| 60 | 60 | * strongest signal available — the big four each tag their own block. `comment` |
| 61 | 61 | * matches the HTML comment wrapper a plugin prints around its head output, |
| 62 | - * for the ones that carry no class. | |
| 62 | + * for the ones that carry no class. `id`, where present, matches the script | |
| 63 | + * tag's id attribute exactly, for a plugin that labels its block that way. | |
| 63 | 64 | * |
| 64 | - * @var array<string, array{name:string, class:string[], comment:string[]}> | |
| 65 | + * @var array<string, array{name:string, class:string[], comment:string[], id?:string[]}> | |
| 65 | 66 | */ |
| 66 | 67 | private const SIGNATURES = [ |
| 67 | 68 | 'yoast' => [ |
| 68 | 69 | 'name' => 'Yoast SEO', |
| @@ -98,8 +99,16 @@ | ||
| 98 | 99 | 'name' => 'WooCommerce', |
| 99 | 100 | 'class' => [], |
| 100 | 101 | 'comment' => ['woocommerce json-ld'], |
| 101 | 102 | ], |
| 103 | + // SureRank tags its block by id, not class, and prints it outside | |
| 104 | + // its "SureRank Meta Data" comment pair (#916). | |
| 105 | + 'surerank' => [ | |
| 106 | + 'name' => 'SureRank', | |
| 107 | + 'class' => [], | |
| 108 | + 'comment' => [], | |
| 109 | + 'id' => ['surerank-schema'], | |
| 110 | + ], | |
| 102 | 111 | ]; |
| 103 | 112 | |
| 104 | 113 | /** |
| 105 | 114 | * Schema types worth warning about when both sides publish one. |
| @@ -356,11 +365,16 @@ | ||
| 356 | 365 | * @return array{slug:string, name:string, guess:bool} |
| 357 | 366 | */ |
| 358 | 367 | private function attribute(array $block): array { |
| 359 | 368 | $classes = $this->class_tokens($block['tag']); |
| 369 | + $tag_id = $this->tag_id($block['tag']); | |
| 360 | 370 | $comment = strtolower($block['preceding']); |
| 361 | 371 | |
| 362 | 372 | foreach (self::SIGNATURES as $slug => $signature) { |
| 373 | + if ($tag_id !== '' && in_array($tag_id, $signature['id'] ?? [], true)) { | |
| 374 | + return ['slug' => $slug, 'name' => $signature['name'], 'guess' => false]; | |
| 375 | + } | |
| 376 | + | |
| 363 | 377 | foreach ($signature['class'] as $class) { |
| 364 | 378 | if (in_array($class, $classes, true)) { |
| 365 | 379 | return ['slug' => $slug, 'name' => $signature['name'], 'guess' => false]; |
| 366 | 380 | } |
| @@ -402,8 +416,25 @@ | ||
| 402 | 416 | return $names; |
| 403 | 417 | } |
| 404 | 418 | |
| 405 | 419 | /** |
| 420 | + * The id attribute of a script tag, lowercased. | |
| 421 | + * | |
| 422 | + * @since 2.15.0 | |
| 423 | + * | |
| 424 | + * @param string $attributes Attribute text from the opening script tag. | |
| 425 | + * @return string Id, or an empty string when the tag has none. | |
| 426 | + */ | |
| 427 | + private function tag_id(string $attributes): string { | |
| 428 | + // Whitespace before the name, so `data-id` is not read as `id`. | |
| 429 | + if (!preg_match('#(?:^|\s)id\s*=\s*["\']([^"\']*)["\']#i', $attributes, $match)) { | |
| 430 | + return ''; | |
| 431 | + } | |
| 432 | + | |
| 433 | + return strtolower(trim($match[1])); | |
| 434 | + } | |
| 435 | + | |
| 436 | + /** | |
| 406 | 437 | * Class tokens on a script tag. |
| 407 | 438 | * |
| 408 | 439 | * @param string $attributes Attribute text from the opening script tag. |
| 409 | 440 | * @return string[] |
| @@ -483,9 +514,18 @@ | ||
| 483 | 514 | * @param string[] $types Type list. |
| 484 | 515 | * @return string[] |
| 485 | 516 | */ |
| 486 | 517 | private function page_level_only(array $types): array { |
| 487 | - return array_values(array_intersect($types, self::PAGE_LEVEL_TYPES)); | |
| 518 | + // Every LocalBusiness subtype is the same kind of entity for this | |
| 519 | + // purpose. ThinkRank publishes the subtype the site chose, so a | |
| 520 | + // "Dentist" of ours beside another plugin's "LocalBusiness" is still | |
| 521 | + // two businesses on one URL, and must still be reported as one. | |
| 522 | + $types = array_map( | |
| 523 | + static fn($type) => \ThinkRank\Config\Local_Business_Types_Config::is_local_business($type) ? 'LocalBusiness' : $type, | |
| 524 | + $types | |
| 525 | + ); | |
| 526 | + | |
| 527 | + return array_values(array_unique(array_intersect($types, self::PAGE_LEVEL_TYPES))); | |
| 488 | 528 | } |
| 489 | 529 | |
| 490 | 530 | /** |
| 491 | 531 | * The URL to scan. |