← All changes
|
includes/diagnostics/class-foreign-schema-detector.php
+33
-2
2.10.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 | } |
| @@ -399,8 +413,25 @@ | ||
| 399 | 413 | $names[$slug] = (string) ($plugin['name'] ?? $slug); |
| 400 | 414 | } |
| 401 | 415 | |
| 402 | 416 | return $names; |
| 417 | + } | |
| 418 | + | |
| 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])); | |
| 403 | 434 | } |
| 404 | 435 | |
| 405 | 436 | /** |
| 406 | 437 | * Class tokens on a script tag. |