• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / dd6e866a9e6d253114633104d9e3858d807178ba

19 Jun 2024 10:03AM UTC coverage: 48.628% (-4.3%) from 52.936%
dd6e866a9e6d253114633104d9e3858d807178ba

push

github

web-flow
Merge pull request #21431 from Yoast/21429-update-copy-in-the-introduction-and-consent-modals

Updates the copy for the introduction and consent modals

7441 of 13454 branches covered (55.31%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

3718 existing lines in 107 files now uncovered.

25100 of 53464 relevant lines covered (46.95%)

62392.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/inc/sitemaps/class-sitemaps-renderer.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\XML_Sitemaps
6
 */
7

8
/**
9
 * Renders XML output for sitemaps.
10
 */
11
class WPSEO_Sitemaps_Renderer {
12

13
        /**
14
         * XSL stylesheet for styling a sitemap for web browsers.
15
         *
16
         * @var string
17
         */
18
        protected $stylesheet = '';
19

20
        /**
21
         * Holds the get_bloginfo( 'charset' ) value to reuse for performance.
22
         *
23
         * @var string
24
         */
25
        protected $charset = 'UTF-8';
26

27
        /**
28
         * Holds charset of output, might be converted.
29
         *
30
         * @var string
31
         */
32
        protected $output_charset = 'UTF-8';
33

34
        /**
35
         * If data encoding needs to be converted for output.
36
         *
37
         * @var bool
38
         */
39
        protected $needs_conversion = false;
40

41
        /**
42
         * Set up object properties.
43
         */
44
        public function __construct() {
×
45
                $stylesheet_url       = preg_replace( '/(^http[s]?:)/', '', $this->get_xsl_url() );
×
46
                $this->stylesheet     = '<?xml-stylesheet type="text/xsl" href="' . esc_url( $stylesheet_url ) . '"?>';
×
47
                $this->charset        = get_bloginfo( 'charset' );
×
48
                $this->output_charset = $this->charset;
×
49

50
                if (
51
                        $this->charset !== 'UTF-8'
×
52
                        && function_exists( 'mb_list_encodings' )
×
53
                        && in_array( $this->charset, mb_list_encodings(), true )
×
54
                ) {
55
                        $this->output_charset = 'UTF-8';
×
56
                }
57

58
                $this->needs_conversion = $this->output_charset !== $this->charset;
×
59
        }
60

61
        /**
62
         * Builds the sitemap index.
63
         *
64
         * @param array<string> $links Set of sitemaps index links.
65
         *
66
         * @return string
67
         */
UNCOV
68
        public function get_index( $links ) {
×
69

UNCOV
70
                $xml = '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
×
71

UNCOV
72
                foreach ( $links as $link ) {
×
UNCOV
73
                        $xml .= $this->sitemap_index_url( $link );
×
74
                }
75

76
                /**
77
                 * Filter to append sitemaps to the index.
78
                 *
79
                 * @param string $index String to append to sitemaps index, defaults to empty.
80
                 */
UNCOV
81
                $xml .= apply_filters( 'wpseo_sitemap_index', '' );
×
UNCOV
82
                $xml .= '</sitemapindex>';
×
83

UNCOV
84
                return $xml;
×
85
        }
86

87
        /**
88
         * Builds the sitemap.
89
         *
90
         * @param array<string> $links        Set of sitemap links.
91
         * @param string        $type         Sitemap type.
92
         * @param int           $current_page Current sitemap page number.
93
         *
94
         * @return string
95
         */
UNCOV
96
        public function get_sitemap( $links, $type, $current_page ) {
×
97

UNCOV
98
                $urlset = '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" '
×
UNCOV
99
                                        . 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd '
×
UNCOV
100
                                        . 'http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" '
×
UNCOV
101
                                        . 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
×
102

103
                /**
104
                 * Filters the `urlset` for all sitemaps.
105
                 *
106
                 * @param string $urlset The output for the sitemap's `urlset`.
107
                 */
UNCOV
108
                $urlset = apply_filters( 'wpseo_sitemap_urlset', $urlset );
×
109

110
                /**
111
                 * Filters the `urlset` for a sitemap by type.
112
                 *
113
                 * @param string $urlset The output for the sitemap's `urlset`.
114
                 */
UNCOV
115
                $xml = apply_filters( "wpseo_sitemap_{$type}_urlset", $urlset );
×
116

UNCOV
117
                foreach ( $links as $url ) {
×
UNCOV
118
                        $xml .= $this->sitemap_url( $url );
×
119
                }
120

121
                /**
122
                 * Filter to add extra URLs to the XML sitemap by type.
123
                 *
124
                 * Only runs for the first page, not on all.
125
                 *
126
                 * @param string $content String content to add, defaults to empty.
127
                 */
UNCOV
128
                if ( $current_page === 1 ) {
×
129
                        $xml .= apply_filters( "wpseo_sitemap_{$type}_content", '' );
×
130
                }
131

UNCOV
132
                $xml .= '</urlset>';
×
133

UNCOV
134
                return $xml;
×
135
        }
136

137
        /**
138
         * Produce final XML output with debug information.
139
         *
140
         * @param string $sitemap Sitemap XML.
141
         *
142
         * @return string
143
         */
144
        public function get_output( $sitemap ) {
×
145

146
                $output = '<?xml version="1.0" encoding="' . esc_attr( $this->output_charset ) . '"?>';
×
147

148
                if ( $this->stylesheet ) {
×
149
                        /**
150
                         * Filter the stylesheet URL for the XML sitemap.
151
                         *
152
                         * @param string $stylesheet Stylesheet URL.
153
                         */
154
                        $output .= apply_filters( 'wpseo_stylesheet_url', $this->stylesheet ) . "\n";
×
155
                }
156

157
                $output .= $sitemap;
×
158
                $output .= "\n<!-- XML Sitemap generated by Yoast SEO -->";
×
159

160
                return $output;
×
161
        }
162

163
        /**
164
         * Get charset for the output.
165
         *
166
         * @return string
167
         */
168
        public function get_output_charset() {
×
169
                return $this->output_charset;
×
170
        }
171

172
        /**
173
         * Set a custom stylesheet for this sitemap. Set to empty to just remove the default stylesheet.
174
         *
175
         * @param string $stylesheet Full XML-stylesheet declaration.
176
         *
177
         * @return void
178
         */
179
        public function set_stylesheet( $stylesheet ) {
×
180
                $this->stylesheet = $stylesheet;
×
181
        }
182

183
        /**
184
         * Build the `<sitemap>` tag for a given URL.
185
         *
186
         * @param array<string> $url Array of parts that make up this entry.
187
         *
188
         * @return string
189
         */
190
        protected function sitemap_index_url( $url ) {
×
191

192
                $date = null;
×
193

194
                if ( ! empty( $url['lastmod'] ) ) {
×
195
                        $date = YoastSEO()->helpers->date->format( $url['lastmod'] );
×
196
                }
197

198
                $url['loc'] = htmlspecialchars( $url['loc'], ENT_COMPAT, $this->output_charset, false );
×
199

200
                $output  = "\t<sitemap>\n";
×
201
                $output .= "\t\t<loc>" . $url['loc'] . "</loc>\n";
×
202
                $output .= empty( $date ) ? '' : "\t\t<lastmod>" . htmlspecialchars( $date, ENT_COMPAT, $this->output_charset, false ) . "</lastmod>\n";
×
203
                $output .= "\t</sitemap>\n";
×
204

205
                return $output;
×
206
        }
207

208
        /**
209
         * Build the `<url>` tag for a given URL.
210
         *
211
         * Public access for backwards compatibility reasons.
212
         *
213
         * @param array<string> $url Array of parts that make up this entry.
214
         *
215
         * @return string
216
         */
217
        public function sitemap_url( $url ) {
×
218

219
                $date = null;
×
220

221
                if ( ! empty( $url['mod'] ) ) {
×
222
                        // Create a DateTime object date in the correct timezone.
223
                        $date = YoastSEO()->helpers->date->format( $url['mod'] );
×
224
                }
225

226
                $output  = "\t<url>\n";
×
227
                $output .= "\t\t<loc>" . $this->encode_and_escape( $url['loc'] ) . "</loc>\n";
×
228
                $output .= empty( $date ) ? '' : "\t\t<lastmod>" . htmlspecialchars( $date, ENT_COMPAT, $this->output_charset, false ) . "</lastmod>\n";
×
229

230
                if ( empty( $url['images'] ) ) {
×
231
                        $url['images'] = [];
×
232
                }
233

234
                foreach ( $url['images'] as $img ) {
×
235

236
                        if ( empty( $img['src'] ) ) {
×
237
                                continue;
×
238
                        }
239

240
                        $output .= "\t\t<image:image>\n";
×
241
                        $output .= "\t\t\t<image:loc>" . $this->encode_and_escape( $img['src'] ) . "</image:loc>\n";
×
242
                        $output .= "\t\t</image:image>\n";
×
243
                }
244
                unset( $img );
×
245

246
                $output .= "\t</url>\n";
×
247

248
                /**
249
                 * Filters the output for the sitemap URL tag.
250
                 *
251
                 * @param string $output The output for the sitemap url tag.
252
                 * @param array  $url    The sitemap URL array on which the output is based.
253
                 */
254
                return apply_filters( 'wpseo_sitemap_url', $output, $url );
×
255
        }
256

257
        /**
258
         * Ensure the URL is encoded per RFC3986 and correctly escaped for use in an XML sitemap.
259
         *
260
         * This method works around a two quirks in esc_url():
261
         * 1. `esc_url()` leaves schema-relative URLs alone, while according to the sitemap specs,
262
         *    the URL must always begin with a protocol.
263
         * 2. `esc_url()` escapes ampersands as `&#038;` instead of the more common `&amp;`.
264
         *    According to the specs, `&amp;` should be used, and even though this shouldn't
265
         *    really make a difference in practice, to quote Jono: "I'd be nervous about &#038;
266
         *    given how many weird and wonderful things eat sitemaps", so better safe than sorry.
267
         *
268
         * @link https://www.sitemaps.org/protocol.html#xmlTagDefinitions
269
         * @link https://www.sitemaps.org/protocol.html#escaping
270
         * @link https://developer.wordpress.org/reference/functions/esc_url/
271
         *
272
         * @param string $url URL to encode and escape.
273
         *
274
         * @return string
275
         */
UNCOV
276
        protected function encode_and_escape( $url ) {
×
UNCOV
277
                $url = $this->encode_url_rfc3986( $url );
×
UNCOV
278
                $url = esc_url( $url );
×
UNCOV
279
                $url = str_replace( '&#038;', '&amp;', $url );
×
UNCOV
280
                $url = str_replace( '&#039;', '&apos;', $url );
×
281

UNCOV
282
                if ( strpos( $url, '//' ) === 0 ) {
×
283
                        // Schema-relative URL for which esc_url() does not add a scheme.
UNCOV
284
                        $url = 'http:' . $url;
×
285
                }
286

UNCOV
287
                return $url;
×
288
        }
289

290
        /**
291
         * Apply some best effort conversion to comply with RFC3986.
292
         *
293
         * @param string $url URL to encode.
294
         *
295
         * @return string
296
         */
UNCOV
297
        protected function encode_url_rfc3986( $url ) {
×
298

UNCOV
299
                if ( filter_var( $url, FILTER_VALIDATE_URL ) ) {
×
UNCOV
300
                        return $url;
×
301
                }
302

UNCOV
303
                $path = wp_parse_url( $url, PHP_URL_PATH );
×
304

UNCOV
305
                if ( ! empty( $path ) && $path !== '/' ) {
×
UNCOV
306
                        $encoded_path = explode( '/', $path );
×
307

308
                        // First decode the path, to prevent double encoding.
UNCOV
309
                        $encoded_path = array_map( 'rawurldecode', $encoded_path );
×
310

UNCOV
311
                        $encoded_path = array_map( 'rawurlencode', $encoded_path );
×
UNCOV
312
                        $encoded_path = implode( '/', $encoded_path );
×
313

UNCOV
314
                        $url = str_replace( $path, $encoded_path, $url );
×
315
                }
316

UNCOV
317
                $query = wp_parse_url( $url, PHP_URL_QUERY );
×
318

UNCOV
319
                if ( ! empty( $query ) ) {
×
320

UNCOV
321
                        parse_str( $query, $parsed_query );
×
322

UNCOV
323
                        $parsed_query = http_build_query( $parsed_query, '', '&amp;', PHP_QUERY_RFC3986 );
×
324

UNCOV
325
                        $url = str_replace( $query, $parsed_query, $url );
×
326
                }
327

UNCOV
328
                return $url;
×
329
        }
330

331
        /**
332
         * Retrieves the XSL URL that should be used in the current environment
333
         *
334
         * When home_url and site_url are not the same, the home_url should be used.
335
         * This is because the XSL needs to be served from the same domain, protocol and port
336
         * as the XML file that is loading it.
337
         *
338
         * @return string The XSL URL that needs to be used.
339
         */
UNCOV
340
        protected function get_xsl_url() {
×
UNCOV
341
                if ( home_url() !== site_url() ) {
×
342
                        return home_url( 'main-sitemap.xsl' );
×
343
                }
344

345
                /*
346
                 * Fallback to circumvent a cross-domain security problem when the XLS file is
347
                 * loaded from a different (sub)domain.
348
                 */
UNCOV
349
                if ( strpos( plugins_url(), home_url() ) !== 0 ) {
×
UNCOV
350
                        return home_url( 'main-sitemap.xsl' );
×
351
                }
352

UNCOV
353
                return plugin_dir_url( WPSEO_FILE ) . 'css/main-sitemap.xsl';
×
354
        }
355
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc