Completed
Push — add/double-encode-message ( 8b6530...2d4e84 )
by
unknown
14:26 queued 05:57
created

Jetpack_Sitemap_Builder::post_to_sitemap_item()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 1
dl 0
loc 64
rs 8.7853
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Build the sitemap tree.
4
 *
5
 * @package Jetpack
6
 * @since 4.8.0
7
 * @author Automattic
8
 */
9
10
/* Include sitemap subclasses, if not already, and include proper buffer based on phpxml's availability. */
11
require_once dirname( __FILE__ ) . '/sitemap-constants.php';
12
require_once dirname( __FILE__ ) . '/sitemap-buffer.php';
13
14
if ( ! class_exists( 'DOMDocument' ) ) {
15
	require_once dirname( __FILE__ ) . '/sitemap-buffer-fallback.php';
16
	require_once dirname( __FILE__ ) . '/sitemap-buffer-image-fallback.php';
17
	require_once dirname( __FILE__ ) . '/sitemap-buffer-master-fallback.php';
18
	require_once dirname( __FILE__ ) . '/sitemap-buffer-news-fallback.php';
19
	require_once dirname( __FILE__ ) . '/sitemap-buffer-page-fallback.php';
20
	require_once dirname( __FILE__ ) . '/sitemap-buffer-video-fallback.php';
21
} else {
22
	require_once dirname( __FILE__ ) . '/sitemap-buffer-image.php';
23
	require_once dirname( __FILE__ ) . '/sitemap-buffer-master.php';
24
	require_once dirname( __FILE__ ) . '/sitemap-buffer-news.php';
25
	require_once dirname( __FILE__ ) . '/sitemap-buffer-page.php';
26
	require_once dirname( __FILE__ ) . '/sitemap-buffer-video.php';
27
}
28
29
require_once dirname( __FILE__ ) . '/sitemap-librarian.php';
30
require_once dirname( __FILE__ ) . '/sitemap-finder.php';
31
require_once dirname( __FILE__ ) . '/sitemap-state.php';
32
33
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
34
	require_once dirname( __FILE__ ) . '/sitemap-logger.php';
35
}
36
37
/**
38
 * Simple class for rendering an empty sitemap with a short TTL
39
 */
40
class Jetpack_Sitemap_Buffer_Empty extends Jetpack_Sitemap_Buffer {
41
42
	public function __construct() {
43
		parent::__construct( JP_SITEMAP_MAX_ITEMS, JP_SITEMAP_MAX_BYTES, '1970-01-01 00:00:00' );
44
45
		$this->doc->appendChild(
46
			$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
47
		);
48
49
		$this->doc->appendChild(
50
			$this->doc->createProcessingInstruction(
51
				'xml-stylesheet',
52
				'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'sitemap-index.xsl' ) . '"'
53
			)
54
		);
55
	}
56
57 View Code Duplication
	protected function get_root_element() {
58
		if ( ! isset( $this->root ) ) {
59
			$this->root = $this->doc->createElement( 'sitemapindex' );
60
			$this->root->setAttribute( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
61
			$this->doc->appendChild( $this->root );
62
			$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
63
		}
64
65
		return $this->root;
66
	}
67
}
68
69
/**
70
 * The Jetpack_Sitemap_Builder object handles the construction of
71
 * all sitemap files (except the XSL files, which are handled by
72
 * Jetpack_Sitemap_Stylist.) Other than the constructor, there are
73
 * only two public functions: build_all_sitemaps and news_sitemap_xml.
74
 *
75
 * @since 4.8.0
76
 */
77
class Jetpack_Sitemap_Builder {
78
79
	/**
80
	 * Librarian object for storing and retrieving sitemap data.
81
	 *
82
	 * @access private
83
	 * @since 4.8.0
84
	 * @var $librarian Jetpack_Sitemap_Librarian
85
	 */
86
	private $librarian;
87
88
	/**
89
	 * Logger object for reporting debug messages.
90
	 *
91
	 * @access private
92
	 * @since 4.8.0
93
	 * @var $logger Jetpack_Sitemap_Logger
94
	 */
95
	private $logger = false;
96
97
	/**
98
	 * Finder object for dealing with sitemap URIs.
99
	 *
100
	 * @access private
101
	 * @since 4.8.0
102
	 * @var $finder Jetpack_Sitemap_Finder
103
	 */
104
	private $finder;
105
106
	/**
107
	 * Construct a new Jetpack_Sitemap_Builder object.
108
	 *
109
	 * @access public
110
	 * @since 4.8.0
111
	 */
112
	public function __construct() {
113
		$this->librarian = new Jetpack_Sitemap_Librarian();
114
		$this->finder    = new Jetpack_Sitemap_Finder();
115
116
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
117
			$this->logger = new Jetpack_Sitemap_Logger();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Jetpack_Sitemap_Logger() of type object<Jetpack_Sitemap_Logger> is incompatible with the declared type boolean of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
118
		}
119
120
		update_option(
121
			'jetpack_sitemap_post_types',
122
			/**
123
			 * The array of post types to be included in the sitemap.
124
			 *
125
			 * Add your custom post type name to the array to have posts of
126
			 * that type included in the sitemap. The default array includes
127
			 * 'page' and 'post'.
128
			 *
129
			 * The result of this filter is cached in an option, 'jetpack_sitemap_post_types',
130
			 * so this filter only has to be applied once per generation.
131
			 *
132
			 * @since 4.8.0
133
			 */
134
			apply_filters(
135
				'jetpack_sitemap_post_types',
136
				array( 'post', 'page' )
137
			)
138
		);
139
	}
140
141
	/**
142
	 * Update the sitemap.
143
	 *
144
	 * All we do here is call build_next_sitemap_file a bunch of times.
145
	 *
146
	 * @since 4.8.0
147
	 */
148
	public function update_sitemap() {
149
		if ( $this->logger ) {
150
			$this->logger->report( '-- Updating...' );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
151
			if ( ! class_exists( 'DOMDocument' ) ) {
152
				$this->logger->report(
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
153
					__(
154
						'Jetpack can not load necessary XML manipulation libraries. Please ask your hosting provider to refer to our server requirements at https://jetpack.com/support/server-requirements/ .',
155
						'jetpack'
156
					),
157
					true
158
				);
159
			}
160
		}
161
162
		for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) {
163
			if ( true === $this->build_next_sitemap_file() ) {
164
				break; // All finished!
165
			}
166
		}
167
168
		if ( $this->logger ) {
169
			$this->logger->report( '-- ...done for now.' );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
170
			$this->logger->time();
0 ignored issues
show
Bug introduced by
The method time cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
171
		}
172
	}
