Completed
Push — fix/nosara ( 09438e...93ba64 )
by
unknown
11:29
created

Jetpack_Sitemap_Builder::build_one_video_sitemap()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 61
Code Lines 34

Duplication

Lines 61
Ratio 100 %

Importance

Changes 0
Metric Value
cc 8
eloc 34
nc 12
nop 2
dl 61
loc 61
rs 7.0047
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
require_once dirname( __FILE__ ) . '/sitemap-constants.php';
11
require_once dirname( __FILE__ ) . '/sitemap-buffer.php';
12
13
if ( ! class_exists( 'DOMDocument' ) ) {
14
	require_once dirname( __FILE__ ) . '/sitemap-buffer-fallback.php';
15
	require_once dirname( __FILE__ ) . '/sitemap-buffer-image-fallback.php';
16
	require_once dirname( __FILE__ ) . '/sitemap-buffer-master-fallback.php';
17
	require_once dirname( __FILE__ ) . '/sitemap-buffer-news-fallback.php';
18
	require_once dirname( __FILE__ ) . '/sitemap-buffer-page-fallback.php';
19
	require_once dirname( __FILE__ ) . '/sitemap-buffer-video-fallback.php';
20
} else {
21
	require_once dirname( __FILE__ ) . '/sitemap-buffer-image.php';
22
	require_once dirname( __FILE__ ) . '/sitemap-buffer-master.php';
23
	require_once dirname( __FILE__ ) . '/sitemap-buffer-news.php';
24
	require_once dirname( __FILE__ ) . '/sitemap-buffer-page.php';
25
	require_once dirname( __FILE__ ) . '/sitemap-buffer-video.php';
26
}
27
28
require_once dirname( __FILE__ ) . '/sitemap-librarian.php';
29
require_once dirname( __FILE__ ) . '/sitemap-finder.php';
30
require_once dirname( __FILE__ ) . '/sitemap-state.php';
31
32
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
33
	require_once dirname( __FILE__ ) . '/sitemap-logger.php';
34
}
35
36
/**
37
 * The Jetpack_Sitemap_Builder object handles the construction of
38
 * all sitemap files (except the XSL files, which are handled by
39
 * Jetpack_Sitemap_Stylist.) Other than the constructor, there are
40
 * only two public functions: build_all_sitemaps and news_sitemap_xml.
41
 *
42
 * @since 4.8.0
43
 */
44
class Jetpack_Sitemap_Builder {
45
46
	/**
47
	 * Librarian object for storing and retrieving sitemap data.
48
	 *
49
	 * @access private
50
	 * @since 4.8.0
51
	 * @var $librarian Jetpack_Sitemap_Librarian
52
	 */
53
	private $librarian;
54
55
	/**
56
	 * Logger object for reporting debug messages.
57
	 *
58
	 * @access private
59
	 * @since 4.8.0
60
	 * @var $logger Jetpack_Sitemap_Logger
61
	 */
62
	private $logger = false;
63
64
	/**
65
	 * Finder object for dealing with sitemap URIs.
66
	 *
67
	 * @access private
68
	 * @since 4.8.0
69
	 * @var $finder Jetpack_Sitemap_Finder
70
	 */
71
	private $finder;
72
73
	/**
74
	 * Construct a new Jetpack_Sitemap_Builder object.
75
	 *
76
	 * @access public
77
	 * @since 4.8.0
78
	 */
79
	public function __construct() {
80
		$this->librarian = new Jetpack_Sitemap_Librarian();
81
		$this->finder = new Jetpack_Sitemap_Finder();
82
83
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
84
			$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...
85
		}
86
87
		update_option(
88
			'jetpack_sitemap_post_types',
89
			/**
90
			 * The array of post types to be included in the sitemap.
91
			 *
92
			 * Add your custom post type name to the array to have posts of
93
			 * that type included in the sitemap. The default array includes
94
			 * 'page' and 'post'.
95
			 *
96
			 * The result of this filter is cached in an option, 'jetpack_sitemap_post_types',
97
			 * so this filter only has to be applied once per generation.
98
			 *
99
			 * @since 4.8.0
100
			 */
101
			apply_filters(
102
				'jetpack_sitemap_post_types',
103
				array( 'post', 'page' )
104
			)
105
		);
106
	}
107
108
	/**
109
	 * Update the sitemap.
110
	 *
111
	 * All we do here is call build_next_sitemap_file a bunch of times.
112
	 *
113
	 * @since 4.8.0
114
	 */
115
	public function update_sitemap() {
116
		if ( $this->logger ) {
117
			$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...
118
			if ( ! class_exists( 'DOMDocument' ) ) {
119
				$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...
120
					__(
121
						'-- WARNING: Jetpack can not load necessary XML manipulation libraries. '
122
						. 'This can happen if XML support in PHP is not enabled on your server. '
123
						. 'XML support is highly recommended for WordPress and Jetpack, please enable '
124
						. 'it or contact your hosting provider about it.',
125
						'jetpack'
126
					)
127
				);
128
			}
129
		}
130
131
		for ( $i = 1; $i <= JP_SITEMAP_UPDATE_SIZE; $i++ ) {
132
			$this->build_next_sitemap_file();
133
		}
134
135
		if ( $this->logger ) {
136
			$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...
137
			$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...
138
		}
139
	}
140
141
	/**
142
	 * Generate the next sitemap file.
143
	 *
144
	 * Reads the most recent state of the sitemap generation phase,
145
	 * constructs the next file, and updates the state.
146
	 *
147
	 * @since 4.8.0
148
	 */
