PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.0
2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 All 51 releases
← All changes | includes/core/class-seo-text.php +81 -0 2.9.0 → 2.10.0 View file →
@@ -81,8 +81,29 @@
81 81 return 'words' === _x('words', 'Word count type. Do not translate!', 'default');
82 82 }
83 83
84 84 /**
85 + * The text a reader sees for a resolved title or description.
86 + *
87 + * Pattern_Resolver hands back what the page will print, and that is still
88 + * HTML: get_the_title() runs wptexturize, so "Foo & Bar" arrives as
89 + * `Foo & Bar`, while the same words typed into the SEO title field
90 + * arrive as `Foo &amp; Bar` or a bare `&`. All three render as one `<title>`.
91 + * Anything that compares, measures or displays the value as text has to
92 + * look at it after the browser would have decoded it, or it groups two
93 + * identical titles apart, counts `&#038;` as six characters, and shows the
94 + * entity to the user.
95 + *
96 + * @since 2.10.0
97 + *
98 + * @param string $value Resolved title or description.
99 + * @return string The same value with HTML entities decoded.
100 + */
101 + public static function as_displayed(string $value): string {
102 + return html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
103 + }
104 +
105 + /**
85 106 * Trim a description to a character budget, multibyte-safe.
86 107 *
87 108 * Cuts on a word boundary when one is available inside the budget, so the
88 109 * result does not end mid-word; falls back to a hard character cut for
@@ -118,8 +139,68 @@
118 139 $cut = mb_substr($cut, 0, $last_gap);
119 140 }
120 141
121 142 return rtrim($cut) . '…';
143 + }
144 +
145 + /**
146 + * Make text fit to appear in JSON-LD.
147 + *
148 + * JSON-LD is not HTML, so an HTML entity in it is not decoded by anything
149 + * downstream: `&amp;` was published to answer engines literally. And the
150 + * excerpt path appends core's trimming marker, so descriptions arrived
151 + * ending in `[…]`, a truncation artefact presented as the page's own
152 + * summary.
153 + *
154 + * Lives here rather than in the schema class that introduced it (#766)
155 + * because two producers build description nodes: the automatic
156 + * Global_SEO_Schema_Output and the Schema Manager's Schema_Builder. Only
157 + * the first normalised, so a deployed node, which outranks the automatic
158 + * one, published the raw entity again.
159 + *
160 + * @since 2.10.0
161 + *
162 + * @param string $text Raw text.
163 + * @return string
164 + */
165 + public static function normalize_schema_text(string $text): string {
166 + if ('' === trim($text)) {
167 + return '';
168 + }
169 +
170 + $text = self::decode_schema_entities(wp_strip_all_tags($text));
171 +
172 + // Core's excerpt marker, in both its entity and literal forms, with or
173 + // without the surrounding brackets it is normally wrapped in.
174 + $text = (string) preg_replace(
175 + '/\s*(\[\s*(\x{2026}|\.\.\.)\s*\]|\x{2026})\s*$/u',
176 + '',
177 + $text
178 + );
179 +
180 + return trim((string) preg_replace('/\s+/u', ' ', $text));
181 + }
182 +
183 + /**
184 + * Decode HTML entities in text bound for JSON-LD, and nothing else.
185 + *
186 + * The narrow half of normalize_schema_text(), for values that must keep
187 + * their exact shape otherwise: a stored snapshot already truncated with an
188 + * ellipsis would lose it to the excerpt-marker strip.
189 + *
190 + * @since 2.10.0
191 + *
192 + * @param string $text Text that may carry HTML entities.
193 + * @return string
194 + */
195 + public static function decode_schema_entities(string $text): string {
196 + // Twice: a description that has been through an escaping pass already
197 + // (core stores `&amp;amp;` for a literal `&amp;` in some paths) would
198 + // otherwise still carry an entity after one decode. Decoding an
199 + // already-plain string is a no-op, so this is safe to repeat.
200 + $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
201 +
202 + return html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
122 203 }
123 204
124 205 /**
125 206 * Apply a WORD cap that stays a word cap.