173
174
	/**
175
	 * Generate the next sitemap file.
176
	 *
177
	 * Reads the most recent state of the sitemap generation phase,
178
	 * constructs the next file, and updates the state.
179
	 *
180
	 * @since 4.8.0
181
	 *
182
	 * @return bool True when finished.
183
	 */
184
	private function build_next_sitemap_file() {
185
		$finished = false; // Initialize finished flag.
186
187
		// Get the most recent state, and lock the state.
188
		$state = Jetpack_Sitemap_State::check_out();
189
190
		// Do nothing if the state was locked.
191
		if ( false === $state ) {
192
			return false;
193
		}
194
195
		// Otherwise, branch on the sitemap-type key of $state.
196
		switch ( $state['sitemap-type'] ) {
197
			case JP_PAGE_SITEMAP_TYPE:
198
				$this->build_next_sitemap_of_type(
199
					JP_PAGE_SITEMAP_TYPE,
200
					array( $this, 'build_one_page_sitemap' ),
201
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:..._next_sitemap_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
202
				);
203
				break;
204
205
			case JP_PAGE_SITEMAP_INDEX_TYPE:
206
				$this->build_next_sitemap_index_of_type(
207
					JP_PAGE_SITEMAP_INDEX_TYPE,
208
					JP_IMAGE_SITEMAP_TYPE,
209
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:...sitemap_index_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
210
				);
211
				break;
212
213
			case JP_IMAGE_SITEMAP_TYPE:
214
				$this->build_next_sitemap_of_type(
215
					JP_IMAGE_SITEMAP_TYPE,
216
					array( $this, 'build_one_image_sitemap' ),
217
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:..._next_sitemap_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
218
				);
219
				break;
220
221
			case JP_IMAGE_SITEMAP_INDEX_TYPE:
222
				$this->build_next_sitemap_index_of_type(
223
					JP_IMAGE_SITEMAP_INDEX_TYPE,
224
					JP_VIDEO_SITEMAP_TYPE,
225
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:...sitemap_index_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
226
				);
227
				break;
228
229
			case JP_VIDEO_SITEMAP_TYPE:
230
				$this->build_next_sitemap_of_type(
231
					JP_VIDEO_SITEMAP_TYPE,
232
					array( $this, 'build_one_video_sitemap' ),
233
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:..._next_sitemap_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
234
				);
235
				break;
236
237
			case JP_VIDEO_SITEMAP_INDEX_TYPE:
238
				$this->build_next_sitemap_index_of_type(
239
					JP_VIDEO_SITEMAP_INDEX_TYPE,
240
					JP_MASTER_SITEMAP_TYPE,
241
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 188 can also be of type boolean; however, Jetpack_Sitemap_Builder:...sitemap_index_of_type() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
242
				);
243
				break;
244
245
			case JP_MASTER_SITEMAP_TYPE:
246
				$this->build_master_sitemap( $state['max'] );
247
248
				// Reset the state and quit.
249
				Jetpack_Sitemap_State::reset(
250
					JP_PAGE_SITEMAP_TYPE
251
				);
252
253
				if ( $this->logger ) {
254
					$this->logger->report( '-- Finished.' );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
255
					$this->logger->time();
0 ignored issues
show
Bug introduced by
The method time cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
256
				}
257
				$finished = true;
258
259
				break;
260
261
			default:
262
				Jetpack_Sitemap_State::reset(
263
					JP_PAGE_SITEMAP_TYPE
264
				);
265
				$finished = true;
266
267
				break;
268
		} // End switch.
269
270
		// Unlock the state.
271
		Jetpack_Sitemap_State::unlock();
272
273
		return $finished;
274
	}
275
276
	/**
277
	 * Build the next sitemap of a given type and update the sitemap state.
278
	 *
279
	 * @since 4.8.0
280
	 *
281
	 * @param string   $sitemap_type The type of the sitemap being generated.
282
	 * @param callback $build_one    A callback which builds a single sitemap file.
283
	 * @param array    $state        A sitemap state.
284
	 */
285
	private function build_next_sitemap_of_type( $sitemap_type, $build_one, $state ) {
286
		$index_type = jp_sitemap_index_type_of( $sitemap_type );
287
288
		// Try to build a sitemap.
289
		$result = call_user_func_array(
290
			$build_one,
291
			array(
292
				$state['number'] + 1,
293
				$state['last-added'],
294
			)
295
		);
296
297 View Code Duplication
		if ( false === $result ) {
298
			// If no sitemap was generated, advance to the next type.
299
			Jetpack_Sitemap_State::check_in(
300
				array(
301
					'sitemap-type'  => $index_type,
302
					'last-added'    => 0,
303
					'number'        => 0,
304
					'last-modified' => '1970-01-01 00:00:00',
305
				)
306
			);
307
308
			if ( $this->logger ) {
309
				$this->logger->report( "-- Cleaning Up $sitemap_type" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
310
			}
311
312
			// Clean up old files.
313
			$this->librarian->delete_numbered_sitemap_rows_after(
314
				$state['number'],
315
				$sitemap_type
316
			);
317
318
			return;
319
		}
320
321
		// Otherwise, update the state.
322
		Jetpack_Sitemap_State::check_in(
323
			array(
324
				'sitemap-type'  => $state['sitemap-type'],
325
				'last-added'    => $result['last_id'],
326
				'number'        => $state['number'] + 1,
327
				'last-modified' => $result['last_modified'],
328
			)
329
		);
330
331
		if ( true === $result['any_left'] ) {
332
			// If there's more work to be done with this type, return.
333
			return;
334
		}
335
336
		// Otherwise, advance state to the next sitemap type.
337
		Jetpack_Sitemap_State::check_in(
338
			array(
339
				'sitemap-type'  => $index_type,
340
				'last-added'    => 0,
341
				'number'        => 0,
342
				'last-modified' => '1970-01-01 00:00:00',
343
			)
344
		);
345
346
		if ( $this->logger ) {
347
			$this->logger->report( "-- Cleaning Up $sitemap_type" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
348
		}
349
350
		// Clean up old files.
351
		$this->librarian->delete_numbered_sitemap_rows_after(
352
			$state['number'] + 1,
353
			$sitemap_type
354
		);
355
	}
356
357
	/**
358
	 * Build the next sitemap index of a given type and update the state.
359
	 *
360
	 * @since 4.8.0
361
	 *
362
	 * @param string $index_type The type of index being generated.
363
	 * @param string $next_type  The next type to generate after this one.
364
	 * @param array  $state      A sitemap state.
365
	 */
366
	private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) {
367
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
368
369
		// If only 0 or 1 sitemaps were built, advance to the next type and return.
370
		if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) {
371
			Jetpack_Sitemap_State::check_in(
372
				array(
373
					'sitemap-type'  => $next_type,
374
					'last-added'    => 0,
375
					'number'        => 0,
376
					'last-modified' => '1970-01-01 00:00:00',
377
				)
378
			);
379
380
			if ( $this->logger ) {
381
				$this->logger->report( "-- Cleaning Up $index_type" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
382
			}
383
384
			// There are no indices of this type.
385
			$this->librarian->delete_numbered_sitemap_rows_after(
386
				0,
387
				$index_type
388
			);
389
390
			return;
391
		}
392
393
		// Otherwise, try to build a sitemap index.
394
		$result = $this->build_one_sitemap_index(
395
			$state['number'] + 1,
396
			$state['last-added'],
397
			$state['last-modified'],
398
			$index_type
399
		);
400
401
		// If no index was built, advance to the next type and return.
402 View Code Duplication
		if ( false === $result ) {
403
			Jetpack_Sitemap_State::check_in(
404
				array(
405
					'sitemap-type'  => $next_type,
406
					'last-added'    => 0,
407
					'number'        => 0,
408
					'last-modified' => '1970-01-01 00:00:00',
409
				)
410
			);
411
412
			if ( $this->logger ) {
413
				$this->logger->report( "-- Cleaning Up $index_type" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
414
			}
415
416
			// Clean up old files.
417
			$this->librarian->delete_numbered_sitemap_rows_after(
418
				$state['number'],
419
				$index_type
420
			);
421
422
			return;
423
		}
424
425
		// Otherwise, check in the state.
426
		Jetpack_Sitemap_State::check_in(
427
			array(
428
				'sitemap-type'  => $index_type,
429
				'last-added'    => $result['last_id'],
430
				'number'        => $state['number'] + 1,
431
				'last-modified' => $result['last_modified'],
432
			)
433
		);
434
435
		// If there are still sitemaps left to index, return.
436
		if ( true === $result['any_left'] ) {
437
			return;
438
		}
439
440
		// Otherwise, advance to the next type.
441
		Jetpack_Sitemap_State::check_in(
442
			array(
443
				'sitemap-type'  => $next_type,
444
				'last-added'    => 0,
445
				'number'        => 0,
446
				'last-modified' => '1970-01-01 00:00:00',
447
			)
448
		);
449
450
		if ( $this->logger ) {
451
			$this->logger->report( "-- Cleaning Up $index_type" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
452
		}
453
454
		// We're done generating indices of this type.
455
		$this->librarian->delete_numbered_sitemap_rows_after(
456
			$state['number'] + 1,
457
			$index_type
458
		);
459
	}
460
461
	/**
462
	 * Builds the master sitemap index.
463
	 *
464
	 * @param array $max Array of sitemap types with max index and datetime.
465
	 *
466
	 * @since 4.8.0
467
	 */
468
	private function build_master_sitemap( $max ) {
469
		$page  = array();
470
		$image = array();
471
		$video = array();
472
		if ( $this->logger ) {
473
			$this->logger->report( '-- Building Master Sitemap.' );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
474
		}
475
476
		$buffer = new Jetpack_Sitemap_Buffer_Master(
477
			JP_SITEMAP_MAX_ITEMS,
478
			JP_SITEMAP_MAX_BYTES
479
		);
480
481 View Code Duplication
		if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
482
			if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
483
				$page['filename']      = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 );
484
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] );
485
			} else {
486
				$page['filename']      = jp_sitemap_filename(
487
					JP_PAGE_SITEMAP_INDEX_TYPE,
488
					$max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number']
489
				);
490
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
491
			}
492
493
			$buffer->append(
494
				array(
495
					'sitemap' => array(
496
						'loc'     => $this->finder->construct_sitemap_url( $page['filename'] ),
497
						'lastmod' => $page['last_modified'],
498
					),
499
				)
500
			);
501
		}
502
503 View Code Duplication
		if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
504
			if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
505
				$image['filename']      = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 );
506
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] );
507
			} else {
508
				$image['filename']      = jp_sitemap_filename(
509
					JP_IMAGE_SITEMAP_INDEX_TYPE,
510
					$max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number']
511
				);
512
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
513
			}
514
515
			$buffer->append(
516
				array(
517
					'sitemap' => array(
518
						'loc'     => $this->finder->construct_sitemap_url( $image['filename'] ),
519
						'lastmod' => $image['last_modified'],
520
					),
521
				)
522
			);
523
		}
524
525 View Code Duplication
		if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
526
			if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
527
				$video['filename']      = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 );
528
				$video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'] );
529
			} else {
530
				$video['filename']      = jp_sitemap_filename(
531
					JP_VIDEO_SITEMAP_INDEX_TYPE,
532
					$max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number']
533
				);
534
				$video['last_modified'] = jp_sitemap_datetime( $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'] );
535
			}
536
537
			$buffer->append(
538
				array(
539
					'sitemap' => array(
540
						'loc'     => $this->finder->construct_sitemap_url( $video['filename'] ),
541
						'lastmod' => $video['last_modified'],
542
					),
543
				)
544
			);
545
		}
546
547
		$this->librarian->store_sitemap_data(
548
			0,
549
			JP_MASTER_SITEMAP_TYPE,
550
			$buffer->contents(),
551
			''
552
		);
553
	}
554
555
	/**
556
	 * Build and store a single page sitemap. Returns false if no sitemap is built.
557
	 *
558
	 * Side effect: Create/update a sitemap row.
559
	 *
560
	 * @access private
561
	 * @since 4.8.0
562
	 *
563
	 * @param int $number The number of the current sitemap.
564
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
565
	 *
566
	 * @return bool|array @args {
567
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
568
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
569
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
570
	 * }
571
	 */
572
	public function build_one_page_sitemap( $number, $from_id ) {
573
		$last_post_id   = $from_id;
574
		$any_posts_left = true;
575
576
		if ( $this->logger ) {
577
			$debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number );
578
			$this->logger->report( "-- Building $debug_name" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
579
		}
580
581
		$buffer = new Jetpack_Sitemap_Buffer_Page(
582
			JP_SITEMAP_MAX_ITEMS,
583
			JP_SITEMAP_MAX_BYTES
584
		);
585
586
		// Add entry for the main page (only if we're at the first one) and it isn't already going to be included as a page.
587
		if ( 1 === $number && 'page' !== get_option( 'show_on_front' ) ) {
588
			$item_array = array(
589
				'url' => array(
590
					'loc' => home_url(),
591
				),
592
			);
593
594
			/**
595
			 * Filter associative array with data to build <url> node
596
			 * and its descendants for site home.
597
			 *
598
			 * @module sitemaps
599
			 *
600
			 * @since 3.9.0
601
			 *
602
			 * @param array $blog_home Data to build parent and children nodes for site home.
603
			 */
604
			$item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array );
605
606
			$buffer->append( $item_array );
607
		}
608
609
		// Add as many items to the buffer as possible.
610
		while ( $last_post_id >= 0 && false === $buffer->is_full() ) {
611
			$posts = $this->librarian->query_posts_after_id(
612
				$last_post_id,
613
				JP_SITEMAP_BATCH_SIZE
614
			);
615
616
			if ( null == $posts ) { // WPCS: loose comparison ok.
617
				$any_posts_left = false;
618
				break;
619
			}
620
621
			foreach ( $posts as $post ) {
622
				$current_item = $this->post_to_sitemap_item( $post );
623
624
				if ( true === $buffer->append( $current_item['xml'] ) ) {
0 ignored issues
show
Documentation introduced by
$current_item['xml'] is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
625
					$last_post_id = $post->ID;
626
					$buffer->view_time( $current_item['last_modified'] );
627
				} else {
628
					break;
629
				}
630
			}
631
		}
632
633
		// Handle other page sitemap URLs.
634
		if ( false === $any_posts_left || $last_post_id < 0 ) {
635
			// Negative IDs are used to track URL indexes.
636
			$last_post_id   = min( 0, $last_post_id );
637
			$any_posts_left = true; // Reinitialize.
638
639
			/**
640
			 * Filter other page sitemap URLs.
641
			 *
642
			 * @module sitemaps
643
			 *
644
			 * @since 6.1.0
645
			 *
646
			 * @param array $urls An array of other URLs.
647
			 */
648
			$other_urls = apply_filters( 'jetpack_page_sitemap_other_urls', array() );
649
650
			if ( $other_urls ) { // Start with index [1].
651
				$other_urls = array_values( $other_urls );
652
				array_unshift( $other_urls, $other_urls[0] );
653
				unset( $other_urls[0] );
654
			}
655
656
			// Add as many items to the buffer as possible.
657
			while ( false === $buffer->is_full() ) {
658
				$last_post_id_index       = abs( $last_post_id );
659
				$start_from_post_id_index = $last_post_id_index ? $last_post_id_index + 1 : 0;
660
				$urls                     = array_slice(
661
					$other_urls,
662
					$start_from_post_id_index,
663
					JP_SITEMAP_BATCH_SIZE,
664
					true
665
				);
666
667
				if ( ! $urls ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urls of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
668
					$any_posts_left = false;
669
					break;
670
				}
671
672
				foreach ( $urls as $index => $url ) {
673
					if ( ! is_array( $url ) ) {
674
						$url = array( 'loc' => $url );
675
					}
676
					$item = array( 'xml' => compact( 'url' ) );
677
678
					if ( true === $buffer->append( $item['xml'] ) ) {
679
						$last_post_id = -$index;
680
					} else {
681
						break;
682
					}
683
				}
684
			}
685
		}
686
687
		// If no items were added, return false.
688
		if ( true === $buffer->is_empty() ) {
689
			return false;
690
		}
691
692
		/**
693
		 * Filter sitemap before rendering it as XML.
694
		 *
695
		 * @module sitemaps
696
		 *
697
		 * @since 3.9.0
698
		 * @since 5.3.0 returns an element of DOMDocument type instead of SimpleXMLElement
699
		 *
700
		 * @param DOMDocument      $doc Data tree for sitemap.
701
		 * @param string           $last_modified Date of last modification.
702
		 */
703
		$tree = apply_filters(
0 ignored issues
show
Unused Code introduced by
$tree is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
704
			'jetpack_print_sitemap',
705
			$buffer->get_document(),
706
			$buffer->last_modified()
707
		);
708
709
		// Store the buffer as the content of a sitemap row.
710
		$this->librarian->store_sitemap_data(
711
			$number,
712
			JP_PAGE_SITEMAP_TYPE,
713
			$buffer->contents(),
714
			$buffer->last_modified()
715
		);
716
717
		/*
718
		 * Now report back with the ID of the last post ID to be
719
		 * successfully added and whether there are any posts left.
720
		 */
721
		return array(
722
			'last_id'       => $last_post_id,
723
			'any_left'      => $any_posts_left,
724
			'last_modified' => $buffer->last_modified(),
725
		);
726
	}
727
728
	/**
729
	 * Build and store a single image sitemap. Returns false if no sitemap is built.
730
	 *
731
	 * Side effect: Create/update an image sitemap row.
732
	 *
733
	 * @access private
734
	 * @since 4.8.0
735
	 *
736
	 * @param int $number The number of the current sitemap.
737
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
738
	 *
739
	 * @return bool|array @args {
740
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
741
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
742
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
743
	 * }
744
	 */
745 View Code Duplication
	public function build_one_image_sitemap( $number, $from_id ) {
746
		$last_post_id   = $from_id;
747
		$any_posts_left = true;
748
749
		if ( $this->logger ) {
750
			$debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number );
751
			$this->logger->report( "-- Building $debug_name" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
752
		}
753
754
		$buffer = new Jetpack_Sitemap_Buffer_Image(
755
			JP_SITEMAP_MAX_ITEMS,
756
			JP_SITEMAP_MAX_BYTES
757
		);
758
759
		// Add as many items to the buffer as possible.
760
		while ( false === $buffer->is_full() ) {
761
			$posts = $this->librarian->query_images_after_id(
762
				$last_post_id,
763
				JP_SITEMAP_BATCH_SIZE
764
			);
765
766
			if ( null == $posts ) { // WPCS: loose comparison ok.
767
				$any_posts_left = false;
768
				break;
769
			}
770
771
			foreach ( $posts as $post ) {
772
				$current_item = $this->image_post_to_sitemap_item( $post );
773
774
				if ( true === $buffer->append( $current_item['xml'] ) ) {
0 ignored issues
show
Documentation introduced by
$current_item['xml'] is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
775
					$last_post_id = $post->ID;
776
					$buffer->view_time( $current_item['last_modified'] );
777
				} else {
778
					break;
779
				}
780
			}
781
		}
782
783
		// If no items were added, return false.
784
		if ( true === $buffer->is_empty() ) {
785
			return false;
786
		}
787
788
		// Store the buffer as the content of a jp_sitemap post.
789
		$this->librarian->store_sitemap_data(
790
			$number,
791
			JP_IMAGE_SITEMAP_TYPE,
792
			$buffer->contents(),
793
			$buffer->last_modified()
794
		);
795
796
		/*
797
		 * Now report back with the ID of the last post to be
798
		 * successfully added and whether there are any posts left.
799
		 */
800
		return array(
801
			'last_id'       => $last_post_id,
802
			'any_left'      => $any_posts_left,
803
			'last_modified' => $buffer->last_modified(),
804
		);
805
	}
806
807
	/**
808
	 * Build and store a single video sitemap. Returns false if no sitemap is built.
809
	 *
810
	 * Side effect: Create/update an video sitemap row.
811
	 *
812
	 * @access private
813
	 * @since 4.8.0
814
	 *
815
	 * @param int $number The number of the current sitemap.
816
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
817
	 *
818
	 * @return bool|array @args {
819
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
820
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
821
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
822
	 * }
823
	 */
824 View Code Duplication
	public function build_one_video_sitemap( $number, $from_id ) {
825
		$last_post_id   = $from_id;
826
		$any_posts_left = true;
827
828
		if ( $this->logger ) {
829
			$debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number );
830
			$this->logger->report( "-- Building $debug_name" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
831
		}
832
833
		$buffer = new Jetpack_Sitemap_Buffer_Video(
834
			JP_SITEMAP_MAX_ITEMS,
835
			JP_SITEMAP_MAX_BYTES
836
		);
837
838
		// Add as many items to the buffer as possible.
839
		while ( false === $buffer->is_full() ) {
840
			$posts = $this->librarian->query_videos_after_id(
841
				$last_post_id,
842
				JP_SITEMAP_BATCH_SIZE
843
			);
844
845
			if ( null == $posts ) { // WPCS: loose comparison ok.
846
				$any_posts_left = false;
847
				break;
848
			}
849
850
			foreach ( $posts as $post ) {
851
				$current_item = $this->video_post_to_sitemap_item( $post );
852
853
				if ( true === $buffer->append( $current_item['xml'] ) ) {
0 ignored issues
show
Documentation introduced by
$current_item['xml'] is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
854
					$last_post_id = $post->ID;
855
					$buffer->view_time( $current_item['last_modified'] );
856
				} else {
857
					break;
858
				}
859
			}
860
		}
861
862
		// If no items were added, return false.
863
		if ( true === $buffer->is_empty() ) {
864
			return false;
865
		}
866
867
		if ( false === $buffer->is_empty() ) {
868
			$this->librarian->store_sitemap_data(
869
				$number,
870
				JP_VIDEO_SITEMAP_TYPE,
871
				$buffer->contents(),
872
				$buffer->last_modified()
873
			);
874
		}
875
876
		/*
877
		 * Now report back with the ID of the last post to be
878
		 * successfully added and whether there are any posts left.
879
		 */
880
		return array(
881
			'last_id'       => $last_post_id,
882
			'any_left'      => $any_posts_left,
883
			'last_modified' => $buffer->last_modified(),
884
		);
885
	}
886
887
	/**
888
	 * Build and store a single page sitemap index. Return false if no index is built.
889
	 *
890
	 * Side effect: Create/update a sitemap index row.
891
	 *
892
	 * @access private
893
	 * @since 4.8.0
894
	 *
895
	 * @param int    $number     The number of the current sitemap index.
896
	 * @param int    $from_id    The greatest lower bound of the IDs of the sitemaps to be included.
897
	 * @param string $datetime   Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format.
898
	 * @param string $index_type Sitemap index type.
899
	 *
900
	 * @return bool|array @args {
901
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
902
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
903
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
904
	 * }
905
	 */
