| @@ -3,9 +3,8 @@ | ||
| 3 | 3 | * MetaSync Breadcrumbs Schema — BreadcrumbList JSON-LD output via wp_head. |
| 4 | 4 | * |
| 5 | 5 | * @package Metasync |
| 6 | 6 | * @subpackage Metasync/breadcrumbs |
| 7 | - * @since 2.9.0 | |
| 8 | 7 | */ |
| 9 | 8 | |
| 10 | 9 | if (!defined('ABSPATH')) { |
| 11 | 10 | exit; |
| @@ -86,9 +85,18 @@ | ||
| 86 | 85 | // Build ListItem entries. |
| 87 | 86 | $list_items = array(); |
| 88 | 87 | $position = 1; |
| 89 | 88 | |
| 90 | - foreach ($trail as $item) { | |
| 89 | + $trail_total = count($trail); | |
| 90 | + | |
| 91 | + foreach ($trail as $trail_index => $item) { | |
| 92 | + // Google allows only the FINAL ListItem to omit `item`. Drop any | |
| 93 | + // earlier crumb whose URL could not be resolved rather than emit | |
| 94 | + // structured data that fails validation. | |
| 95 | + if ($trail_index !== $trail_total - 1 && empty($item['url'])) { | |
| 96 | + continue; | |
| 97 | + } | |
| 98 | + | |
| 91 | 99 | $list_item = array( |
| 92 | 100 | '@type' => 'ListItem', |
| 93 | 101 | 'position' => $position, |
| 94 | 102 | 'name' => html_entity_decode($item['label'], ENT_QUOTES | ENT_HTML5, 'UTF-8'), |
| @@ -111,10 +119,15 @@ | ||
| 111 | 119 | ), |
| 112 | 120 | ), |
| 113 | 121 | ); |
| 114 | 122 | |
| 123 | + $json = metasync_safe_json_ld_encode($json_ld, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); | |
| 124 | + if (!is_string($json) || $json === '') { | |
| 125 | + return; | |
| 126 | + } | |
| 127 | + | |
| 115 | 128 | echo '<script type="application/ld+json" class="metasync-breadcrumb-schema">' . "\n"; |
| 116 | - echo wp_json_encode($json_ld, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); | |
| 129 | + echo $json; | |
| 117 | 130 | echo "\n" . '</script>' . "\n"; |
| 118 | 131 | |
| 119 | 132 | self::$breadcrumb_list_injected = true; |
| 120 | 133 | } |