149
	private function build_next_sitemap_file() {
150
		// Get the most recent state, and lock the state.
151
		$state = Jetpack_Sitemap_State::check_out();
152
153
		// Do nothing if the state was locked.
154
		if ( false === $state ) {
155
			return;
156
		}
157
158
		// Otherwise, branch on the sitemap-type key of $state.
159
		switch ( $state['sitemap-type'] ) {
160
			case JP_PAGE_SITEMAP_TYPE:
161
				$this->build_next_sitemap_of_type(
162
					JP_PAGE_SITEMAP_TYPE,
163
					array( $this, 'build_one_page_sitemap' ),
164
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
165
				);
166
				break;
167
168
			case JP_PAGE_SITEMAP_INDEX_TYPE:
169
				$this->build_next_sitemap_index_of_type(
170
					JP_PAGE_SITEMAP_INDEX_TYPE,
171
					JP_IMAGE_SITEMAP_TYPE,
172
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
173
				);
174
				break;
175
176
			case JP_IMAGE_SITEMAP_TYPE:
177
				$this->build_next_sitemap_of_type(
178
					JP_IMAGE_SITEMAP_TYPE,
179
					array( $this, 'build_one_image_sitemap' ),
180
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
181
				);
182
				break;
183
184
			case JP_IMAGE_SITEMAP_INDEX_TYPE:
185
				$this->build_next_sitemap_index_of_type(
186
					JP_IMAGE_SITEMAP_INDEX_TYPE,
187
					JP_VIDEO_SITEMAP_TYPE,
188
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
189
				);
190
				break;
191
192
			case JP_VIDEO_SITEMAP_TYPE:
193
				$this->build_next_sitemap_of_type(
194
					JP_VIDEO_SITEMAP_TYPE,
195
					array( $this, 'build_one_video_sitemap' ),
196
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
197
				);
198
				break;
199
200
			case JP_VIDEO_SITEMAP_INDEX_TYPE:
201
				$this->build_next_sitemap_index_of_type(
202
					JP_VIDEO_SITEMAP_INDEX_TYPE,
203
					JP_MASTER_SITEMAP_TYPE,
204
					$state
0 ignored issues
show
Bug introduced by
It seems like $state defined by \Jetpack_Sitemap_State::check_out() on line 151 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...
205
				);
206
				break;
207
208
			case JP_MASTER_SITEMAP_TYPE:
209
				$this->build_master_sitemap( $state['max'] );
210
211
				// Reset the state and quit.
212
				Jetpack_Sitemap_State::reset(
213
					JP_PAGE_SITEMAP_TYPE
214
				);
215
216
				if ( $this->logger ) {
217
					$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...
218
					$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...
219
				}
220
221
				die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method build_next_sitemap_file() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
222
223
			default:
224
				// Otherwise, reset the state.
225
				Jetpack_Sitemap_State::reset(
226
					JP_PAGE_SITEMAP_TYPE
227
				);
228
				die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method build_next_sitemap_file() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
229
		} // End switch().
230
231
		// Unlock the state.
232
		Jetpack_Sitemap_State::unlock();
233
	}
234
235
	/**
236
	 * Build the next sitemap of a given type and update the sitemap state.
237
	 *
238
	 * @since 4.8.0
239
	 *
240
	 * @param string   $sitemap_type The type of the sitemap being generated.
241
	 * @param callback $build_one    A callback which builds a single sitemap file.
242
	 * @param array    $state        A sitemap state.
243
	 */
244
	private function build_next_sitemap_of_type( $sitemap_type, $build_one, $state ) {
245
		$index_type = jp_sitemap_index_type_of( $sitemap_type );
246
247
		// Try to build a sitemap.
248
		$result = call_user_func_array(
249
			$build_one,
250
			array(
251
				$state['number'] + 1,
252
				$state['last-added'],
253
			)
254
		);
255
256 View Code Duplication
		if ( false === $result ) {
257
			// If no sitemap was generated, advance to the next type.
258
			Jetpack_Sitemap_State::check_in( array(
259
				'sitemap-type'  => $index_type,
260
				'last-added'    => 0,
261
				'number'        => 0,
262
				'last-modified' => '1970-01-01 00:00:00',
263
			) );
264
265
			if ( $this->logger ) {
266
				$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...
267
			}
268
269
			// Clean up old files.
270
			$this->librarian->delete_numbered_sitemap_rows_after(
271
				$state['number'], $sitemap_type
272
			);
273
274
			return;
275
		}
276
277
		// Otherwise, update the state.
278
		Jetpack_Sitemap_State::check_in( array(
279
			'sitemap-type'  => $state['sitemap-type'],
280
			'last-added'    => $result['last_id'],
281
			'number'        => $state['number'] + 1,
282
			'last-modified' => $result['last_modified'],
283
		) );
284
285
		if ( true === $result['any_left'] ) {
286
			// If there's more work to be done with this type, return.
287
			return;
288
		}
289
290
		// Otherwise, advance state to the next sitemap type.
291
		Jetpack_Sitemap_State::check_in( array(
292
			'sitemap-type'  => $index_type,
293
			'last-added'    => 0,
294
			'number'        => 0,
295
			'last-modified' => '1970-01-01 00:00:00',
296
		) );
297
298
		if ( $this->logger ) {
299
			$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...
300
		}
301
302
		// Clean up old files.
303
		$this->librarian->delete_numbered_sitemap_rows_after(
304
			$state['number'] + 1, $sitemap_type
305
		);
306
	}
307
308
	/**
309
	 * Build the next sitemap index of a given type and update the state.
310
	 *
311
	 * @since 4.8.0
312
	 *
313
	 * @param string $index_type The type of index being generated.
314
	 * @param string $next_type  The next type to generate after this one.
315
	 * @param array  $state      A sitemap state.
316
	 */
317
	private function build_next_sitemap_index_of_type( $index_type, $next_type, $state ) {
318
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
319
320
		// If only 0 or 1 sitemaps were built, advance to the next type and return.
321
		if ( 1 >= $state['max'][ $sitemap_type ]['number'] ) {
322
			Jetpack_Sitemap_State::check_in( array(
323
				'sitemap-type'  => $next_type,
324
				'last-added'    => 0,
325
				'number'        => 0,
326
				'last-modified' => '1970-01-01 00:00:00',
327
			) );
328
329
			if ( $this->logger ) {
330
				$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...
331
			}
332
333
			// There are no indices of this type.
334
			$this->librarian->delete_numbered_sitemap_rows_after(
335
				0, $index_type
336
			);
337
338
			return;
339
		}
340
341
		// Otherwise, try to build a sitemap index.
342
		$result = $this->build_one_sitemap_index(
343
			$state['number'] + 1,
344
			$state['last-added'],
345
			$state['last-modified'],
346
			$index_type
347
		);
348
349
		// If no index was built, advance to the next type and return.
350 View Code Duplication
		if ( false === $result ) {
351
			Jetpack_Sitemap_State::check_in( array(
352
				'sitemap-type'  => $next_type,
353
				'last-added'    => 0,
354
				'number'        => 0,
355
				'last-modified' => '1970-01-01 00:00:00',
356
			) );
357
358
			if ( $this->logger ) {
359
				$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...
360
			}
361
362
			// Clean up old files.
363
			$this->librarian->delete_numbered_sitemap_rows_after(
364
				$state['number'], $index_type
365
			);
366
367
			return;
368
		}
369
370
		// Otherwise, check in the state.
371
		Jetpack_Sitemap_State::check_in( array(
372
			'sitemap-type'  => $index_type,
373
			'last-added'    => $result['last_id'],
374
			'number'        => $state['number'] + 1,
375
			'last-modified' => $result['last_modified'],
376
		) );
377
378
		// If there are still sitemaps left to index, return.
379
		if ( true === $result['any_left'] ) {
380
			return;
381
		}
382
383
		// Otherwise, advance to the next type.
384
		Jetpack_Sitemap_State::check_in( array(
385
			'sitemap-type'  => $next_type,
386
			'last-added'    => 0,
387
			'number'        => 0,
388
			'last-modified' => '1970-01-01 00:00:00',
389
		) );
390
391
		if ( $this->logger ) {
392
			$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...
393
		}
394
395
		// We're done generating indices of this type.
396
		$this->librarian->delete_numbered_sitemap_rows_after(
397
			$state['number'] + 1, $index_type
398
		);
399
400
		return;
401
	}
402
403
	/**
404
	 * Builds the master sitemap index.
405
	 *
406
	 * @param array $max Array of sitemap types with max index and datetime.
407
	 *
408
	 * @since 4.8.0
409
	 */
410
	private function build_master_sitemap( $max ) {
411
		if ( $this->logger ) {
412
			$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...
413
		}
414
415
		$buffer = new Jetpack_Sitemap_Buffer_Master(
416
			JP_SITEMAP_MAX_ITEMS,
417
			JP_SITEMAP_MAX_BYTES
418
		);
419
420 View Code Duplication
		if ( 0 < $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
421
			if ( 1 === $max[ JP_PAGE_SITEMAP_TYPE ]['number'] ) {
422
				$page['filename'] = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, 1 );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$page was never initialized. Although not strictly required by PHP, it is generally a good practice to add $page = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
423
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_TYPE ]['lastmod'] );
424
			} else {
425
				$page['filename'] = jp_sitemap_filename(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$page was never initialized. Although not strictly required by PHP, it is generally a good practice to add $page = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
426
					JP_PAGE_SITEMAP_INDEX_TYPE,
427
					$max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['number']
428
				);
429
				$page['last_modified'] = jp_sitemap_datetime( $max[ JP_PAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
430
			}
431
432
			$buffer->append(
433
				array(
434
					'sitemap' => array(
435
						'loc'     => $this->finder->construct_sitemap_url( $page['filename'] ),
436
						'lastmod' => $page['last_modified'],
437
					),
438
				)
439
			);
440
		}
441
442 View Code Duplication
		if ( 0 < $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
443
			if ( 1 === $max[ JP_IMAGE_SITEMAP_TYPE ]['number'] ) {
444
				$image['filename'] = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, 1 );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$image was never initialized. Although not strictly required by PHP, it is generally a good practice to add $image = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
445
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_TYPE ]['lastmod'] );
446
			} else {
447
				$image['filename'] = jp_sitemap_filename(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$image was never initialized. Although not strictly required by PHP, it is generally a good practice to add $image = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
448
					JP_IMAGE_SITEMAP_INDEX_TYPE,
449
					$max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['number']
450
				);
451
				$image['last_modified'] = jp_sitemap_datetime( $max[ JP_IMAGE_SITEMAP_INDEX_TYPE ]['lastmod'] );
452
			}
453
454
			$buffer->append(
455
				array(
456
					'sitemap' => array(
457
						'loc'     => $this->finder->construct_sitemap_url( $image['filename'] ),
458
						'lastmod' => $image['last_modified'],
459
					),
460
				)
461
			);
462
		}
463
464 View Code Duplication
		if ( 0 < $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
465
			if ( 1 === $max[ JP_VIDEO_SITEMAP_TYPE ]['number'] ) {
466
				$video['filename'] = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, 1 );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$video was never initialized. Although not strictly required by PHP, it is generally a good practice to add $video = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
467
				$video['last_modified'] = $max[ JP_VIDEO_SITEMAP_TYPE ]['lastmod'];
468
			} else {
469
				$video['filename'] = jp_sitemap_filename(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$video was never initialized. Although not strictly required by PHP, it is generally a good practice to add $video = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
470
					JP_VIDEO_SITEMAP_INDEX_TYPE,
471
					$max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['number']
472
				);
473
				$video['last_modified'] = $max[ JP_VIDEO_SITEMAP_INDEX_TYPE ]['lastmod'];
474
			}
475
476
			$buffer->append(
477
				array(
478
					'sitemap' => array(
479
						'loc'     => $this->finder->construct_sitemap_url( $video['filename'] ),
480
						'lastmod' => $video['last_modified'],
481
					),
482
				)
483
			);
484
		}
485
486
		$this->librarian->store_sitemap_data(
487
			0,
488
			JP_MASTER_SITEMAP_TYPE,
489
			$buffer->contents(),
490
			''
491
		);
492
	}
493
494
	/**
495
	 * Build and store a single page sitemap. Returns false if no sitemap is built.
496
	 *
497
	 * Side effect: Create/update a sitemap row.
498
	 *
499
	 * @access private
500
	 * @since 4.8.0
501
	 *
502
	 * @param int $number The number of the current sitemap.
503
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
504
	 *
505
	 * @return bool|array @args {
506
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
507
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
508
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
509
	 * }
510
	 */
511
	public function build_one_page_sitemap( $number, $from_id ) {
512
		$last_post_id = $from_id;
513
		$any_posts_left = true;
514
515
		if ( $this->logger ) {
516
			$debug_name = jp_sitemap_filename( JP_PAGE_SITEMAP_TYPE, $number );
517
			$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...
518
		}
519
520
		$buffer = new Jetpack_Sitemap_Buffer_Page(
521
			JP_SITEMAP_MAX_ITEMS,
522
			JP_SITEMAP_MAX_BYTES
523
		);
524
525
		// Add entry for the main page (only if we're at the first one).
526
		if ( 1 === $number ) {
527
			$item_array = array(
528
				'url' => array(
529
					'loc' => home_url(),
530
				),
531
			);
532
533
			/**
534
			 * Filter associative array with data to build <url> node
535
			 * and its descendants for site home.
536
			 *
537
			 * @module sitemaps
538
			 *
539
			 * @since 3.9.0
540
			 *
541
			 * @param array $blog_home Data to build parent and children nodes for site home.
542
			 */
543
			$item_array = apply_filters( 'jetpack_sitemap_url_home', $item_array );
544
545
			$buffer->append( $item_array );
546
		}
547
548
		// Add as many items to the buffer as possible.
549
		while ( false === $buffer->is_full() ) {
550
			$posts = $this->librarian->query_posts_after_id(
551
				$last_post_id, JP_SITEMAP_BATCH_SIZE
552
			);
553
554
			if ( null == $posts ) { // WPCS: loose comparison ok.
555
				$any_posts_left = false;
556
				break;
557
			}
558
559
			foreach ( $posts as $post ) {
560
				$current_item = $this->post_to_sitemap_item( $post );
561
562
				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...
563
					$last_post_id = $post->ID;
564
					$buffer->view_time( $current_item['last_modified'] );
565
				} else {
566
					break;
567
				}
568
			}
569
		}
570
571
		// If no items were added, return false.
572
		if ( true === $buffer->is_empty() ) {
573
			return false;
574
		}
575
576
		/**
577
		 * Filter sitemap before rendering it as XML.
578
		 *
579
		 * @module sitemaps
580
		 *
581
		 * @since 3.9.0
582
		 * @since 5.3.0 returns an element of DOMDocument type instead of SimpleXMLElement
583
		 *
584
		 * @param DOMDocument      $doc Data tree for sitemap.
585
		 * @param string           $last_modified Date of last modification.
586
		 */
587
		$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...
588
			'jetpack_print_sitemap',
589
			$buffer->get_document(),
590
			$buffer->last_modified()
591
		);
592
593
		// Store the buffer as the content of a sitemap row.
594
		$this->librarian->store_sitemap_data(
595
			$number,
596
			JP_PAGE_SITEMAP_TYPE,
597
			$buffer->contents(),
598
			$buffer->last_modified()
599
		);
600
601
		/*
602
		 * Now report back with the ID of the last post ID to be
603
		 * successfully added and whether there are any posts left.
604
		 */
605
		return array(
606
			'last_id'       => $last_post_id,
607
			'any_left'      => $any_posts_left,
608
			'last_modified' => $buffer->last_modified(),
609
		);
610
	}
611
612
	/**
613
	 * Build and store a single image sitemap. Returns false if no sitemap is built.
614
	 *
615
	 * Side effect: Create/update an image sitemap row.
616
	 *
617
	 * @access private
618
	 * @since 4.8.0
619
	 *
620
	 * @param int $number The number of the current sitemap.
621
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
622
	 *
623
	 * @return bool|array @args {
624
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
625
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
626
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
627
	 * }
628
	 */
629 View Code Duplication
	public function build_one_image_sitemap( $number, $from_id ) {
630
		$last_post_id = $from_id;
631
		$any_posts_left = true;
632
633
		if ( $this->logger ) {
634
			$debug_name = jp_sitemap_filename( JP_IMAGE_SITEMAP_TYPE, $number );
635
			$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...
636
		}
637
638
		$buffer = new Jetpack_Sitemap_Buffer_Image(
639
			JP_SITEMAP_MAX_ITEMS,
640
			JP_SITEMAP_MAX_BYTES
641
		);
642
643
		// Add as many items to the buffer as possible.
644
		while ( false === $buffer->is_full() ) {
645
			$posts = $this->librarian->query_images_after_id(
646
				$last_post_id, JP_SITEMAP_BATCH_SIZE
647
			);
648
649
			if ( null == $posts ) { // WPCS: loose comparison ok.
650
				$any_posts_left = false;
651
				break;
652
			}
653
654
			foreach ( $posts as $post ) {
655
				$current_item = $this->image_post_to_sitemap_item( $post );
656
657
				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...
658
					$last_post_id = $post->ID;
659
					$buffer->view_time( $current_item['last_modified'] );
660
				} else {
661
					break;
662
				}
663
			}
664
		}
665
666
		// If no items were added, return false.
667
		if ( true === $buffer->is_empty() ) {
668
			return false;
669
		}
670
671
		// Store the buffer as the content of a jp_sitemap post.
672
		$this->librarian->store_sitemap_data(
673
			$number,
674
			JP_IMAGE_SITEMAP_TYPE,
675
			$buffer->contents(),
676
			$buffer->last_modified()
677
		);
678
679
		/*
680
		 * Now report back with the ID of the last post to be
681
		 * successfully added and whether there are any posts left.
682
		 */
683
		return array(
684
			'last_id'       => $last_post_id,
685
			'any_left'      => $any_posts_left,
686
			'last_modified' => $buffer->last_modified(),
687
		);
688
	}
689
690
	/**
691
	 * Build and store a single video sitemap. Returns false if no sitemap is built.
692
	 *
693
	 * Side effect: Create/update an video sitemap row.
694
	 *
695
	 * @access private
696
	 * @since 4.8.0
697
	 *
698
	 * @param int $number The number of the current sitemap.
699
	 * @param int $from_id The greatest lower bound of the IDs of the posts to be included.
700
	 *
701
	 * @return bool|array @args {
702
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
703
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
704
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
705
	 * }
706
	 */
707 View Code Duplication
	public function build_one_video_sitemap( $number, $from_id ) {
708
		$last_post_id = $from_id;
709
		$any_posts_left = true;
710
711
		if ( $this->logger ) {
712
			$debug_name = jp_sitemap_filename( JP_VIDEO_SITEMAP_TYPE, $number );
713
			$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...
714
		}
715
716
		$buffer = new Jetpack_Sitemap_Buffer_Video(
717
			JP_SITEMAP_MAX_ITEMS,
718
			JP_SITEMAP_MAX_BYTES
719
		);
720
721
		// Add as many items to the buffer as possible.
722
		while ( false === $buffer->is_full() ) {
723
			$posts = $this->librarian->query_videos_after_id(
724
				$last_post_id, JP_SITEMAP_BATCH_SIZE
725
			);
726
727
			if ( null == $posts ) { // WPCS: loose comparison ok.
728
				$any_posts_left = false;
729
				break;
730
			}
731
732
			foreach ( $posts as $post ) {
733
				$current_item = $this->video_post_to_sitemap_item( $post );
734
735
				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...
736
					$last_post_id = $post->ID;
737
					$buffer->view_time( $current_item['last_modified'] );
738
				} else {
739
					break;
740
				}
741
			}
742
		}
743
744
		// If no items were added, return false.
745
		if ( true === $buffer->is_empty() ) {
746
			return false;
747
		}
748
749
		if ( false === $buffer->is_empty() ) {
750
			$this->librarian->store_sitemap_data(
751
				$number,
752
				JP_VIDEO_SITEMAP_TYPE,
753
				$buffer->contents(),
754
				$buffer->last_modified()
755
			);
756
		}
757
758
		/*
759
		 * Now report back with the ID of the last post to be
760
		 * successfully added and whether there are any posts left.
761
		 */
762
		return array(
763
			'last_id'       => $last_post_id,
764
			'any_left'      => $any_posts_left,
765
			'last_modified' => $buffer->last_modified(),
766
		);
767
	}
768
769
	/**
770
	 * Build and store a single page sitemap index. Return false if no index is built.
771
	 *
772
	 * Side effect: Create/update a sitemap index row.
773
	 *
774
	 * @access private
775
	 * @since 4.8.0
776
	 *
777
	 * @param int    $number     The number of the current sitemap index.
778
	 * @param int    $from_id    The greatest lower bound of the IDs of the sitemaps to be included.
779
	 * @param string $datetime   Datetime of previous sitemap in 'YYYY-MM-DD hh:mm:ss' format.
780
	 * @param string $index_type Sitemap index type.
781
	 *
782
	 * @return bool|array @args {
783
	 *   @type int    $last_id       The ID of the last item to be successfully added to the buffer.
784
	 *   @type bool   $any_left      'true' if there are items which haven't been saved to a sitemap, 'false' otherwise.
785
	 *   @type string $last_modified The most recent timestamp to appear on the sitemap.
786
	 * }
787
	 */
788
	private function build_one_sitemap_index( $number, $from_id, $datetime, $index_type ) {
789
		$last_sitemap_id   = $from_id;
790
		$any_sitemaps_left = true;
791
792
		// Check the datetime format.
793
		$datetime = jp_sitemap_datetime( $datetime );
794
795
		$sitemap_type = jp_sitemap_child_type_of( $index_type );
796
797
		if ( $this->logger ) {
798
			$index_debug_name = jp_sitemap_filename( $index_type, $number );
799
			$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...
800
		}
801
802
		$buffer = new Jetpack_Sitemap_Buffer_Master(
803
			JP_SITEMAP_MAX_ITEMS,
804
			JP_SITEMAP_MAX_BYTES,
805
			$datetime
806
		);
807
808
		// Add pointer to the previous sitemap index (unless we're at the first one).
809
		if ( 1 !== $number ) {
810
			$i = $number - 1;
811
			$prev_index_url = $this->finder->construct_sitemap_url(
812
				jp_sitemap_filename( $index_type, $i )
813
			);
814
815
			$item_array = array(
816
				'sitemap' => array(
817
					'loc'     => $prev_index_url,
818
					'lastmod' => $datetime,
819
				),
820
			);
821
822
			$buffer->append( $item_array );
823
		}
824
825
		// Add as many items to the buffer as possible.
826
		while ( false === $buffer->is_full() ) {
827
			// Retrieve a batch of posts (in order).
828
			$posts = $this->librarian->query_sitemaps_after_id(
829
				$sitemap_type, $last_sitemap_id, JP_SITEMAP_BATCH_SIZE
830
			);
831
832
			// If there were no posts to get, make a note.
833
			if ( null == $posts ) { // WPCS: loose comparison ok.
834
				$any_sitemaps_left = false;
835
				break;
836
			}
837
838
			// Otherwise, loop through each post in the batch.
839
			foreach ( $posts as $post ) {
840
				// Generate the sitemap XML for the post.
841
				$current_item = $this->sitemap_row_to_index_item( (array) $post );
842
843
				// Try adding this item to the buffer.
844
				if ( true === $buffer->append( $current_item['xml'] ) ) {
845
					$last_sitemap_id = $post['ID'];
846
					$buffer->view_time( $current_item['last_modified'] );
847
				} else {
848
					// Otherwise stop looping through posts.
849
					break;
850
				}
851
			}
852
		}
853
854
		// If no items were added, return false.
855
		if ( true === $buffer->is_empty() ) {
856
			return false;
857
		}
858
859
		$this->librarian->store_sitemap_data(
860
			$number,
861
			$index_type,
862
			$buffer->contents(),
863
			$buffer->last_modified()
864
		);
865
866
		/*
867
		 * Now report back with the ID of the last sitemap post ID to
868
		 * be successfully added, whether there are any sitemap posts
869
		 * left, and the most recent modification time seen.
870
		 */
871
		return array(
872
			'last_id'       => $last_sitemap_id,
873
			'any_left'      => $any_sitemaps_left,
874
			'last_modified' => $buffer->last_modified(),
875
		);
876
	}
877
878
	/**
879
	 * Construct the sitemap index url entry for a sitemap row.
880
	 *
881
	 * @link http://www.sitemaps.org/protocol.html#sitemapIndex_sitemap
882
	 *
883
	 * @access private
884
	 * @since 4.8.0
885
	 *
886
	 * @param array $row The sitemap data to be processed.
887
	 *
888
	 * @return string An XML fragment representing the post URL.
889
	 */
890
	private function sitemap_row_to_index_item( $row ) {
891
		$url = $this->finder->construct_sitemap_url( $row['post_title'] );
892
893
		$item_array = array(
894
			'sitemap' => array(
895
				'loc'     => $url,
896
				'lastmod' => jp_sitemap_datetime( $row['post_date'] ),
897
			),
898
		);
899
900
		return array(
901
			'xml'           => $item_array,
902
			'last_modified' => $row['post_date'],
903
		);
904
	}
905
906
	/**
907
	 * Build and return the news sitemap xml. Note that the result of this
908
	 * function is cached in the transient 'jetpack_news_sitemap_xml'.
909
	 *
910
	 * @access public
911
	 * @since 4.8.0
912
	 *
913
	 * @return string The news sitemap xml.
914
	 */
915
	public function news_sitemap_xml() {
916
		$the_stored_news_sitemap = get_transient( 'jetpack_news_sitemap_xml' );
917
918
		if ( false === $the_stored_news_sitemap ) {
919
920
			if ( $this->logger ) {
921
				$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...
922
			}
923
924
			/**
925
			 * Filter limit of entries to include in news sitemap.
926
			 *
927
			 * @module sitemaps
928
			 *
929
			 * @since 3.9.0
930
			 *
931
			 * @param int $count Number of entries to include in news sitemap.
932
			 */
933
			$item_limit = apply_filters(
934
				'jetpack_sitemap_news_sitemap_count',
935
				JP_NEWS_SITEMAP_MAX_ITEMS
936
			);
937
938
			$buffer = new Jetpack_Sitemap_Buffer_News(
939
				min( $item_limit, JP_NEWS_SITEMAP_MAX_ITEMS ),
940
				JP_SITEMAP_MAX_BYTES
941
			);
942
943
			$posts = $this->librarian->query_most_recent_posts( JP_NEWS_SITEMAP_MAX_ITEMS );
944
945
			foreach ( $posts as $post ) {
946
				$current_item = $this->post_to_news_sitemap_item( $post );
947
948
				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...
949
					break;
950
				}
951
			}
952
953
			if ( $this->logger ) {
954
				$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...
955
			}
956
957
			$the_stored_news_sitemap = $buffer->contents();
958
959
			set_transient(
960
				'jetpack_news_sitemap_xml',
961
				$the_stored_news_sitemap,
962
				JP_NEWS_SITEMAP_INTERVAL
963
			);
964
		} // End if().
965
966
		return $the_stored_news_sitemap;
967
	}
968
969
	/**
970
	 * Construct the sitemap url entry for a WP_Post.
971
	 *
972
	 * @link http://www.sitemaps.org/protocol.html#urldef
973
	 * @access private
974
	 * @since 4.8.0
975
	 *
976
	 * @param WP_Post $post The post to be processed.
977
	 *
978
	 * @return array An array representing the post URL.
979
	 */
980
	private function post_to_sitemap_item( $post ) {
981
982
		/**
983
		 * Filter condition to allow skipping specific posts in sitemap.
984
		 *
985
		 * @module sitemaps
986
		 *
987
		 * @since 3.9.0
988
		 *
989
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
990
		 * @param WP_POST $post Current post object.
991
		 */
992
		if ( true === apply_filters( 'jetpack_sitemap_skip_post', false, $post ) ) {
993
			return array(
994
				'xml'           => null,
995
				'last_modified' => null,
996
			);
997
		}
998
999
		$url = esc_url( get_permalink( $post ) );
1000
1001
		/*
1002
		 * Spec requires the URL to be <=2048 bytes.
1003
		 * In practice this constraint is unlikely to be violated.
1004
		 */
1005
		if ( 2048 < strlen( $url ) ) {
1006
			$url = home_url() . '/?p=' . $post->ID;
1007
		}
1008
1009
		$last_modified = $post->post_modified_gmt;
1010
1011
		// Check for more recent comments.
1012
		// Note that 'Y-m-d h:i:s' strings sort lexicographically.
1013
		if ( 0 < $post->comment_count ) {
1014
			$last_modified = max(
1015
				$last_modified,
1016
				$this->librarian->query_latest_approved_comment_time_on_post( $post->ID )
1017
			);
1018
		}
1019
1020
		$item_array = array(
1021
			'url' => array(
1022
				'loc'     => $url,
1023
				'lastmod' => jp_sitemap_datetime( $last_modified ),
1024
			),
1025
		);
1026
1027
		/**
1028
		 * Filter sitemap URL item before rendering it as XML.
1029
		 *
1030
		 * @module sitemaps
1031
		 *
1032
		 * @since 3.9.0
1033
		 *
1034
		 * @param array $tree Associative array representing sitemap URL element.
1035
		 * @param int   $post_id ID of the post being processed.
1036
		 */
1037
		$item_array = apply_filters( 'jetpack_sitemap_url', $item_array, $post->ID );
1038
1039
		return array(
1040
			'xml'           => $item_array,
1041
			'last_modified' => $last_modified,
1042
		);
1043
	}
1044
1045
	/**
1046
	 * Construct the image sitemap url entry for a WP_Post of image type.
1047
	 *
1048
	 * @link http://www.sitemaps.org/protocol.html#urldef
1049
	 *
1050
	 * @access private
1051
	 * @since 4.8.0
1052
	 *
1053
	 * @param WP_Post $post The image post to be processed.
1054
	 *
1055
	 * @return string An XML fragment representing the post URL.
1056
	 */
1057
	private function image_post_to_sitemap_item( $post ) {
1058
1059
		/**
1060
		 * Filter condition to allow skipping specific image posts in the sitemap.
1061
		 *
1062
		 * @module sitemaps
1063
		 *
1064
		 * @since 4.8.0
1065
		 *
1066
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1067
		 * @param WP_POST $post Current post object.
1068
		 */
1069
		if ( apply_filters( 'jetpack_sitemap_image_skip_post', false, $post ) ) {
1070
			return array(
1071
				'xml'           => null,
1072
				'last_modified' => null,
1073
			);
1074
		}
1075
1076
		$url = wp_get_attachment_url( $post->ID );
1077
1078
		$parent_url = get_permalink( get_post( $post->post_parent ) );
1079
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1080
			$parent_url = get_permalink( $post );
1081
		}
1082
1083
		$item_array = array(
1084
			'url' => array(
1085
				'loc'         => $parent_url,
1086
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1087
				'image:image' => array(
1088
					'image:loc' => $url,
1089
				),
1090
			),
1091
		);
1092
1093
		$item_array['url']['image:image']['image:title'] = $post->post_title;
1094
		$item_array['url']['image:image']['image:caption'] = $post->post_excerpt;
1095
1096
		/**
1097
		 * Filter associative array with data to build <url> node
1098
		 * and its descendants for current post in image sitemap.
1099
		 *
1100
		 * @module sitemaps
1101
		 *
1102
		 * @since 4.8.0
1103
		 *
1104
		 * @param array $item_array Data to build parent and children nodes for current post.
1105
		 * @param int   $post_id Current image post ID.
1106
		 */
1107
		$item_array = apply_filters(
1108
			'jetpack_sitemap_image_sitemap_item',
1109
			$item_array,
1110
			$post->ID
1111
		);
1112
1113
		return array(
1114
			'xml'           => $item_array,
1115
			'last_modified' => $post->post_modified_gmt,
1116
		);
1117
	}
1118
1119
	/**
1120
	 * Construct the video sitemap url entry for a WP_Post of video type.
1121
	 *
1122
	 * @link http://www.sitemaps.org/protocol.html#urldef
1123
	 * @link https://developers.google.com/webmasters/videosearch/sitemaps
1124
	 *
1125
	 * @access private
1126
	 * @since 4.8.0
1127
	 *
1128
	 * @param WP_Post $post The video post to be processed.
1129
	 *
1130
	 * @return string An XML fragment representing the post URL.
1131
	 */
1132
	private function video_post_to_sitemap_item( $post ) {
1133
1134
		/**
1135
		 * Filter condition to allow skipping specific image posts in the sitemap.
1136
		 *
1137
		 * @module sitemaps
1138
		 *
1139
		 * @since 4.8.0
1140
		 *
1141
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1142
		 * @param WP_POST $post Current post object.
1143
		 */
1144
		if ( apply_filters( 'jetpack_sitemap_video_skip_post', false, $post ) ) {
1145
			return array(
1146
				'xml'           => null,
1147
				'last_modified' => null,
1148
			);
1149
		}
1150
1151
		$parent_url = esc_url( get_permalink( get_post( $post->post_parent ) ) );
1152
		if ( '' == $parent_url ) { // WPCS: loose comparison ok.
1153
			$parent_url = esc_url( get_permalink( $post ) );
1154
		}
1155
1156
		// Prepare the content like get_the_content_feed().
1157
		$content = $post->post_content;
1158
		/** This filter is already documented in core/wp-includes/post-template.php */
1159
		$content = apply_filters( 'the_content', $content );
1160
1161
		/** This filter is already documented in core/wp-includes/feed.php */
1162
		$content = apply_filters( 'the_content_feed', $content, 'rss2' );
1163
1164
		$item_array = array(
1165
			'url' => array(
1166
				'loc'         => $parent_url,
1167
				'lastmod'     => jp_sitemap_datetime( $post->post_modified_gmt ),
1168
				'video:video' => array(
1169
					/** This filter is already documented in core/wp-includes/feed.php */
1170
					'video:title'         => apply_filters( 'the_title_rss', $post->post_title ),
1171
					'video:thumbnail_loc' => '',
1172
					'video:description'   => $content,
1173
					'video:content_loc'   => esc_url( wp_get_attachment_url( $post->ID ) ),
1174
				),
1175
			),
1176
		);
1177
1178
		// TODO: Integrate with VideoPress here.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1179
		// cf. video:player_loc tag in video sitemap spec.
1180
1181
		/**
1182
		 * Filter associative array with data to build <url> node
1183
		 * and its descendants for current post in video sitemap.
1184
		 *
1185
		 * @module sitemaps
1186
		 *
1187
		 * @since 4.8.0
1188
		 *
1189
		 * @param array $item_array Data to build parent and children nodes for current post.
1190
		 * @param int   $post_id Current video post ID.
1191
		 */
1192
		$item_array = apply_filters(
1193
			'jetpack_sitemap_video_sitemap_item',
1194
			$item_array,
1195
			$post->ID
1196
		);
1197
1198
		return array(
1199
			'xml'           => $item_array,
1200
			'last_modified' => $post->post_modified_gmt,
1201
		);
1202
	}
1203
1204
	/**
1205
	 * Construct the news sitemap url entry for a WP_Post.
1206
	 *
1207
	 * @link http://www.sitemaps.org/protocol.html#urldef
1208
	 *
1209
	 * @access private
1210
	 * @since 4.8.0
1211
	 *
1212
	 * @param WP_Post $post The post to be processed.
1213
	 *
1214
	 * @return string An XML fragment representing the post URL.
1215
	 */
1216
	private function post_to_news_sitemap_item( $post ) {
1217
1218
		/**
1219
		 * Filter condition to allow skipping specific posts in news sitemap.
1220
		 *
1221
		 * @module sitemaps
1222
		 *
1223
		 * @since 3.9.0
1224
		 *
1225
		 * @param bool    $skip Current boolean. False by default, so no post is skipped.
1226
		 * @param WP_POST $post Current post object.
1227
		 */
1228
		if ( apply_filters( 'jetpack_sitemap_news_skip_post', false, $post ) ) {
1229
			return array(
1230
				'xml' => null,
1231
			);
1232
		}
1233
1234
		$url = get_permalink( $post );
1235
1236
		/*
1237
		 * Spec requires the URL to be <=2048 bytes.
1238
		 * In practice this constraint is unlikely to be violated.
1239
		 */
1240
		if ( 2048 < strlen( $url ) ) {
1241
			$url = home_url() . '/?p=' . $post->ID;
1242
		}
1243
1244
		/*
1245
		 * Trim the locale to an ISO 639 language code as required by Google.
1246
		 * Special cases are zh-cn (Simplified Chinese) and zh-tw (Traditional Chinese).
1247
		 * @link http://www.loc.gov/standards/iso639-2/php/code_list.php
1248
		 */
1249
		$language = strtolower( get_locale() );
1250
1251
		if ( in_array( $language, array( 'zh_tw', 'zh_cn' ), true ) ) {
1252
			$language = str_replace( '_', '-', $language );
1253
		} else {
1254
			$language = preg_replace( '/(_.*)$/i', '', $language );
1255
		}
1256
1257
		$item_array = array(
1258
			'url' => array(
1259
				'loc' => $url,
1260
				'lastmod' => jp_sitemap_datetime( $post->post_modified_gmt ),
1261
				'news:news' => array(
1262
					'news:publication' => array(
1263
						'news:name'     => get_bloginfo( 'name' ),
1264
						'news:language' => $language,
1265
					),
1266
					/** This filter is already documented in core/wp-includes/feed.php */
1267
					'news:title'            => apply_filters( 'the_title_rss', $post->post_title ),
1268
					'news:publication_date' => jp_sitemap_datetime( $post->post_date_gmt ),
1269
					'news:genres'           => 'Blog',
1270
				),
1271
			),
1272
		);
1273
1274
		/**
1275
		 * Filter associative array with data to build <url> node
1276
		 * and its descendants for current post in news sitemap.
1277
		 *
1278
		 * @module sitemaps
1279
		 *
1280
		 * @since 3.9.0
1281
		 *
1282
		 * @param array $item_array Data to build parent and children nodes for current post.
1283
		 * @param int   $post_id Current post ID.
1284
		 */
1285
		$item_array = apply_filters(
1286
			'jetpack_sitemap_news_sitemap_item',
1287
			$item_array,
1288
			$post->ID
1289
		);
1290
1291
		return array(
1292
			'xml' => $item_array,
1293
		);
1294
	}
1295
}
1296