906
	private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) {
907
		$last_sitemap_id   = $from_id;
908
		$any_sitemaps_left = true;
909
910
		// Check the datetime format.
911
		$datetime = jp_sitemap_datetime( $datetime );
912
913
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
914
915
		if ( $this->logger ) {
916
			$index_debug_name = jp_sitemap_filename( $index_type, $number );
917
			$this->logger->report( "-- Building $index_debug_name" );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
918
		}
919
920
		$buffer = new Jetpack_Sitemap_Buffer_Master(
921
			JP_SITEMAP_MAX_ITEMS,
922
			JP_SITEMAP_MAX_BYTES,
923
			$datetime
924
		);
925
926
		// Add pointer to the previous sitemap index (unless we're at the first one).
927
		if ( 1 !== $number ) {
928
			$i              = $number - 1;
929
			$prev_index_url = $this->finder->construct_sitemap_url(
930
				jp_sitemap_filename( $index_type, $i )
931
			);
932
933
			$item_array = array(
934
				'sitemap' => array(
935
					'loc'     => $prev_index_url,
936
					'lastmod' => $datetime,
937
				),
938
			);
939
940
			$buffer->append( $item_array );
941
		}
942
943
		// Add as many items to the buffer as possible.
944
		while ( false === $buffer->is_full() ) {
945
			// Retrieve a batch of posts (in order).
946
			$posts = $this->librarian->query_sitemaps_after_id(
947
				$sitemap_type,
948
				$last_sitemap_id,
949
				JP_SITEMAP_BATCH_SIZE
950
			);
951
952
			// If there were no posts to get, make a note.
953
			if ( null == $posts ) { // WPCS: loose comparison ok.
954
				$any_sitemaps_left = false;
955
				break;
956
			}
957
958
			// Otherwise, loop through each post in the batch.
959
			foreach ( $posts as $post ) {
960
				// Generate the sitemap XML for the post.
961
				$current_item = $this->sitemap_row_to_index_item( (array) $post );
962
963
				// Try adding this item to the buffer.
964
				if ( true === $buffer->append( $current_item['xml'] ) ) {
965
					$last_sitemap_id = $post['ID'];
966
					$buffer->view_time( $current_item['last_modified'] );
967
				} else {
968
					// Otherwise stop looping through posts.
969
					break;
970
				}
971
			}
972
		}
973
974
		// If no items were added, return false.
975
		if ( true === $buffer->is_empty() ) {
976
			return false;
977
		}
978
979
		$this->librarian->store_sitemap_data(
980
			$number,
981
			$index_type,
982
			$buffer->contents(),
983
			$buffer->last_modified()
984
		);
985
986
		/*
987
		 * Now report back with the ID of the last sitemap post ID to
988
		 * be successfully added, whether there are any sitemap posts
989
		 * left, and the most recent modification time seen.
990
		 */
991
		return array(
992
			'last_id'       => $last_sitemap_id,
993
			'any_left'      => $any_sitemaps_left,
994
			'last_modified' => $buffer->last_modified(),
995
		);
996
	}
997
998
	/**
999
	 * Construct the sitemap index url entry for a sitemap row.
1000
	 *
1001
	 * @link http://www.sitemaps.org/protocol.html#sitemapIndex_sitemap
1002
	 *
1003
	 * @access private
1004
	 * @since 4.8.0
1005
	 *
1006
	 * @param array $row The sitemap data to be processed.
1007
	 *
1008
	 * @return string An XML fragment representing the post URL.
1009
	 */
1010
	private function sitemap_row_to_index_item( $row ) {
1011
		$url = $this->finder->construct_sitemap_url( $row['post_title'] );
1012
1013
		$item_array = array(
1014
			'sitemap' => array(
1015
				'loc'     => $url,
1016
				'lastmod' => jp_sitemap_datetime( $row['post_date'] ),
1017
			),
1018
		);
1019
1020
		return array(
1021
			'xml'           => $item_array,
1022
			'last_modified' => $row['post_date'],
1023
		);
1024
	}
1025
1026
1027
	/**
1028
	 * This is served instead of a 404 when the master sitemap is requested
1029
	 * but not yet generated.
1030
	 *
1031
	 * @access public
1032
	 * @since 6.7.0
1033
	 *
1034
	 * @return string The empty sitemap xml.
1035
	 */
1036
	public function empty_sitemap_xml() {
1037
		$empty_sitemap = new Jetpack_Sitemap_Buffer_Empty();
1038
		return $empty_sitemap->contents();
1039
	}
1040
1041
	/**
1042
	 * Build and return the news sitemap xml. Note that the result of this
1043
	 * function is cached in the transient 'jetpack_news_sitemap_xml'.
1044
	 *
1045
	 * @access public
1046
	 * @since 4.8.0
1047
	 *
1048
	 * @return string The news sitemap xml.
1049
	 */
1050
	public function news_sitemap_xml() {
1051
		$the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' );
1052
1053
		if ( false === $the_stored_news_sitemap ) {
1054
1055
			if ( $this->logger ) {
1056
				$this->logger->report( 'Beginning news sitemap generation.' );
0 ignored issues
show
Bug introduced by
The method report cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1057
			}
1058
1059
			/**
1060
			 * Filter limit of entries to include in news sitemap.
1061
			 *
1062
			 * @module sitemaps
1063
			 *
1064
			 * @since 3.9.0
1065
			 *
1066
			 * @param int $count Number of entries to include in news sitemap.
1067
			 */
1068
			$item_limit = apply_filters(
1069
				'jetpack_sitemap_news_sitemap_count',
1070
				JP_NEWS_SITEMAP_MAX_ITEMS
1071
			);
1072
1073
			$buffer = new Jetpack_Sitemap_Buffer_News(
1074
				min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ),
1075
				JP_SITEMAP_MAX_BYTES
1076
			);
1077
1078
			$posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS );
1079
1080
			foreach ( $posts as $post ) {
1081
				$current_item = $this->post_to_news_sitemap_item( $post );
1082
1083
				if ( false === $buffer->append( $current_item['xml'] ) ) {
0 ignored issues
show
Documentation introduced by
$current_item['xml'] is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1084
					break;
1085
				}
1086
			}
1087
1088
			if ( $this->logger ) {
1089
				$this->logger->time( 'End news sitemap generation.' );
0 ignored issues
show
Bug introduced by
The method time cannot be called on $this->logger (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1090
			}
1091
1092
			$the_stored_news_sitemap = $buffer->contents();
1093
1094
			set_transient(
1095
				'jetpack_news_sitemap_xml',
1096
				$the_stored_news_sitemap,
1097
				JP_NEWS_SITEMAP_INTERVAL
1098
			);
1099
		} // End if.
1100
1101
		return $the_stored_news_sitemap;
1102
	}
1103
1104
	/**
1105
	 * Construct the sitemap url entry for a WP_Post.
1106
	 *
1107
	 * @link http://www.sitemaps.org/protocol.html#urldef
1108
	 * @access private
1109
	 * @since 4.8.0
1110
	 *
1111
	 * @param WP_Post $post The post to be processed.
1112
	 *
1113
	 * @return array
1114
	 *              @type array  $xml An XML fragment representing the post URL.
1115
	 *              @type string $last_modified Date post was last modified.
1116
	 */
1117
	private function post_to_sitemap_item( $post ) {
1118
1119
		/**
1120
		 * Filter condition to allow skipping specific posts in sitemap.
1121
		 *
1122
		 * @module sitemaps
1123
		 *
1124
		 * @since 3.9.0
1125
		 *
1126
		 * @param bool   $skip Current boolean. False by default, so no post is skipped.
1127
		 * @param object $post Current post in the form of a $wpdb result object. Not WP_Post.
1128
		 */
1129
		if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) {
1130
			return array(
1131
				'xml'           => null,
1132
				'last_modified' => null,
1133
			);
1134
		}
1135
1136
		$url = esc_url( get_permalink( $post ) );
1137
1138
		/*
1139
		 * Spec requires the URL to be <=2048 bytes.
1140
		 * In practice this constraint is unlikely to be violated.
1141
		 */
1142
		if ( 2048 < strlen( $url ) ) {
1143
			$url = home_url() . '/?p=' . $post->ID;
1144
		}
1145
1146
		$last_modified = $post->post_modified_gmt;
1147
1148
		// Check for more recent comments.
1149
		// Note that 'Y-m-d h:i:s' strings sort lexicographically.
1150
		if ( 0 < $post->comment_count ) {
1151
			$last_modified = max(
1152
				$last_modified,
1153
				$this->librarian->query_latest_approved_comment_time_on_post( $post->ID )
1154
			);
1155
		}
1156
1157
		$item_array = array(
1158
			'url' => array(
1159
				'loc'     => $url,
1160
				'lastmod' => jp_sitemap_datetime( $last_modified ),
1161
			),
1162
		);
1163
1164
		/**
1165
		 * Filter sitemap URL item before rendering it as XML.
1166
		 *
1167
		 * @module sitemaps
1168
		 *
1169
		 * @since 3.9.0
1170
		 *
1171
		 * @param array $tree Associative array representing sitemap URL element.
1172
		 * @param int   $post_id ID of the post being processed.
1173
		 */
1174
		$item_array = apply_filters( 'jetpack_sitemap_url', $item_array, $post->ID );
1175
1176
		return array(
1177
			'xml'           => $item_array,
1178
			'last_modified' => $last_modified,
1179
		);
1180
	}
1181
1182
	/**
1183
	 * Construct the image sitemap url entry for a WP_Post of image type.
1184
	 *
1185
	 * @link http://www.sitemaps.org/protocol.html#urldef
1186
	 *
1187
	 * @access private
1188
	 * @since 4.8.0
1189
	 *
1190
	 * @param WP_Post $post The image post to be processed.
1191
	 *
1192
	 * @return array
1193
	 *              @type array  $xml An XML fragment representing the post URL.
1194
	 *              @type string $last_modified Date post was last modified.
1195
	 */
1196
	private function image_post_to_sitemap_item( $post ) {
1197
1198
		/**
1199
		 * Filter condition to allow skipping specific image posts in the sitemap.
1200
		 *
1201
		 * @module sitemaps
1202
		 *
1203
		 * @since 4.8.0
1204
		 *
1205
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1206
		 * @param WP_POST $post Current post object.
1207
		 */
1208
		if ( apply_filters( 'jetpack_sitemap_image_skip_post', false, $post ) ) {
1209
			return array(
1210
				'xml'           => null,
1211
				'last_modified' => null,
1212
			);
1213
		}
1214
1215
		$url = wp_get_attachment_url( $post->ID );
1216
1217
		// Do not include the image if the attached parent is not published.
1218
		// Unattached will be published. Otherwise, will inherit parent status.
1219 View Code Duplication
		if ( 'publish' !== get_post_status( $post ) ) {
1220
			return array(
1221
				'xml'           => null,
1222
				'last_modified' => null,
1223
			);
1224
		}
1225
1226
		$parent_url = get_permalink( get_post( $post->post_parent ) );
1227
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1228
			$parent_url = get_permalink( $post );
1229
		}
1230
1231
		$item_array = array(
1232
			'url' => array(
1233
				'loc'         => $parent_url,
1234
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1235
				'image:image' => array(
1236
					'image:loc' => $url,
1237
				),
1238
			),
1239
		);
1240
1241
		$item_array['url']['image:image']['image:title']   = $post->post_title;
1242
		$item_array['url']['image:image']['image:caption'] = $post->post_excerpt;
1243
1244
		/**
1245
		 * Filter associative array with data to build <url> node
1246
		 * and its descendants for current post in image sitemap.
1247
		 *
1248
		 * @module sitemaps
1249
		 *
1250
		 * @since 4.8.0
1251
		 *
1252
		 * @param array $item_array Data to build parent and children nodes for current post.
1253
		 * @param int   $post_id Current image post ID.
1254
		 */
1255
		$item_array = apply_filters(
1256
			'jetpack_sitemap_image_sitemap_item',
1257
			$item_array,
1258
			$post->ID
1259
		);
1260
1261
		return array(
1262
			'xml'           => $item_array,
1263
			'last_modified' => $post->post_modified_gmt,
1264
		);
1265
	}
1266
1267
	/**
1268
	 * Construct the video sitemap url entry for a WP_Post of video type.
1269
	 *
1270
	 * @link http://www.sitemaps.org/protocol.html#urldef
1271
	 * @link https://developers.google.com/webmasters/videosearch/sitemaps
1272
	 *
1273
	 * @access private
1274
	 * @since 4.8.0
1275
	 *
1276
	 * @param WP_Post $post The video post to be processed.
1277
	 *
1278
	 * @return array
1279
	 *              @type array  $xml An XML fragment representing the post URL.
1280
	 *              @type string $last_modified Date post was last modified.
1281
	 */
1282
	private function video_post_to_sitemap_item( $post ) {
1283
1284
		/**
1285
		 * Filter condition to allow skipping specific image posts in the sitemap.
1286
		 *
1287
		 * @module sitemaps
1288
		 *
1289
		 * @since 4.8.0
1290
		 *
1291
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1292
		 * @param WP_POST $post Current post object.
1293
		 */
1294
		if ( apply_filters( 'jetpack_sitemap_video_skip_post', false, $post ) ) {
1295
			return array(
1296
				'xml'           => null,
1297
				'last_modified' => null,
1298
			);
1299
		}
1300
1301
		// Do not include the video if the attached parent is not published.
1302
		// Unattached will be published. Otherwise, will inherit parent status.
1303 View Code Duplication
		if ( 'publish' !== get_post_status( $post ) ) {
1304
			return array(
1305
				'xml'           => null,
1306
				'last_modified' => null,
1307
			);
1308
		}
1309
1310
		$parent_url = esc_url( get_permalink( get_post( $post->post_parent ) ) );
1311
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1312
			$parent_url = esc_url( get_permalink( $post ) );
1313
		}
1314
1315
		// Prepare the content like get_the_content_feed().
1316
		$content = $post->post_content;
1317
		/** This filter is already documented in core/wp-includes/post-template.php */
1318
		$content = apply_filters( 'the_content', $content );
1319
1320
		/** This filter is already documented in core/wp-includes/feed.php */
1321
		$content = apply_filters( 'the_content_feed', $content, 'rss2' );
1322
1323
		$item_array = array(
1324
			'url' => array(
1325
				'loc'         => $parent_url,
1326
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1327
				'video:video' => array(
1328
					/** This filter is already documented in core/wp-includes/feed.php */
1329
					'video:title'         => apply_filters( 'the_title_rss', $post->post_title ),
1330
					'video:thumbnail_loc' => '',
1331
					'video:description'   => $content,
1332
					'video:content_loc'   => esc_url( wp_get_attachment_url( $post->ID ) ),
1333
				),
1334
			),
1335
		);
1336
1337
		// TODO: Integrate with VideoPress here.
1338
		// cf. video:player_loc tag in video sitemap spec.
1339
1340
		/**
1341
		 * Filter associative array with data to build <url> node
1342
		 * and its descendants for current post in video sitemap.
1343
		 *
1344
		 * @module sitemaps
1345
		 *
1346
		 * @since 4.8.0
1347
		 *
1348
		 * @param array $item_array Data to build parent and children nodes for current post.
1349
		 * @param int   $post_id Current video post ID.
1350
		 */
1351
		$item_array = apply_filters(
1352
			'jetpack_sitemap_video_sitemap_item',
1353
			$item_array,
1354
			$post->ID
1355
		);
1356
1357
		return array(
1358
			'xml'           => $item_array,
1359
			'last_modified' => $post->post_modified_gmt,
1360
		);
1361
	}
1362
1363
	/**
1364
	 * Construct the news sitemap url entry for a WP_Post.
1365
	 *
1366
	 * @link http://www.sitemaps.org/protocol.html#urldef
1367
	 *
1368
	 * @access private
1369
	 * @since 4.8.0
1370
	 *
1371
	 * @param WP_Post $post The post to be processed.
1372
	 *
1373
	 * @return string An XML fragment representing the post URL.
1374
	 */
1375
	private function post_to_news_sitemap_item( $post ) {
1376
1377
		/**
1378
		 * Filter condition to allow skipping specific posts in news sitemap.
1379
		 *
1380
		 * @module sitemaps
1381
		 *
1382
		 * @since 3.9.0
1383
		 *
1384
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1385
		 * @param WP_POST $post Current post object.
1386
		 */
1387
		if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) {
1388
			return array(
1389
				'xml' => null,
1390
			);
1391
		}
1392
1393
		$url = get_permalink( $post );
1394
1395
		/*
1396
		 * Spec requires the URL to be <=2048 bytes.
1397
		 * In practice this constraint is unlikely to be violated.
1398
		 */
1399
		if ( 2048 < strlen( $url ) ) {
1400
			$url = home_url() . '/?p=' . $post->ID;
1401
		}
1402
1403
		/*
1404
		 * Trim the locale to an ISO 639 language code as required by Google.
1405
		 * Special cases are zh-cn (Simplified Chinese) and zh-tw (Traditional Chinese).
1406
		 * @link http://www.loc.gov/standards/iso639-2/php/code_list.php
1407
		 */
1408
		$language = strtolower( get_locale() );
1409
1410
		if ( in_array( $language, array( 'zh_tw', 'zh_cn' ), true ) ) {
1411
			$language = str_replace( '_', '-', $language );
1412
		} else {
1413
			$language = preg_replace( '/(_.*)$/i', '', $language );
1414
		}
1415
1416
		$item_array = array(
1417
			'url' => array(
1418
				'loc'       => $url,
1419
				'lastmod'   => jp_sitemap_datetime( $post->post_modified_gmt ),
1420
				'news:news' => array(
1421
					'news:publication'      => array(
1422
						'news:name'     => html_entity_decode( get_bloginfo( 'name' ) ),
1423
						'news:language' => $language,
1424
					),
1425
					/** This filter is already documented in core/wp-includes/feed.php */
1426
					'news:title'            => apply_filters( 'the_title_rss', $post->post_title ),
1427
					'news:publication_date' => jp_sitemap_datetime( $post->post_date_gmt ),
1428
					'news:genres'           => 'Blog',
1429
				),
1430
			),
1431
		);
1432
1433
		/**
1434
		 * Filter associative array with data to build <url> node
1435
		 * and its descendants for current post in news sitemap.
1436
		 *
1437
		 * @module sitemaps
1438
		 *
1439
		 * @since 3.9.0
1440
		 *
1441
		 * @param array $item_array Data to build parent and children nodes for current post.
1442
		 * @param int   $post_id Current post ID.
1443
		 */
1444
		$item_array = apply_filters(
1445
			'jetpack_sitemap_news_sitemap_item',
1446
			$item_array,
1447
			$post->ID
1448
		);
1449
1450
		return array(
1451
			'xml' => $item_array,
1452
		);
1453
	}
1454
}
1455